X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gweb%2Fgweb.c;h=12fcb1d8ab32662cd95e9ffb62c81aa8f445bd7b;hb=dd3cccc5e67548dcc2dd6c6254ed6c97859085d5;hp=9d746c82b6207d843b6abe57f7054e1aade11921;hpb=6b05f1c07e2d43c6c60fe950e3b62897ce133976;p=platform%2Fupstream%2Fconnman.git diff --git a/gweb/gweb.c b/gweb/gweb.c index 9d746c8..12fcb1d 100644 --- a/gweb/gweb.c +++ b/gweb/gweb.c @@ -2,7 +2,7 @@ * * Web service library with GLib integration * - * Copyright (C) 2009-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2009-2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -25,12 +25,19 @@ #include #include +#include #include #include #include #include #include +#include +#include #include +#include +#include +#include +#include #include "giognutls.h" #include "gresolv.h" @@ -51,7 +58,7 @@ struct _GWebResult { guint16 status; const guint8 *buffer; gsize length; - gboolean use_chunk; + bool use_chunk; gchar *last_key; GHashTable *headers; }; @@ -63,6 +70,7 @@ struct web_session { char *host; uint16_t port; unsigned long flags; + struct addrinfo *addr; char *content_type; @@ -71,16 +79,17 @@ struct web_session { guint send_watch; guint resolv_action; + guint address_action; char *request; guint8 *receive_buffer; gsize receive_space; GString *send_buffer; GString *current_header; - gboolean header_done; - gboolean body_done; - gboolean more_data; - gboolean request_started; + bool header_done; + bool body_done; + bool more_data; + bool request_started; enum chunk_state chunck_state; gsize chunk_size; @@ -90,15 +99,21 @@ struct web_session { GWebResult result; GWebResultFunc result_func; + GWebRouteFunc route_func; GWebInputFunc input_func; + int fd; + gsize length; + gsize offset; gpointer user_data; }; struct _GWeb { - gint ref_count; + int ref_count; guint next_query_id; + int family; + int index; GList *session_list; @@ -106,38 +121,52 @@ struct _GWeb { char *proxy; char *accept_option; char *user_agent; + char *user_agent_profile; char *http_version; - gboolean close_connection; + bool close_connection; GWebDebugFunc debug_func; gpointer debug_data; }; -static inline void debug(GWeb *web, const char *format, ...) +#define debug(web, format, arg...) \ + _debug(web, __FILE__, __func__, format, ## arg) + +static void _debug(GWeb *web, const char *file, const char *caller, + const char *format, ...) { char str[256]; va_list ap; + int len; - if (web->debug_func == NULL) + if (!web->debug_func) return; va_start(ap, format); - if (vsnprintf(str, sizeof(str), format, ap) > 0) - web->debug_func(str, web->debug_data); + if ((len = snprintf(str, sizeof(str), "%s:%s() web %p ", + file, caller, web)) > 0) { + if (vsnprintf(str + len, sizeof(str) - len, format, ap) > 0) + web->debug_func(str, web->debug_data); + } va_end(ap); } static void free_session(struct web_session *session) { - GWeb *web = session->web; + GWeb *web; - if (session == NULL) + if (!session) return; g_free(session->request); + web = session->web; + + if (session->address_action > 0) + g_source_remove(session->address_action); + if (session->resolv_action > 0) g_resolv_cancel_lookup(web->resolv, session->resolv_action); @@ -147,20 +176,29 @@ static void free_session(struct web_session *session) if (session->send_watch > 0) g_source_remove(session->send_watch); - if (session->transport_channel != NULL) + if (session->transport_channel) g_io_channel_unref(session->transport_channel); g_free(session->result.last_key); - g_hash_table_destroy(session->result.headers); - g_string_free(session->send_buffer, TRUE); - g_string_free(session->current_header, TRUE); + if (session->result.headers) + g_hash_table_destroy(session->result.headers); + + if (session->send_buffer) + g_string_free(session->send_buffer, TRUE); + + if (session->current_header) + g_string_free(session->current_header, TRUE); + g_free(session->receive_buffer); g_free(session->content_type); g_free(session->host); g_free(session->address); + if (session->addr) + freeaddrinfo(session->addr); + g_free(session); } @@ -184,45 +222,47 @@ GWeb *g_web_new(int index) return NULL; web = g_try_new0(GWeb, 1); - if (web == NULL) + if (!web) return NULL; web->ref_count = 1; web->next_query_id = 1; + web->family = AF_UNSPEC; + web->index = index; web->session_list = NULL; web->resolv = g_resolv_new(index); - if (web->resolv == NULL) { + if (!web->resolv) { g_free(web); return NULL; } web->accept_option = g_strdup("*/*"); web->user_agent = g_strdup_printf("GWeb/%s", VERSION); - web->close_connection = FALSE; + web->close_connection = false; return web; } GWeb *g_web_ref(GWeb *web) { - if (web == NULL) + if (!web) return NULL; - g_atomic_int_inc(&web->ref_count); + __sync_fetch_and_add(&web->ref_count, 1); return web; } void g_web_unref(GWeb *web) { - if (web == NULL) + if (!web) return; - if (g_atomic_int_dec_and_test(&web->ref_count) == FALSE) + if (__sync_fetch_and_sub(&web->ref_count, 1) != 1) return; flush_sessions(web); @@ -233,14 +273,20 @@ void g_web_unref(GWeb *web) g_free(web->accept_option); g_free(web->user_agent); + g_free(web->user_agent_profile); g_free(web->http_version); g_free(web); } +bool g_web_supports_tls(void) +{ + return g_io_channel_supports_tls(); +} + void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data) { - if (web == NULL) + if (!web) return; web->debug_func = func; @@ -249,14 +295,14 @@ void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data) g_resolv_set_debug(web->resolv, func, user_data); } -gboolean g_web_set_proxy(GWeb *web, const char *proxy) +bool g_web_set_proxy(GWeb *web, const char *proxy) { - if (web == NULL) - return FALSE; + if (!web) + return false; g_free(web->proxy); - if (proxy == NULL) { + if (!proxy) { web->proxy = NULL; debug(web, "clearing proxy"); } else { @@ -264,24 +310,39 @@ gboolean g_web_set_proxy(GWeb *web, const char *proxy) debug(web, "setting proxy %s", web->proxy); } - return TRUE; + return true; } -gboolean g_web_add_nameserver(GWeb *web, const char *address) +bool g_web_set_address_family(GWeb *web, int family) { - if (web == NULL) - return FALSE; + if (!web) + return false; + + if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6) + return false; + + web->family = family; + + g_resolv_set_address_family(web->resolv, family); + + return true; +} + +bool g_web_add_nameserver(GWeb *web, const char *address) +{ + if (!web) + return false; g_resolv_add_nameserver(web->resolv, address, 53, 0); - return TRUE; + return true; } -static gboolean set_accept_option(GWeb *web, const char *format, va_list args) +static bool set_accept_option(GWeb *web, const char *format, va_list args) { g_free(web->accept_option); - if (format == NULL) { + if (!format) { web->accept_option = NULL; debug(web, "clearing accept option"); } else { @@ -289,16 +350,16 @@ static gboolean set_accept_option(GWeb *web, const char *format, va_list args) debug(web, "setting accept %s", web->accept_option); } - return TRUE; + return true; } -gboolean g_web_set_accept(GWeb *web, const char *format, ...) +bool g_web_set_accept(GWeb *web, const char *format, ...) { va_list args; - gboolean result; + bool result; - if (web == NULL) - return FALSE; + if (!web) + return false; va_start(args, format); result = set_accept_option(web, format, args); @@ -307,11 +368,11 @@ gboolean g_web_set_accept(GWeb *web, const char *format, ...) return result; } -static gboolean set_user_agent(GWeb *web, const char *format, va_list args) +static bool set_user_agent(GWeb *web, const char *format, va_list args) { g_free(web->user_agent); - if (format == NULL) { + if (!format) { web->user_agent = NULL; debug(web, "clearing user agent"); } else { @@ -319,16 +380,16 @@ static gboolean set_user_agent(GWeb *web, const char *format, va_list args) debug(web, "setting user agent %s", web->user_agent); } - return TRUE; + return true; } -gboolean g_web_set_user_agent(GWeb *web, const char *format, ...) +bool g_web_set_user_agent(GWeb *web, const char *format, ...) { va_list args; - gboolean result; + bool result; - if (web == NULL) - return FALSE; + if (!web) + return false; va_start(args, format); result = set_user_agent(web, format, args); @@ -337,86 +398,143 @@ gboolean g_web_set_user_agent(GWeb *web, const char *format, ...) return result; } -gboolean g_web_set_http_version(GWeb *web, const char *version) +bool g_web_set_ua_profile(GWeb *web, const char *profile) { - if (web == NULL) - return FALSE; + if (!web) + return false; + + g_free(web->user_agent_profile); + + web->user_agent_profile = g_strdup(profile); + debug(web, "setting user agent profile %s", web->user_agent); + + return true; +} + +bool g_web_set_http_version(GWeb *web, const char *version) +{ + if (!web) + return false; g_free(web->http_version); - if (version == NULL) { + if (!version) { web->http_version = NULL; debug(web, "clearing HTTP version"); } else { web->http_version = g_strdup(version); - debug(web, "setting HTTP version %s", web->http_version); + debug(web, "setting HTTP version %s", web->http_version); } - return TRUE; + return true; } -void g_web_set_close_connection(GWeb *web, gboolean enabled) +void g_web_set_close_connection(GWeb *web, bool enabled) { - if (web == NULL) + if (!web) return; web->close_connection = enabled; } -gboolean g_web_get_close_connection(GWeb *web) +bool g_web_get_close_connection(GWeb *web) { - if (web == NULL) - return FALSE; + if (!web) + return false; return web->close_connection; } static inline void call_result_func(struct web_session *session, guint16 status) { - gboolean result; - if (session->result_func == NULL) + if (!session->result_func) return; if (status != 0) session->result.status = status; - result = session->result_func(&session->result, session->user_data); + session->result_func(&session->result, session->user_data); - debug(session->web, "[result function] %s", - result == TRUE ? "continue" : "stop"); } -static gboolean process_send_buffer(struct web_session *session) +static inline void call_route_func(struct web_session *session) { - GString *buf = session->send_buffer; + if (session->route_func) + session->route_func(session->address, session->addr->ai_family, + session->web->index, session->user_data); +} + +static bool process_send_buffer(struct web_session *session) +{ + GString *buf; gsize count, bytes_written; GIOStatus status; + if (!session) + return false; + + buf = session->send_buffer; count = buf->len; if (count == 0) { - if (session->request_started == TRUE && - session->more_data == FALSE) - session->body_done = TRUE; + if (session->request_started && + !session->more_data && + session->fd == -1) + session->body_done = true; - return FALSE; + return false; } - debug(session->web, "bytes to write %zu", count); - status = g_io_channel_write_chars(session->transport_channel, buf->str, count, &bytes_written, NULL); - debug(session->web, "status %u bytes written %zu", - status, bytes_written); + debug(session->web, "status %u bytes to write %zu bytes written %zu", + status, count, bytes_written); if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) - return FALSE; + return false; g_string_erase(buf, 0, bytes_written); - return TRUE; + return true; +} + +static bool process_send_file(struct web_session *session) +{ + int sk; + off_t offset; + ssize_t bytes_sent; + + if (session->fd == -1) + return false; + + if (!session->request_started || session->more_data) + return false; + + sk = g_io_channel_unix_get_fd(session->transport_channel); + if (sk < 0) + return false; + + offset = session->offset; + + bytes_sent = sendfile(sk, session->fd, &offset, session->length); + + debug(session->web, "errno: %d, bytes to send %zu / bytes sent %zu", + errno, session->length, bytes_sent); + + if (bytes_sent < 0 && errno != EAGAIN) + return false; + + session->offset = offset; + session->length -= bytes_sent; + + if (session->length == 0) { + session->body_done = true; + return false; + } + + return true; } static void process_next_chunk(struct web_session *session) @@ -425,8 +543,8 @@ static void process_next_chunk(struct web_session *session) const guint8 *body; gsize length; - if (session->input_func == NULL) { - session->more_data = FALSE; + if (!session->input_func) { + session->more_data = false; return; } @@ -439,7 +557,7 @@ static void process_next_chunk(struct web_session *session) g_string_append(buf, "\r\n"); } - if (session->more_data == FALSE) + if (!session->more_data) g_string_append(buf, "0\r\n\r\n"); } @@ -455,12 +573,12 @@ static void start_request(struct web_session *session) g_string_truncate(buf, 0); - if (session->web->http_version == NULL) + if (!session->web->http_version) version = "1.1"; else version = session->web->http_version; - if (session->content_type == NULL) + if (!session->content_type) g_string_append_printf(buf, "GET %s HTTP/%s\r\n", session->request, version); else @@ -469,41 +587,46 @@ static void start_request(struct web_session *session) g_string_append_printf(buf, "Host: %s\r\n", session->host); - if (session->web->user_agent != NULL) + if (session->web->user_agent) g_string_append_printf(buf, "User-Agent: %s\r\n", session->web->user_agent); - if (session->web->accept_option != NULL) + if (session->web->user_agent_profile) { + g_string_append_printf(buf, "x-wap-profile: %s\r\n", + session->web->user_agent_profile); + } + + if (session->web->accept_option) g_string_append_printf(buf, "Accept: %s\r\n", session->web->accept_option); - if (session->content_type != NULL) { + if (session->content_type) { g_string_append_printf(buf, "Content-Type: %s\r\n", session->content_type); - if (session->input_func == NULL) { - session->more_data = FALSE; - length = 0; + if (!session->input_func) { + session->more_data = false; + length = session->length; } else session->more_data = session->input_func(&body, &length, session->user_data); - if (session->more_data == FALSE) + if (!session->more_data) g_string_append_printf(buf, "Content-Length: %zu\r\n", length); else g_string_append(buf, "Transfer-Encoding: chunked\r\n"); } - if (session->web->close_connection == TRUE) + if (session->web->close_connection) g_string_append(buf, "Connection: close\r\n"); g_string_append(buf, "\r\n"); - if (session->content_type != NULL && length > 0) { - if (session->more_data == TRUE) { + if (session->content_type && length > 0) { + if (session->more_data) { g_string_append_printf(buf, "%zx\r\n", length); g_string_append_len(buf, (char *) body, length); g_string_append(buf, "\r\n"); - } else + } else if (session->fd == -1) g_string_append_len(buf, (char *) body, length); } } @@ -518,18 +641,21 @@ static gboolean send_data(GIOChannel *channel, GIOCondition cond, return FALSE; } - if (process_send_buffer(session) == TRUE) + if (process_send_buffer(session)) return TRUE; - if (session->request_started == FALSE) { - session->request_started = TRUE; + if (process_send_file(session)) + return TRUE; + + if (!session->request_started) { + session->request_started = true; start_request(session); - } else if (session->more_data == TRUE) + } else if (session->more_data) process_next_chunk(session); process_send_buffer(session); - if (session->body_done == TRUE) { + if (session->body_done) { session->send_watch = 0; return FALSE; } @@ -551,7 +677,7 @@ static int decode_chunked(struct web_session *session, switch (session->chunck_state) { case CHUNK_SIZE: pos = memchr(ptr, '\n', len); - if (pos == NULL) { + if (!pos) { g_string_append_len(session->current_header, (gchar *) ptr, len); return 0; @@ -639,7 +765,7 @@ static int handle_body(struct web_session *session, debug(session->web, "[body] length %zu", len); - if (session->result.use_chunk == FALSE) { + if (!session->result.use_chunk) { if (len > 0) { session->result.buffer = buf; session->result.length = len; @@ -666,6 +792,9 @@ static void handle_multi_line(struct web_session *session) char *str; gchar *value; + if (!session->result.last_key) + return; + str = session->current_header->str; if (str[0] != ' ' && str[0] != '\t') @@ -682,7 +811,7 @@ static void handle_multi_line(struct web_session *session) value = g_hash_table_lookup(session->result.headers, session->result.last_key); - if (value != NULL) { + if (value) { g_string_insert(session->current_header, 0, value); str = session->current_header->str; @@ -704,7 +833,7 @@ static void add_header_field(struct web_session *session) str = session->current_header->str; pos = memchr(str, ':', session->current_header->len); - if (pos != NULL) { + if (pos) { *pos = '\0'; pos++; @@ -719,7 +848,7 @@ static void add_header_field(struct web_session *session) g_string_erase(session->current_header, 0, count); value = g_hash_table_lookup(session->result.headers, key); - if (value != NULL) { + if (value) { g_string_insert_c(session->current_header, 0, ' '); g_string_insert_c(session->current_header, 0, ';'); @@ -767,7 +896,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, session->receive_buffer[bytes_read] = '\0'; - if (session->header_done == TRUE) { + if (session->header_done) { if (handle_body(session, session->receive_buffer, bytes_read) < 0) { session->transport_watch = 0; @@ -782,7 +911,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, char *str; pos = memchr(ptr, '\n', bytes_read); - if (pos == NULL) { + if (!pos) { g_string_append_len(session->current_header, (gchar *) ptr, bytes_read); return TRUE; @@ -807,14 +936,14 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, if (session->current_header->len == 0) { char *val; - session->header_done = TRUE; + session->header_done = true; val = g_hash_table_lookup(session->result.headers, "Transfer-Encoding"); - if (val != NULL) { + if (val) { val = g_strrstr(val, "chunked"); - if (val != NULL) { - session->result.use_chunk = TRUE; + if (val) { + session->result.use_chunk = true; session->chunck_state = CHUNK_SIZE; session->chunk_left = 0; @@ -852,16 +981,76 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, return TRUE; } +static int bind_to_address(int sk, const char *interface, int family) +{ + struct ifaddrs *ifaddr_list, *ifaddr; + int size, err = -1; + + if (getifaddrs(&ifaddr_list) < 0) + return err; + + for (ifaddr = ifaddr_list; ifaddr; ifaddr = ifaddr->ifa_next) { + if (g_strcmp0(ifaddr->ifa_name, interface) != 0) + continue; + + if (!ifaddr->ifa_addr || + ifaddr->ifa_addr->sa_family != family) + continue; + + switch (family) { + case AF_INET: + size = sizeof(struct sockaddr_in); + break; + case AF_INET6: + size = sizeof(struct sockaddr_in6); + break; + default: + continue; + } + + err = bind(sk, (struct sockaddr *) ifaddr->ifa_addr, size); + break; + } + + freeifaddrs(ifaddr_list); + return err; +} + +static inline int bind_socket(int sk, int index, int family) +{ + char interface[IF_NAMESIZE]; + int err; + + if (!if_indextoname(index, interface)) + return -1; + + err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, + interface, IF_NAMESIZE); + if (err < 0) + err = bind_to_address(sk, interface, family); + + return err; +} + static int connect_session_transport(struct web_session *session) { GIOFlags flags; - struct sockaddr_in sin; int sk; - sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + sk = socket(session->addr->ai_family, SOCK_STREAM | SOCK_CLOEXEC, + IPPROTO_TCP); if (sk < 0) return -EIO; + if (session->web->index > 0) { + if (bind_socket(sk, session->web->index, + session->addr->ai_family) < 0) { + debug(session->web, "bind() %s", strerror(errno)); + close(sk); + return -EIO; + } + } + if (session->flags & SESSION_FLAG_USE_TLS) { debug(session->web, "using TLS encryption"); session->transport_channel = g_io_channel_gnutls_new(sk); @@ -870,7 +1059,8 @@ static int connect_session_transport(struct web_session *session) session->transport_channel = g_io_channel_unix_new(sk); } - if (session->transport_channel == NULL) { + if (!session->transport_channel) { + debug(session->web, "channel missing"); close(sk); return -ENOMEM; } @@ -884,14 +1074,10 @@ static int connect_session_transport(struct web_session *session) g_io_channel_set_close_on_unref(session->transport_channel, TRUE); - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(session->port); - sin.sin_addr.s_addr = inet_addr(session->address); - - if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + if (connect(sk, session->addr->ai_addr, + session->addr->ai_addrlen) < 0) { if (errno != EINPROGRESS) { - close(sk); + debug(session->web, "connect() %s", strerror(errno)); return -EIO; } } @@ -921,16 +1107,17 @@ static int create_transport(struct web_session *session) return 0; } -static int parse_url(struct web_session *session, const char *url) +static int parse_url(struct web_session *session, + const char *url, const char *proxy) { char *scheme, *host, *port, *path; scheme = g_strdup(url); - if (scheme == NULL) + if (!scheme) return -EINVAL; host = strstr(scheme, "://"); - if (host != NULL) { + if (host) { *host = '\0'; host += 3; @@ -949,13 +1136,58 @@ static int parse_url(struct web_session *session, const char *url) } path = strchr(host, '/'); - if (path != NULL) + if (path) *(path++) = '\0'; - session->request = g_strdup_printf("/%s", path ? path : ""); + if (!proxy) + session->request = g_strdup_printf("/%s", path ? path : ""); + else + session->request = g_strdup(url); port = strrchr(host, ':'); - if (port != NULL) { + if (port) { + char *end; + int tmp = strtol(port + 1, &end, 10); + + if (*end == '\0') { + *port = '\0'; + session->port = tmp; + } + + if (!proxy) + session->host = g_strdup(host); + else + session->host = g_strdup_printf("%s:%u", host, tmp); + } else + session->host = g_strdup(host); + + g_free(scheme); + + if (!proxy) + return 0; + + scheme = g_strdup(proxy); + if (!scheme) + return -EINVAL; + + host = strstr(proxy, "://"); + if (host) { + *host = '\0'; + host += 3; + + if (strcasecmp(scheme, "http") != 0) { + g_free(scheme); + return -EINVAL; + } + } else + host = scheme; + + path = strchr(host, '/'); + if (path) + *(path++) = '\0'; + + port = strrchr(host, ':'); + if (port) { char *end; int tmp = strtol(port + 1, &end, 10); @@ -965,31 +1197,39 @@ static int parse_url(struct web_session *session, const char *url) } } - session->host = g_strdup(host); + session->address = g_strdup(host); g_free(scheme); return 0; } -static void resolv_result(GResolvResultStatus status, - char **results, gpointer user_data) +static void handle_resolved_address(struct web_session *session) { - struct web_session *session = user_data; + struct addrinfo hints; + char *port; + int ret; - if (results == NULL || results[0] == NULL) { - call_result_func(session, 404); - return; - } + debug(session->web, "address %s", session->address); - debug(session->web, "address %s", results[0]); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = session->web->family; - if (inet_aton(results[0], NULL) == 0) { + if (session->addr) { + freeaddrinfo(session->addr); + session->addr = NULL; + } + + port = g_strdup_printf("%u", session->port); + ret = getaddrinfo(session->address, port, &hints, &session->addr); + g_free(port); + if (ret != 0 || !session->addr) { call_result_func(session, 400); return; } - session->address = g_strdup(results[0]); + call_route_func(session); if (create_transport(session) < 0) { call_result_func(session, 409); @@ -997,30 +1237,78 @@ static void resolv_result(GResolvResultStatus status, } } +static gboolean already_resolved(gpointer data) +{ + struct web_session *session = data; + + session->address_action = 0; + handle_resolved_address(session); + + return FALSE; +} + +static void resolv_result(GResolvResultStatus status, + char **results, gpointer user_data) +{ + struct web_session *session = user_data; + + if (!results || !results[0]) { + call_result_func(session, 404); + return; + } + + g_free(session->address); + session->address = g_strdup(results[0]); + + handle_resolved_address(session); +} + +static bool is_ip_address(const char *host) +{ + struct addrinfo hints; + struct addrinfo *addr; + int result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + addr = NULL; + + result = getaddrinfo(host, NULL, &hints, &addr); + if(!result) + freeaddrinfo(addr); + + return result == 0; +} + static guint do_request(GWeb *web, const char *url, const char *type, GWebInputFunc input, - GWebResultFunc func, gpointer user_data) + int fd, gsize length, GWebResultFunc func, + GWebRouteFunc route, gpointer user_data) { struct web_session *session; + const gchar *host; - if (web == NULL || url == NULL) + if (!web || !url) return 0; debug(web, "request %s", url); session = g_try_new0(struct web_session, 1); - if (session == NULL) + if (!session) return 0; - if (parse_url(session, url) < 0) { + if (parse_url(session, url, web->proxy) < 0) { free_session(session); return 0; } - debug(web, "host %s:%u", session->host, session->port); + debug(web, "proxy host %s", session->address); + debug(web, "port %u", session->port); + debug(web, "host %s", session->host); debug(web, "flags %lu", session->flags); + debug(web, "request %s", session->request); - if (type != NULL) { + if (type) { session->content_type = g_strdup(type); debug(web, "content-type %s", session->content_type); @@ -1029,18 +1317,22 @@ static guint do_request(GWeb *web, const char *url, session->web = web; session->result_func = func; + session->route_func = route; session->input_func = input; + session->fd = fd; + session->length = length; + session->offset = 0; session->user_data = user_data; session->receive_buffer = g_try_malloc(DEFAULT_BUFFER_SIZE); - if (session->receive_buffer == NULL) { + if (!session->receive_buffer) { free_session(session); return 0; } session->result.headers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - if (session->result.headers == NULL) { + if (!session->result.headers) { free_session(session); return 0; } @@ -1048,20 +1340,20 @@ static guint do_request(GWeb *web, const char *url, session->receive_space = DEFAULT_BUFFER_SIZE; session->send_buffer = g_string_sized_new(0); session->current_header = g_string_sized_new(0); - session->header_done = FALSE; - session->body_done = FALSE; - - if (inet_aton(session->host, NULL) == 0) { - session->resolv_action = g_resolv_lookup_hostname(web->resolv, - session->host, resolv_result, session); - if (session->resolv_action == 0) { - free_session(session); - return 0; + session->header_done = false; + session->body_done = false; + + host = session->address ? session->address : session->host; + if (is_ip_address(host)) { + if (session->address != host) { + g_free(session->address); + session->address = g_strdup(host); } + session->address_action = g_idle_add(already_resolved, session); } else { - session->address = g_strdup(session->host); - - if (create_transport(session) < 0) { + session->resolv_action = g_resolv_lookup_hostname(web->resolv, + host, resolv_result, session); + if (session->resolv_action == 0) { free_session(session); return 0; } @@ -1072,67 +1364,90 @@ static guint do_request(GWeb *web, const char *url, return web->next_query_id++; } -guint g_web_request_get(GWeb *web, const char *url, - GWebResultFunc func, gpointer user_data) +guint g_web_request_get(GWeb *web, const char *url, GWebResultFunc func, + GWebRouteFunc route, gpointer user_data) { - return do_request(web, url, NULL, NULL, func, user_data); + return do_request(web, url, NULL, NULL, -1, 0, func, route, user_data); } guint g_web_request_post(GWeb *web, const char *url, const char *type, GWebInputFunc input, GWebResultFunc func, gpointer user_data) { - return do_request(web, url, type, input, func, user_data); + return do_request(web, url, type, input, -1, 0, func, NULL, user_data); } -gboolean g_web_cancel_request(GWeb *web, guint id) +guint g_web_request_post_file(GWeb *web, const char *url, + const char *type, const char *file, + GWebResultFunc func, gpointer user_data) { - if (web == NULL) - return FALSE; + struct stat st; + int fd; + guint ret; - return TRUE; + if (stat(file, &st) < 0) + return 0; + + fd = open(file, O_RDONLY); + if (fd < 0) + return 0; + + ret = do_request(web, url, type, NULL, fd, st.st_size, func, NULL, + user_data); + if (ret == 0) + close(fd); + + return ret; +} + +bool g_web_cancel_request(GWeb *web, guint id) +{ + if (!web) + return false; + + return true; } guint16 g_web_result_get_status(GWebResult *result) { - if (result == NULL) + if (!result) return 0; return result->status; } -gboolean g_web_result_get_chunk(GWebResult *result, +bool g_web_result_get_chunk(GWebResult *result, const guint8 **chunk, gsize *length) { - if (result == NULL) - return FALSE; + if (!result) + return false; - if (chunk == NULL) - return FALSE; + if (!chunk) + return false; *chunk = result->buffer; - if (length != NULL) + if (length) *length = result->length; - return TRUE; + return true; } -gboolean g_web_result_get_header(GWebResult *result, +bool g_web_result_get_header(GWebResult *result, const char *header, const char **value) { - if (result == NULL) - return FALSE; + if (!result) + return false; - if (value == NULL) - return FALSE; + if (!value) + return false; *value = g_hash_table_lookup(result->headers, header); - if (*value == NULL) - return FALSE; + if (!*value) + return false; - return TRUE; + return true; } struct _GWebParser { @@ -1142,7 +1457,7 @@ struct _GWebParser { const char *token_str; size_t token_len; size_t token_pos; - gboolean intoken; + bool intoken; GString *content; GWebParserFunc func; gpointer user_data; @@ -1153,20 +1468,17 @@ GWebParser *g_web_parser_new(const char *begin, const char *end, { GWebParser *parser; + if (!begin || !end) + return NULL; + parser = g_try_new0(GWebParser, 1); - if (parser == NULL) + if (!parser) return NULL; parser->ref_count = 1; parser->begin_token = g_strdup(begin); parser->end_token = g_strdup(end); - - if (parser->begin_token == NULL) { - g_free(parser); - return NULL; - } - parser->func = func; parser->user_data = user_data; @@ -1174,7 +1486,7 @@ GWebParser *g_web_parser_new(const char *begin, const char *end, parser->token_len = strlen(parser->token_str); parser->token_pos = 0; - parser->intoken = FALSE; + parser->intoken = false; parser->content = g_string_sized_new(0); return parser; @@ -1182,20 +1494,20 @@ GWebParser *g_web_parser_new(const char *begin, const char *end, GWebParser *g_web_parser_ref(GWebParser *parser) { - if (parser == NULL) + if (!parser) return NULL; - g_atomic_int_inc(&parser->ref_count); + __sync_fetch_and_add(&parser->ref_count, 1); return parser; } void g_web_parser_unref(GWebParser *parser) { - if (parser == NULL) + if (!parser) return; - if (g_atomic_int_dec_and_test(&parser->ref_count) == FALSE) + if (__sync_fetch_and_sub(&parser->ref_count, 1) != 1) return; g_string_free(parser->content, TRUE); @@ -1210,7 +1522,7 @@ void g_web_parser_feed_data(GWebParser *parser, { const guint8 *ptr = data; - if (parser == NULL) + if (!parser) return; while (length > 0) { @@ -1220,14 +1532,14 @@ void g_web_parser_feed_data(GWebParser *parser, guint8 *pos; pos = memchr(ptr, chr, length); - if (pos == NULL) { - if (parser->intoken == TRUE) + if (!pos) { + if (parser->intoken) g_string_append_len(parser->content, (gchar *) ptr, length); break; } - if (parser->intoken == TRUE) + if (parser->intoken) g_string_append_len(parser->content, (gchar *) ptr, (pos - ptr) + 1); @@ -1238,7 +1550,7 @@ void g_web_parser_feed_data(GWebParser *parser, continue; } - if (parser->intoken == TRUE) + if (parser->intoken) g_string_append_c(parser->content, ptr[0]); if (ptr[0] != chr) { @@ -1255,11 +1567,11 @@ void g_web_parser_feed_data(GWebParser *parser, parser->token_pos++; if (parser->token_pos == parser->token_len) { - if (parser->intoken == FALSE) { + if (!parser->intoken) { g_string_append(parser->content, parser->token_str); - parser->intoken = TRUE; + parser->intoken = true; parser->token_str = parser->end_token; parser->token_len = strlen(parser->end_token); parser->token_pos = 0; @@ -1271,7 +1583,7 @@ void g_web_parser_feed_data(GWebParser *parser, parser->func(str, parser->user_data); g_free(str); - parser->intoken = FALSE; + parser->intoken = false; parser->token_str = parser->begin_token; parser->token_len = strlen(parser->begin_token); parser->token_pos = 0; @@ -1282,6 +1594,6 @@ void g_web_parser_feed_data(GWebParser *parser, void g_web_parser_end_data(GWebParser *parser) { - if (parser == NULL) + if (!parser) return; }