aboutsummaryrefslogtreecommitdiffstats
path: root/facebook/facebook-http.c
diff options
context:
space:
mode:
authorjgeboski <jgeboski@gmail.com>2015-08-24 17:01:16 -0400
committerjgeboski <jgeboski@gmail.com>2015-08-24 17:01:16 -0400
commit53cedfaca78278a64cc347e8ed51bb67363b3dcc (patch)
treedd2c6cac0c3d5d4af1cccaa9cbbc7a6ca4522572 /facebook/facebook-http.c
parent60a42307f24c617029efbb7bcd432d162706c692 (diff)
downloadbitlbee-facebook-53cedfaca78278a64cc347e8ed51bb67363b3dcc.tar.gz
bitlbee-facebook-53cedfaca78278a64cc347e8ed51bb67363b3dcc.tar.bz2
bitlbee-facebook-53cedfaca78278a64cc347e8ed51bb67363b3dcc.tar.xz
Imported changes from purple-facebook
Diffstat (limited to 'facebook/facebook-http.c')
-rw-r--r--facebook/facebook-http.c1174
1 files changed, 638 insertions, 536 deletions
diff --git a/facebook/facebook-http.c b/facebook/facebook-http.c
index e5564a8..6cd9768 100644
--- a/facebook/facebook-http.c
+++ b/facebook/facebook-http.c
@@ -17,179 +17,224 @@
#include <bitlbee.h>
#include <string.h>
+#include <url.h>
#include "facebook-http.h"
#include "facebook-util.h"
-/**
- * Gets the error domain for #fb_http.
- *
- * @return The #GQuark of the error domain.
- **/
-GQuark fb_http_error_quark(void)
+struct _FbHttpPrivate
{
- static GQuark q;
+ FbHttpValues *cookies;
+ GHashTable *reqs;
+ gchar *agent;
+};
- if (G_UNLIKELY(q == 0))
- q = g_quark_from_static_string("fb-http-error-quark");
+struct _FbHttpRequestPrivate
+{
+ FbHttp *http;
+ gchar *url;
+ url_t purl;
+ gboolean post;
- return q;
+ FbHttpValues *headers;
+ FbHttpValues *params;
+
+ FbHttpFunc func;
+ gpointer data;
+
+ GError *error;
+ struct http_request *request;
+ gboolean freed;
+};
+
+G_DEFINE_TYPE(FbHttp, fb_http, G_TYPE_OBJECT);
+G_DEFINE_TYPE(FbHttpRequest, fb_http_request, G_TYPE_OBJECT);
+
+static void
+fb_http_dispose(GObject *obj)
+{
+ FbHttp *http = FB_HTTP(obj);
+ FbHttpPrivate *priv = http->priv;
+
+ g_free(priv->agent);
+ fb_http_close_requests(http);
+ g_hash_table_destroy(priv->reqs);
+ fb_http_values_free(priv->cookies);
}
-/**
- * Creates a new #fb_http. The returned #fb_http should be freed with
- * #fb_http_free() when no longer needed.
- *
- * @param agent The HTTP agent.
- *
- * @return The #fb_http or NULL on error.
- **/
-fb_http_t *fb_http_new(const gchar *agent)
+static void
+fb_http_class_init(FbHttpClass *klass)
{
- fb_http_t *http;
+ GObjectClass *gklass = G_OBJECT_CLASS(klass);
- http = g_new0(fb_http_t, 1);
+ gklass->dispose = fb_http_dispose;
+ g_type_class_add_private(klass, sizeof (FbHttpPrivate));
+}
- http->agent = g_strdup(agent);
- http->reqs = g_hash_table_new(g_direct_hash, g_direct_equal);
- http->cookies = g_hash_table_new_full(g_str_hash,
- (GEqualFunc) fb_util_str_iequal,
- g_free, g_free);
- return http;
+static void
+fb_http_init(FbHttp *http)
+{
+ FbHttpPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE(http, FB_TYPE_HTTP, FbHttpPrivate);
+ http->priv = priv;
+
+ priv->cookies = fb_http_values_new();
+ priv->reqs = g_hash_table_new(g_direct_hash, g_direct_equal);
}
-/**
- * Frees all #fb_http_req inside a #fb_http.
- *
- * @param http The #fb_http.
- **/
-void fb_http_free_reqs(fb_http_t *http)
+static void
+fb_http_req_close_nuller(struct http_request *request)
{
- GHashTableIter iter;
- gpointer key;
- if (G_UNLIKELY(http == NULL))
- return;
+}
- g_hash_table_iter_init(&iter, http->reqs);
+static void
+fb_http_request_dispose(GObject *obj)
+{
+ FbHttpRequestPrivate *priv = FB_HTTP_REQUEST(obj)->priv;
- while (g_hash_table_iter_next(&iter, &key, NULL)) {
- g_hash_table_iter_remove(&iter);
- fb_http_req_free(key);
+ if ((priv->request != NULL) && !priv->freed) {
+ /* Prevent more than one call to request->func() */
+ priv->request->func = fb_http_req_close_nuller;
+ priv->request->data = NULL;
+ http_close(priv->request);
+ }
+
+ if (priv->error != NULL) {
+ g_error_free(priv->error);
}
+
+ g_free(priv->url);
+ fb_http_values_free(priv->headers);
+ fb_http_values_free(priv->params);
}
-/**
- * Frees all memory used by a #fb_http.
- *
- * @param http The #fb_http.
- **/
-void fb_http_free(fb_http_t *http)
+static void
+fb_http_request_class_init(FbHttpRequestClass *klass)
{
- if (G_UNLIKELY(http == NULL))
- return;
+ GObjectClass *gklass = G_OBJECT_CLASS(klass);
- fb_http_free_reqs(http);
- g_hash_table_destroy(http->reqs);
- g_hash_table_destroy(http->cookies);
+ gklass->dispose = fb_http_request_dispose;
+ g_type_class_add_private(klass, sizeof (FbHttpRequestPrivate));
+}
- g_free(http->agent);
- g_free(http);
+static void
+fb_http_request_init(FbHttpRequest *req)
+{
+ FbHttpRequestPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE(req, FB_TYPE_HTTP_REQUEST,
+ FbHttpRequestPrivate);
+ req->priv = priv;
+
+ priv->headers = fb_http_values_new();
+ priv->params = fb_http_values_new();
}
-/**
- * Inserts a #va_list into a #GHashTable.
- *
- * @param table The #GHashTable.
- * @param pair The first #fb_http_pair.
- * @param ap The #va_list.
- **/
-static void fb_http_tree_ins(GHashTable *table, const fb_http_pair_t *pair,
- va_list ap)
-{
- const fb_http_pair_t *p;
- gchar *key;
- gchar *val;
+GQuark
+fb_http_error_quark(void)
+{
+ static GQuark q;
- for (p = pair; p != NULL; ) {
- if (p->key == NULL)
- continue;
+ if (G_UNLIKELY(q == 0))
+ q = g_quark_from_static_string("fb-http-error-quark");
- key = g_strdup(p->key);
- val = g_strdup(p->val);
+ return q;
+}
- g_hash_table_replace(table, key, val);
- p = va_arg(ap, const fb_http_pair_t*);
- }
+FbHttp *
+fb_http_new(const gchar *agent)
+{
+ FbHttp *http;
+ FbHttpPrivate *priv;
+
+ http = g_object_new(FB_TYPE_HTTP, NULL);
+ priv = http->priv;
+ priv->agent = g_strdup(agent);
+ return http;
}
-/**
- * Sets cookies from #fb_http_pair. If a cookie already exists, it is
- * overwritten with the new value.
- *
- * @param http The #fb_http.
- * @param pair The first #fb_http_pair.
- * @param ... The additional #fb_http_pair.
- **/
-void fb_http_cookies_set(fb_http_t *http, const fb_http_pair_t *pair, ...)
+FbHttpValues *
+fb_http_get_cookies(FbHttp *http)
{
- va_list ap;
+ FbHttpPrivate *priv;
- g_return_if_fail(http != NULL);
+ g_return_val_if_fail(FB_IS_HTTP(http), NULL);
+ priv = http->priv;
- va_start(ap, pair);
- fb_http_tree_ins(http->cookies, pair, ap);
- va_end(ap);
+ return priv->cookies;
}
-/**
- * Parses cookies from a #fb_http_req. If a cookie already exists, it
- * is overwritten with the new value.
- *
- * @param http The #fb_http.
- * @param req The #fb_http_req.
- **/
-void fb_http_cookies_parse_req(fb_http_t *http, const fb_http_req_t *req)
+void
+fb_http_close_requests(FbHttp *http)
{
+ FbHttpPrivate *priv;
+ FbHttpRequest *req;
+ GHashTableIter iter;
+
+ g_return_if_fail(FB_IS_HTTP(http));
+ priv = http->priv;
+
+ g_hash_table_iter_init(&iter, priv->reqs);
+
+ while (g_hash_table_iter_next(&iter, (gpointer) &req, NULL)) {
+ g_hash_table_iter_remove(&iter);
+ g_object_unref(req);
+ }
+}
+
+void
+fb_http_cookies_parse_request(FbHttp *http, FbHttpRequest *req)
+{
+ FbHttpPrivate *hriv;
+ FbHttpRequestPrivate *priv;
gchar **hdrs;
gchar **kv;
- gchar *str;
- gsize i;
- gsize j;
+ gchar *str;
+ guint i;
+ guint j;
- g_return_if_fail(http != NULL);
- g_return_if_fail(req != NULL);
+ g_return_if_fail(FB_IS_HTTP(http));
+ g_return_if_fail(FB_IS_HTTP_REQUEST(req));
+ hriv = http->priv;
+ priv = req->priv;
- if (req->request == NULL)
+ if (priv->request == NULL) {
return;
+ }
- hdrs = g_strsplit(req->request->reply_headers, "\r\n", 0);
+ hdrs = g_strsplit(priv->request->reply_headers, "\r\n", 0);
for (i = 0; hdrs[i] != NULL; i++) {
- if (g_ascii_strncasecmp(hdrs[i], "Set-Cookie", 10) != 0)
+ if (g_ascii_strncasecmp(hdrs[i], "Set-Cookie", 10) != 0) {
continue;
+ }
str = strchr(hdrs[i], ';');
- if (str != NULL)
+ if (str != NULL) {
str[0] = 0;
+ }
str = strchr(hdrs[i], ':');
- if (str == NULL)
+ if (str == NULL) {
continue;
+ }
str = g_strstrip(++str);
kv = g_strsplit(str, "=", 2);
for (j = 0; kv[j] != NULL; j++) {
- str = fb_http_uri_unescape(kv[j]);
+ str = g_uri_unescape_string(kv[j], NULL);
g_free(kv[j]);
kv[j] = str;
}
- if (g_strv_length(kv) > 1)
- fb_http_cookies_set(http, FB_HTTP_PAIR(kv[0], kv[1]), NULL);
+ if (g_strv_length(kv) > 1) {
+ fb_http_values_set_str(hriv->cookies, kv[0], kv[1]);
+ }
g_strfreev(kv);
}
@@ -197,568 +242,625 @@ void fb_http_cookies_parse_req(fb_http_t *http, const fb_http_req_t *req)
g_strfreev(hdrs);
}
-/**
- * Parses cookies from a string. If a cookie already exists, it is
- * overwritten with the new value.
- *
- * @param http The #fb_http.
- * @param data The string.
- **/
-void fb_http_cookies_parse_str(fb_http_t *http, const gchar *data)
+FbHttpRequest *
+fb_http_request_new(FbHttp *http, const gchar *url, gboolean post,
+ FbHttpFunc func, gpointer data)
{
- gchar **ckis;
- gchar **kv;
- gchar *str;
- gsize i;
- gsize j;
+ FbHttpPrivate *hriv;
+ FbHttpRequest *req;
+ FbHttpRequestPrivate *priv;
- g_return_if_fail(http != NULL);
- g_return_if_fail(data != NULL);
+ g_return_val_if_fail(FB_IS_HTTP(http), NULL);
+ g_return_val_if_fail(url != NULL, NULL);
+ g_return_val_if_fail(func != NULL, NULL);
- ckis = g_strsplit(data, ";", 0);
+ req = g_object_new(FB_TYPE_HTTP_REQUEST, NULL);
+ priv = req->priv;
+ hriv = http->priv;
- for (i = 0; ckis[i] != NULL; i++) {
- str = g_strstrip(ckis[i]);
- kv = g_strsplit(str, "=", 2);
+ if (!url_set(&priv->purl, url)) {
+ g_object_unref(req);
+ return NULL;
+ }
- for (j = 0; kv[j] != NULL; j++) {
- str = fb_http_uri_unescape(kv[j]);
- g_free(kv[j]);
- kv[j] = str;
+ priv->http = http;
+ priv->url = g_strdup(url);
+ priv->post = post;
+ priv->func = func;
+ priv->data = data;
+
+ if (hriv->agent != NULL) {
+ fb_http_values_set_str(priv->headers, "User-Agent", hriv->agent);
+ }
+
+ fb_http_values_set_str(priv->headers, "Host", priv->purl.host);
+ fb_http_values_set_str(priv->headers, "Accept", "*/*");
+ fb_http_values_set_str(priv->headers, "Connection", "Close");
+
+ return req;
+}
+
+const gchar *
+fb_http_request_get_data(FbHttpRequest *req, gsize *size)
+{
+ FbHttpRequestPrivate *priv;
+
+ g_return_val_if_fail(FB_IS_HTTP_REQUEST(req), NULL);
+ priv = req->priv;
+
+ if (priv->request == NULL) {
+ if (size != NULL) {
+ *size = 0;
}
- if (g_strv_length(kv) > 1)
- fb_http_cookies_set(http, FB_HTTP_PAIR(kv[0], kv[1]), NULL);
+ return NULL;
+ }
- g_strfreev(kv);
+ if (size != NULL) {
+ *size = priv->request->body_size;
}
- g_strfreev(ckis);
+ return priv->request->reply_body;
}
-/**
- * Gets a string representation of the cookies of a #fb_http. The
- * returned string should be freed with #g_free() when no longer
- * needed.
- *
- * @param http The #fb_http.
- *
- * @return The string representation of the cookies.
- **/
-gchar *fb_http_cookies_str(fb_http_t *http)
+FbHttpValues *
+fb_http_request_get_headers(FbHttpRequest *req)
{
- GHashTableIter iter;
- GString *gstr;
- gchar *key;
- gchar *val;
- gchar *str;
+ FbHttpRequestPrivate *priv;
- g_return_val_if_fail(http != NULL, NULL);
+ g_return_val_if_fail(FB_IS_HTTP_REQUEST(req), NULL);
+ priv = req->priv;
- gstr = g_string_sized_new(128);
- g_hash_table_iter_init(&iter, http->cookies);
+ return priv->headers;
+}
- while (g_hash_table_iter_next(&iter, (gpointer*) &key, (gpointer*) &val)) {
- if (val == NULL)
- val = "";
+FbHttpValues *
+fb_http_request_get_params(FbHttpRequest *req)
+{
+ FbHttpRequestPrivate *priv;
- key = fb_http_uri_escape(key);
- val = fb_http_uri_escape(val);
+ g_return_val_if_fail(FB_IS_HTTP_REQUEST(req), NULL);
+ priv = req->priv;
- str = (gstr->len > 0) ? "; " : "";
- g_string_append_printf(gstr, "%s%s=%s", str, key, val);
+ return priv->params;
+}
- g_free(key);
- g_free(val);
- }
+const gchar *
+fb_http_request_get_status(FbHttpRequest *req, gint *code)
+{
+ FbHttpRequestPrivate *priv;
- str = g_strdup(gstr->str);
- g_string_free(gstr, TRUE);
+ g_return_val_if_fail(FB_IS_HTTP_REQUEST(req), NULL);
+ priv = req->priv;
- return str;
-}
+ if (priv->request == NULL) {
+ if (code != NULL) {
+ *code = 0;
+ }
-/**
- * Creates a new #fb_http_req. The returned #fb_http_req should be
- * freed with #fb_http_req_free() when no longer needed.
- *
- * @param http The #fb_http.
- * @param host The hostname.
- * @param port The port number.
- * @param path The pathname.
- * @param func The user callback function or NULL.
- * @param data The user define data or NULL.
- *
- * @return The #fb_http_req or NULL on error.
- **/
-fb_http_req_t *fb_http_req_new(fb_http_t *http, const gchar *host,
- gint port, const gchar *path,
- fb_http_func_t func, gpointer data)
-{
- fb_http_req_t *req;
-
- req = g_new0(fb_http_req_t, 1);
-
- req->http = http;
- req->host = g_strdup(host);
- req->port = port;
- req->path = g_strdup(path);
- req->func = func;
- req->data = data;
-
- req->headers = g_hash_table_new_full(g_str_hash,
- (GEqualFunc) fb_util_str_iequal,
- g_free, g_free);
- req->params = g_hash_table_new_full(g_str_hash,
- (GEqualFunc) fb_util_str_iequal,
- g_free, g_free);
-
- fb_http_req_headers_set(req,
- FB_HTTP_PAIR("User-Agent", http->agent),
- FB_HTTP_PAIR("Host", host),
- FB_HTTP_PAIR("Accept", "*/*"),
- FB_HTTP_PAIR("Connection", "Close"),
- NULL
- );
+ return NULL;
+ }
- return req;
+ if (code != NULL) {
+ *code = priv->request->status_code;
+ }
+
+ return priv->request->status_string;
}
-/**
- * Implemented #http_input_function for nulling the callback operation.
- *
- * @param request The #http_request.
- **/
-static void fb_http_req_close_nuller(struct http_request *request)
+GError *
+fb_http_request_take_error(FbHttpRequest *req)
{
+ FbHttpRequestPrivate *priv;
+ GError *err;
+
+ g_return_val_if_fail(FB_IS_HTTP_REQUEST(req), NULL);
+ priv = req->priv;
+
+ err = priv->error;
+ priv->error = NULL;
+ return err;
}
-/**
- * Closes the underlying #http_request.
- *
- * @param callback TRUE to execute the callback, otherwise FALSE.
- *
- * @param req The #fb_http_req.
- **/
-static void fb_http_req_close(fb_http_req_t *req, gboolean callback)
+static void
+fb_http_request_debug(FbHttpRequest *req, gboolean response,
+ const gchar *header, const gchar *body)
{
- g_return_if_fail(req != NULL);
+ const gchar *action;
+ const gchar *method;
+ const gchar *status;
+ FbHttpRequestPrivate *priv = req->priv;
+ gchar **lines;
+ gchar *str;
+ gint code;
+ guint i;
- b_event_remove(req->toid);
+ status = fb_http_request_get_status(req, &code);
+ action = response ? "Response" : "Request";
+ method = priv->post ? "POST" : "GET";
- if ((req->err == NULL) && (req->scode == 0)) {
- g_set_error(&req->err, FB_HTTP_ERROR, FB_HTTP_ERROR_CLOSED,
- "Request closed");
+ if (status != NULL) {
+ str = g_strdup_printf(" (%s)", status);
+ } else if (response) {
+ str = g_strdup_printf(" (%d)", code);
+ } else {
+ str = g_strdup("");
}
- if (callback && (req->func != NULL))
- req->func(req, req->data);
+ fb_util_debug_info("%s %s (%p): %s%s",
+ method, action, req,
+ priv->url, str);
+ g_free(str);
- if (req->request != NULL) {
- /* Prevent more than one call to request->func() */
- req->request->func = fb_http_req_close_nuller;
- req->request->data = NULL;
+ if ((header != NULL) && (strlen(header) > 0)) {
+ lines = g_strsplit(header, "\n", 0);
- if (!(req->request->flags & FB_HTTP_CLIENT_FREED))
- http_close(req->request);
+ for (i = 0; lines[i] != NULL; i++) {
+ fb_util_debug_info(" %s", lines[i]);
+ }
+
+ g_strfreev(lines);
+ } else {
+ fb_util_debug_info(" ** No header data **");
+ fb_util_debug_info("%s", "");
}
- req->status = NULL;
- req->scode = 0;
- req->header = NULL;
- req->body = NULL;
- req->body_size = 0;
- req->toid = 0;
- req->request = NULL;
+ if ((body != NULL) && (strlen(body) > 0)) {
+ lines = g_strsplit(body, "\n", 0);
+
+ for (i = 0; lines[i] != NULL; i++) {
+ fb_util_debug_info(" %s", lines[i]);
+ }
+
+ g_strfreev(lines);
+ } else {
+ fb_util_debug_info(" ** No body data **");
+ }
}
-/**
- * Frees all memory used by a #fb_http_req.
- *
- * @param req The #fb_http_req.
- **/
-void fb_http_req_free(fb_http_req_t *req)
+static void
+fb_http_request_cb(struct http_request *request)
{
- if (G_UNLIKELY(req == NULL))
- return;
+ const gchar *status;
+ FbHttpRequest *req = request->data;
+ FbHttpRequestPrivate *priv = req->priv;
+ gint code;
+
+ status = fb_http_request_get_status(req, &code);
+ g_hash_table_remove(priv->http->priv->reqs, req);
+ priv->freed = TRUE;
+
+ switch (code) {
+ case 200:
+ case 301:
+ case 302:
+ case 303:
+ case 307:
+ break;
- fb_http_req_close(req, TRUE);
+ default:
+ g_set_error(&priv->error, FB_HTTP_ERROR, code, "%s", status);
+ }
- if (req->err != NULL)
- g_error_free(req->err);
+ fb_http_request_debug(req, TRUE, priv->request->reply_headers,
+ priv->request->reply_body);
- g_hash_table_destroy(req->headers);
- g_hash_table_destroy(req->params);
+ if (G_LIKELY(priv->func != NULL)) {
+ priv->func(req, priv->data);
+ }
- g_free(req->path);
- g_free(req->host);
- g_free(req);
+ g_object_unref(req);
}
-#ifdef DEBUG_FACEBOOK
-static void fb_http_req_debug(fb_http_req_t *req, gboolean response,
- const gchar *header, const gchar *body)
+void
+fb_http_request_send(FbHttpRequest *req)
{
- const gchar *act;
- const gchar *type;
- const gchar *prot;
- gchar *str;
- gchar **ls;
- guint i;
-
- if (req->err != NULL)
- str = g_strdup_printf(" (%s)", req->err->message);
- else if (req->status != NULL)
- str = g_strdup_printf(" (%s)", req->status);
- else
- str = g_strdup("");
+ FbHttpPrivate *hriv;
+ FbHttpRequestPrivate *priv;
+ gchar *hdrs;
+ gchar *prms;
+ gchar *str;
+ gsize size;
- act = response ? "Response" : "Request";
- type = (req->flags & FB_HTTP_REQ_FLAG_POST) ? "POST" : "GET";
- prot = (req->flags & FB_HTTP_REQ_FLAG_SSL) ? "https" : "http";
+ g_return_if_fail(FB_IS_HTTP_REQUEST(req));
+ priv = req->priv;
+ hriv = priv->http->priv;
- FB_UTIL_DEBUGLN("%s %s (%p): %s://%s:%d%s%s",
- type, act, req, prot,
- req->host, req->port,
- req->path, str);
- g_free(str);
+ if (g_hash_table_size(hriv->cookies) > 0) {
+ str = fb_http_values_str_cookies(hriv->cookies);
+ fb_http_values_set_str(priv->headers, "Cookie", str);
+ g_free(str);
+ }
- if (req->rsc > 0)
- FB_UTIL_DEBUGLN("Reattempt: #%u", req->rsc);
+ prms = fb_http_values_str_params(priv->params, NULL);
- if ((header != NULL) && (strlen(header) > 0)) {
- ls = g_strsplit(header, "\n", 0);
+ if (priv->post) {
+ size = strlen(prms);
+ fb_http_values_set_strf(priv->headers, "Content-Length",
+ "%" G_GSIZE_FORMAT, size);
+ fb_http_values_set_str(priv->headers, "Content-Type",
+ "application/x-www-form-urlencoded");
+ }
- for (i = 0; ls[i] != NULL; i++)
- FB_UTIL_DEBUGLN(" %s", ls[i]);
+ hdrs = fb_http_values_str_headers(priv->headers);
- g_strfreev(ls);
+ if (priv->post) {
+ str = g_strdup_printf("POST %s HTTP/1.1\r\n%s\r\n%s",
+ priv->purl.file, hdrs, prms);
} else {
- FB_UTIL_DEBUGLN(" ** No header data **");
- FB_UTIL_DEBUGLN("");
+ str = g_strdup_printf("GET %s?%s HTTP/1.1\r\n%s\r\n",
+ priv->purl.file, prms, hdrs);
}
- if ((body != NULL) && (strlen(body) > 0)) {
- ls = g_strsplit(body, "\n", 0);
+ fb_http_request_debug(req, FALSE, hdrs, prms);
+ priv->request = http_dorequest(priv->purl.host, priv->purl.port,
+ priv->purl.proto == PROTO_HTTPS,
+ str, fb_http_request_cb, req);
- for (i = 0; ls[i] != NULL; i++)
- FB_UTIL_DEBUGLN(" %s", ls[i]);
+ g_free(hdrs);
+ g_free(prms);
+ g_free(str);
- g_strfreev(ls);
- } else {
- FB_UTIL_DEBUGLN(" ** No body data **");
+ if (G_UNLIKELY(priv->request == NULL)) {
+ g_set_error(&priv->error, FB_HTTP_ERROR, FB_HTTP_ERROR_INIT,
+ "Failed to init request");
+
+ if (G_LIKELY(priv->func != NULL)) {
+ priv->func(req, priv->data);
+ }
+
+ g_object_unref(req);
+ return;
}
+
+ g_hash_table_replace(hriv->reqs, req, req);
}
-#endif /* DEBUG_FACEBOOK */
-/**
- * Sets headers from #fb_http_pair. If a header already exists, it is
- * overwritten with the new value.
- *
- * @param req The #fb_http_req.
- * @param pair The first #fb_http_pair.
- * @param ... The additional #fb_http_pair.
- **/
-void fb_http_req_headers_set(fb_http_req_t *req, const fb_http_pair_t *pair,
- ...)
+gboolean
+fb_http_urlcmp(const gchar *url1, const gchar *url2, gboolean protocol)
{
- va_list ap;
+ gboolean ret;
+ url_t purl1;
+ url_t purl2;
- g_return_if_fail(req != NULL);
+ if ((url1 == NULL) || (url2 == NULL)) {
+ return url1 == url2;
+ }
- va_start(ap, pair);
- fb_http_tree_ins(req->headers, pair, ap);
- va_end(ap);
+ if (!url_set(&purl1, url1) || !url_set(&purl2, url2)) {
+ return g_ascii_strcasecmp(url1, url2) == 0;
+ }
+
+ ret = (g_ascii_strcasecmp(purl1.host, purl2.host) == 0) &&
+ (g_strcmp0(purl1.file, purl2.file) == 0) &&
+ (g_strcmp0(purl1.user, purl2.user) == 0) &&
+ (g_strcmp0(purl1.pass, purl2.pass) == 0);
+
+ if (ret && protocol) {
+ ret = (purl1.proto == purl2.proto) && (purl1.port == purl2.port);
+ }
+
+ return ret;
}
-/**
- * Sets parameters from #fb_http_pair. If a parameter already exists,
- * it is overwritten with the new value.
- *
- * @param req The #fb_http_req.
- * @param pair The first #fb_http_pair.
- * @param ... The additional #fb_http_pair.
- **/
-void fb_http_req_params_set(fb_http_req_t *req, const fb_http_pair_t *pair,
- ...)
+static gboolean
+fb_http_value_equal(gconstpointer a, gconstpointer b)
{
- va_list ap;
+ return g_ascii_strcasecmp(a, b) == 0;
+}
- g_return_if_fail(req != NULL);
+FbHttpValues *
+fb_http_values_new(void)
+{
+ return g_hash_table_new_full(g_str_hash, fb_http_value_equal,
+ g_free, g_free);
+}
- va_start(ap, pair);
- fb_http_tree_ins(req->params, pair, ap);
- va_end(ap);
+void
+fb_http_values_free(FbHttpValues *values)
+{
+ g_hash_table_destroy(values);
}
-/**
- * Implemented #b_event_handler for resending failed a #fb_http_req.
- *
- * @param data The user defined data, which is a #fb_http_req.
- * @param fd The file descriptor.
- * @param cond The #b_input_condition.
- *
- * @return FALSE to kill the timer.
- **/
-static gboolean fb_http_req_done_error(gpointer data, gint fd,
- b_input_condition cond)
+void
+fb_http_values_consume(FbHttpValues *values, FbHttpValues *consume)
{
- fb_http_req_t *req = data;
+ GHashTableIter iter;
+ gpointer key;
+ gpointer val;
+
+ g_hash_table_iter_init(&iter, consume);
- fb_http_req_send(req);
- return FALSE;
+ while (g_hash_table_iter_next(&iter, &key, &val)) {
+ g_hash_table_iter_steal(&iter);
+ g_hash_table_replace(values, key, val);
+ }
+
+ g_hash_table_destroy(consume);
}
-/**
- * Processes all #fb_http_req by resending, queuing, and freeing.
- *
- * @param req The #fb_http_req.
- **/
-static void fb_http_req_done(fb_http_req_t *req)
-{
-#ifdef DEBUG_FACEBOOK
- fb_http_req_debug(req, TRUE, req->header, req->body);
-#endif /* DEBUG_FACEBOOK */
-
- if (req->err != NULL) {
- if (req->rsc < FB_HTTP_RESEND_MAX) {
- fb_http_req_close(req, FALSE);
- g_error_free(req->err);
- req->err = NULL;
-
- req->toid = b_timeout_add(FB_HTTP_RESEND_TIMEOUT,
- fb_http_req_done_error, req);
- req->rsc++;
+void
+fb_http_values_parse(FbHttpValues *values, const gchar *data, gboolean isurl)
+{
+ const gchar *tail;
+ gchar *key;
+ gchar **params;
+ gchar *val;
+ guint i;
+
+ g_return_if_fail(data != NULL);
+
+ if (isurl) {
+ data = strchr(data, '?');
+
+ if (data++ == NULL) {
return;
}
- g_prefix_error(&req->err, "HTTP: ");
+ tail = strchr(data, '#');
+
+ if (tail != NULL) {
+ data = g_strndup(data, tail - data);
+ } else {
+ data = g_strdup(data);
+ }
+ }
+
+ params = g_strsplit(data, "&", 0);
+
+ for (i = 0; params[i] != NULL; i++) {
+ key = params[i];
+ val = strchr(params[i], '=');
+
+ if (val == NULL) {
+ continue;
+ }
+
+ *(val++) = 0;
+ key = g_uri_unescape_string(key, NULL);
+ val = g_uri_unescape_string(val, NULL);
+ g_hash_table_replace(values, key, val);
+ }
+
+ if (isurl) {
+ g_free((gchar*) data);
}
- g_hash_table_remove(req->http->reqs, req);
- fb_http_req_free(req);
+ g_strfreev(params);
}
-/**
- * Implemented #http_input_function for all #fb_http_req.
- *
- * @param request The #http_request.
- **/
-static void fb_http_req_cb(struct http_request *request)
+gchar *
+fb_http_values_str_cookies(FbHttpValues *values)
{
- fb_http_req_t *req = request->data;
+ GHashTableIter iter;
+ gchar *key;
+ gchar *val;
+ GString *ret;
- /* Shortcut request elements */
- req->status = request->status_string;
- req->scode = request->status_code;
- req->header = request->reply_headers;
- req->body = request->reply_body;
- req->body_size = request->body_size;
+ ret = g_string_new(NULL);
+ g_hash_table_iter_init(&iter, values);
- switch (req->scode) {
- case 200:
- case 301:
- case 302:
- case 303:
- case 307:
- break;
+ while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
+ if (val == NULL) {
+ val = "";
+ }
- default:
- g_set_error(&req->err, FB_HTTP_ERROR, req->scode, "%s", req->status);
+ if (ret->len > 0) {
+ g_string_append(ret, "; ");
+ }
+
+ g_string_append_uri_escaped(ret, key, NULL, TRUE);
+ g_string_append_c(ret, '=');
+ g_string_append_uri_escaped(ret, val, NULL, TRUE);
}
- req->request->flags |= FB_HTTP_CLIENT_FREED;
- fb_http_req_done(req);
+ return g_string_free(ret, FALSE);
}
-/**
- * Implemented #b_event_handler for handling a timed out #fb_http_req.
- *
- * @param data The user defined data, which is a #fb_http_req.
- * @param fd The file descriptor.
- * @param cond The #b_input_condition.
- *
- * @return FALSE to kill the timer.
- **/
-static gboolean fb_http_req_send_timeout(gpointer data, gint fd,
- b_input_condition cond)
+gchar *
+fb_http_values_str_headers(FbHttpValues *values)
{
- fb_http_req_t *req = data;
+ GHashTableIter iter;
+ gchar *key;
+ gchar *val;
+ GString *ret;
+
+ ret = g_string_new(NULL);
+ g_hash_table_iter_init(&iter, values);
+
+ while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
+ if (val == NULL) {
+ val = "";
+ }
- g_set_error(&req->err, FB_HTTP_ERROR, FB_HTTP_ERROR_TIMEOUT,
- "Request timed out");
+ g_string_append_printf(ret, "%s: %s\r\n", key, val);
+ }
- req->toid = 0;
- fb_http_req_done(req);
- return FALSE;
+ return g_string_free(ret, FALSE);
}
-/**
- * Assembles a #fb_http_req. The returned strings should be freed with
- * #g_free() when no longer needed.
- *
- * @param req The #fb_http_req.
- * @param hs The return location for the header string.
- * @param ps The return location for the param string.
- * @param fs The return location for the full string.
- **/
-static void fb_http_req_asm(fb_http_req_t *req, gchar **hs, gchar **ps,
- gchar **fs)
-{
- GHashTableIter iter;
- GString *hgs;
- GString *pgs;
- gchar *str;
- gchar *key;
- gchar *val;
-
- g_hash_table_iter_init(&iter, req->params);
- pgs = g_string_sized_new(128);
-
- while (g_hash_table_iter_next(&iter, (gpointer*) &key, (gpointer*) &val)) {
- if (val == NULL)
- val = "";
+gchar *
+fb_http_values_str_params(FbHttpValues *values, const gchar *url)
+{
+ GHashTableIter iter;
+ gchar *key;
+ gchar *val;
+ GString *ret;
- key = fb_http_uri_escape(key);
- val = fb_http_uri_escape(val);
+ ret = g_string_new(NULL);
+ g_hash_table_iter_init(&iter, values);
- str = (pgs->len > 0) ? "&" : "";
- g_string_append_printf(pgs, "%s%s=%s", str, key, val);
+ while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
+ if (val == NULL) {
+ val = "";
+ }
- g_free(key);
- g_free(val);
+ if (ret->len > 0) {
+ g_string_append_c(ret, '&');
+ }
+
+ g_string_append_uri_escaped(ret, key, NULL, TRUE);
+ g_string_append_c(ret, '=');
+ g_string_append_uri_escaped(ret, val, NULL, TRUE);
}
- if (g_hash_table_size(req->http->cookies) > 0) {
- str = fb_http_cookies_str(req->http);
- fb_http_req_headers_set(req, FB_HTTP_PAIR("Cookie", str), NULL);
- g_free(str);
+ if (url != NULL) {
+ g_string_prepend_c(ret, '?');
+ g_string_prepend(ret, url);
}
- if (req->flags & FB_HTTP_REQ_FLAG_POST) {
- str = g_strdup_printf("%" G_GSIZE_FORMAT, pgs->len);
+ return g_string_free(ret, FALSE);
+}
- fb_http_req_headers_set(req,
- FB_HTTP_PAIR("Content-Type", "application/"
- "x-www-form-urlencoded"),
- FB_HTTP_PAIR("Content-Length", str),
- NULL
- );
+gboolean
+fb_http_values_remove(FbHttpValues *values, const gchar *name)
+{
+ return g_hash_table_remove(values, name);
+}
- g_free(str);
- }
+GList *
+fb_http_values_get_keys(FbHttpValues *values)
+{
+ return g_hash_table_get_keys(values);
+}
- g_hash_table_iter_init(&iter, req->headers);
- hgs = g_string_sized_new(128);
+static const gchar *
+fb_http_values_get(FbHttpValues *values, const gchar *name, GError **error)
+{
+ const gchar *ret;
- while (g_hash_table_iter_next(&iter, (gpointer*) &key, (gpointer*) &val)) {
- if (val == NULL)
- val = "";
+ ret = g_hash_table_lookup(values, name);
- g_string_append_printf(hgs, "%s: %s\r\n", key, val);
+ if (ret == NULL) {
+ g_set_error(error, FB_HTTP_ERROR, FB_HTTP_ERROR_NOMATCH,
+ "No matches for %s", name);
+ return NULL;
}
- if (req->flags & FB_HTTP_REQ_FLAG_POST) {
- *fs = g_strdup_printf("POST %s HTTP/1.1\r\n%s\r\n%s",
- req->path, hgs->str, pgs->str);
- } else {
- *fs = g_strdup_printf("GET %s?%s HTTP/1.1\r\n%s\r\n",
- req->path, pgs->str, hgs->str);
+ return ret;
+}
+
+gboolean
+fb_http_values_get_bool(FbHttpValues *values, const gchar *name,
+ GError **error)
+{
+ const gchar *val;
+
+ val = fb_http_values_get(values, name, error);
+
+ if (val == NULL) {
+ return FALSE;
}
- *hs = g_string_free(hgs, FALSE);
- *ps = g_string_free(pgs, FALSE);
+ return bool2int((gchar *) name);
}
-/**
- * Sends a #fb_http_req.
- *
- * @param req The #fb_http_req.
- **/
-void fb_http_req_send(fb_http_req_t *req)
+gdouble
+fb_http_values_get_dbl(FbHttpValues *values, const gchar *name,
+ GError **error)
{
- gchar *str;
- gchar *hs;
- gchar *ps;
+ const gchar *val;
- g_return_if_fail(req != NULL);
+ val = fb_http_values_get(values, name, error);
- fb_http_req_asm(req, &hs, &ps, &str);
+ if (val == NULL) {
+ return 0.0;
+ }
-#ifdef DEBUG_FACEBOOK
- fb_http_req_debug(req, FALSE, hs, ps);
-#endif /* DEBUG_FACEBOOK */
+ return g_ascii_strtod(val, NULL);
+}
- req->request = http_dorequest(req->host, req->port,
- (req->flags & FB_HTTP_REQ_FLAG_SSL),
- str, fb_http_req_cb, req);
- g_hash_table_add(req->http->reqs, req);
+gint64
+fb_http_values_get_int(FbHttpValues *values, const gchar *name,
+ GError **error)
+{
+ const gchar *val;
- g_free(hs);
- g_free(ps);
- g_free(str);
+ val = fb_http_values_get(values, name, error);
- if (G_UNLIKELY(req->request == NULL)) {
- g_set_error(&req->err, FB_HTTP_ERROR, FB_HTTP_ERROR_INIT,
- "Failed to init request");
- fb_http_req_done(req);
- return;
+ if (val == NULL) {
+ return 0;
}
- /* Prevent automatic redirection */
- req->request->redir_ttl = 0;
+ return g_ascii_strtoll(val, NULL, 10);
+}
- if (req->timeout > 0) {
- req->toid = b_timeout_add(req->timeout, fb_http_req_send_timeout,
- req);
- }
+
+const gchar *
+fb_http_values_get_str(FbHttpValues *values, const gchar *name,
+ GError **error)
+{
+ return fb_http_values_get(values, name, error);
}
-/**
- * Escapes the characters of a string to make it URL safe. The returned
- * string should be freed with #g_free() when no longer needed.
- *
- * @param unescaped The string.
- *
- * @return The escaped string or NULL on error.
- **/
-gchar *fb_http_uri_escape(const gchar *unescaped)
+gchar *
+fb_http_values_dup_str(FbHttpValues *values, const gchar *name,
+ GError **error)
{
- gchar *ret;
- gchar *str;
+ const gchar *str;
+
+ str = fb_http_values_get_str(values, name, error);
+ return g_strdup(str);
+}
- g_return_val_if_fail(unescaped != NULL, NULL);
+static void
+fb_http_values_set(FbHttpValues *values, const gchar *name, gchar *value)
+{
+ gchar *key;
- str = g_strndup(unescaped, (strlen(unescaped) * 3) + 1);
- http_encode(str);
+ key = g_strdup(name);
+ g_hash_table_replace(values, key, value);
+}
- ret = g_strdup(str);
- g_free(str);
+void
+fb_http_values_set_bool(FbHttpValues *values, const gchar *name,
+ gboolean value)
+{
+ gchar *val;
- return ret;
+ val = g_strdup(value ? "true" : "false");
+ fb_http_values_set(values, name, val);
}
-/**
- * Unescapes the characters of a string to make it a normal string. The
- * returned string should be freed with #g_free() when no longer needed.
- *
- * @param escaped The string.
- *
- * @return The unescaped string or NULL on error.
- **/
-gchar *fb_http_uri_unescape(const gchar *escaped)
+void
+fb_http_values_set_dbl(FbHttpValues *values, const gchar *name, gdouble value)
{
- gchar *ret;
- gchar *str;
+ gchar *val;
- g_return_val_if_fail(escaped != NULL, NULL);
+ val = g_strdup_printf("%f", value);
+ fb_http_values_set(values, name, val);
+}
- str = g_strdup(escaped);
- http_decode(str);
+void
+fb_http_values_set_int(FbHttpValues *values, const gchar *name, gint64 value)
+{
+ gchar *val;
- ret = g_strdup(str);
- g_free(str);
+ val = g_strdup_printf("%" G_GINT64_FORMAT, value);
+ fb_http_values_set(values, name, val);
+}
- return ret;
+void
+fb_http_values_set_str(FbHttpValues *values, const gchar *name,
+ const gchar *value)
+{
+ gchar *val;
+
+ val = g_strdup(value);
+ fb_http_values_set(values, name, val);
+}
+
+void
+fb_http_values_set_strf(FbHttpValues *values, const gchar *name,
+ const gchar *format, ...)
+{
+ gchar *val;
+ va_list ap;
+
+ va_start(ap, format);
+ val = g_strdup_vprintf(format, ap);
+ va_end(ap);
+
+ fb_http_values_set(values, name, val);
}