From 29343066a1aa9a9974700399b89db0424380cc1e Mon Sep 17 00:00:00 2001 From: "Ruslan N. Marchenko" Date: Wed, 4 Dec 2019 07:19:56 +0100 Subject: [PATCH 1/4] Always seek to prefer latest spec --- src/hfu_disco.c | 60 +++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/src/hfu_disco.c b/src/hfu_disco.c index 0048e4f..1b1528d 100644 --- a/src/hfu_disco.c +++ b/src/hfu_disco.c @@ -26,46 +26,42 @@ static void jabber_hfu_disco_info_cb(JabberStream *js, const char *from, query = xmlnode_get_child_with_namespace(packet, "query", NS_DISCO_INFO); - if (type == JABBER_IQ_RESULT && query) - { - js_data = g_hash_table_lookup(HFUJabberStreamDataTable, js); - if (!js_data) return; + if (type != JABBER_IQ_RESULT || query == NULL) + return; + + js_data = g_hash_table_lookup(HFUJabberStreamDataTable, js); + // Always prefer latest standard, skip if already found + if (!js_data || str_equal(js_data->ns, NS_HTTP_FILE_UPLOAD_V0)) + return; for (feature = xmlnode_get_child(query, "feature") ; feature; feature = xmlnode_get_next_twin(feature)) { - const char *var = xmlnode_get_attrib(feature, "var"); - if(!var) - continue; + const char *var = xmlnode_get_attrib(feature, "var"); + if(!var) + continue; - if(str_equal(var, NS_HTTP_FILE_UPLOAD)) - js_data->ns = NS_HTTP_FILE_UPLOAD; - else if(str_equal(var, NS_HTTP_FILE_UPLOAD_V0)) - js_data->ns = NS_HTTP_FILE_UPLOAD_V0; + if(str_equal(var, NS_HTTP_FILE_UPLOAD) && js_data->ns == NULL) + js_data->ns = NS_HTTP_FILE_UPLOAD; + else if(str_equal(var, NS_HTTP_FILE_UPLOAD_V0)) + js_data->ns = NS_HTTP_FILE_UPLOAD_V0; + else + continue; + + g_free(js_data->host); + js_data->host = g_strdup(from); - if (str_equal(var, NS_HTTP_FILE_UPLOAD) || str_equal(var, NS_HTTP_FILE_UPLOAD_V0)) + x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"); + if (x) + { + for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { - js_data->host = g_strdup(from); - - x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"); - if (x) - { - for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) - { - const char *var = xmlnode_get_attrib(field, "var"); - - if(var && str_equal(var, "max-file-size")) - { - if((value = xmlnode_get_child(field, "value"))) - { - js_data->max_file_size = (gsize) atol(xmlnode_get_data(value)); - } - } - } - } + const char *var = xmlnode_get_attrib(field, "var"); + + if(var && str_equal(var, "max-file-size")) + if((value = xmlnode_get_child(field, "value"))) + js_data->max_file_size = (gsize) atol(xmlnode_get_data(value)); } } - - } } -- GitLab From 77dc40a5d010f19cfb6a912c6286809499d7547f Mon Sep 17 00:00:00 2001 From: "Ruslan N. Marchenko" Date: Wed, 4 Dec 2019 23:29:55 +0100 Subject: [PATCH 2/4] Some plumbing and header injection --- src/jabber_http_file_upload.c | 48 +++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/jabber_http_file_upload.c b/src/jabber_http_file_upload.c index 59435b1..dc2d201 100644 --- a/src/jabber_http_file_upload.c +++ b/src/jabber_http_file_upload.c @@ -64,7 +64,7 @@ static void jabber_hfu_http_read(gpointer user_data, PurpleSslConnection *ssl_co static void jabber_hfu_http_send_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) { PurpleHttpURL *httpurl; - gchar *headers, *host, *path; + gchar *headers, *host, *path, *auth = NULL, *expire = NULL, *cookie = NULL; PurpleXfer *xfer = data; HFUXfer *hfux = purple_xfer_get_protocol_data(xfer); @@ -73,10 +73,18 @@ static void jabber_hfu_http_send_connect_cb(gpointer data, PurpleSslConnection * httpurl = purple_http_url_parse(hfux->put_url); path = purple_http_url_get_path(httpurl); - if (str_equal(js_data->ns, NS_HTTP_FILE_UPLOAD_V0)) - host = g_hash_table_lookup(hfux->put_headers, "Host") ?: purple_http_url_get_host(httpurl); - else - host = purple_http_url_get_host(httpurl); + if (str_equal(js_data->ns, NS_HTTP_FILE_UPLOAD_V0)) { + char *a = g_hash_table_lookup(hfux->put_headers, "Authorisation"); + char *c = g_hash_table_lookup(hfux->put_headers, "Cookie"); + char *e = g_hash_table_lookup(hfux->put_headers, "Expires"); + if(a) + auth = g_strdup_printf("Authorisation: %s\r\n", a); + if(c) + cookie = g_strdup_printf("Cookie: %s\r\n", c); + if(e) + expire = g_strdup_printf("Expires: %s\r\n", e); + } + host = purple_http_url_get_host(httpurl); headers = g_strdup_printf("PUT /%s HTTP/1.0\r\n" @@ -85,12 +93,13 @@ static void jabber_hfu_http_send_connect_cb(gpointer data, PurpleSslConnection * "Content-Length: %" G_GSIZE_FORMAT "\r\n" "Content-Type: application/octet-stream\r\n" "User-Agent: libpurple\r\n" - "\r\n", - path, - host, - (gsize) purple_xfer_get_size(xfer)); + "%s%s%s\r\n", + path, host, (gsize) purple_xfer_get_size(xfer), + (auth?:""), (expire?:""), (cookie?:"")); - //add headers!!! + g_free(auth); + g_free(expire); + g_free(cookie); purple_ssl_write(ssl_connection, headers, strlen(headers)); @@ -401,6 +410,24 @@ static void jabber_hfu_signed_on_cb(PurpleConnection *conn, void *data) jabber_hfu_disco_items_server(js); } +static void jabber_hfu_signed_off_cb(PurpleConnection *conn, void *data) +{ + PurpleAccount *account = purple_connection_get_account(conn); + + if (strcmp(JABBER_PLUGIN_ID, purple_account_get_protocol_id(account))) + return; + + JabberStream *js = purple_connection_get_protocol_data(conn); + + HFUJabberStreamData *js_data = g_hash_table_lookup(HFUJabberStreamDataTable, js); + + if(js_data) { + g_hash_table_remove(HFUJabberStreamDataTable, js); + g_free(js_data->host); + g_free(js_data); + } +} + static void jabber_hfu_send_act(PurpleBlistNode *node, gpointer ignored) { PurpleConnection *gc = NULL; @@ -465,6 +492,7 @@ gboolean plugin_load(PurplePlugin *plugin) jabber_protocol_info->blist_node_menu = jabber_hfu_blist_node_menu; purple_signal_connect(purple_connections_get_handle(), "signed-on", jabber_plugin, PURPLE_CALLBACK(jabber_hfu_signed_on_cb), NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-off", jabber_plugin, PURPLE_CALLBACK(jabber_hfu_signed_off_cb), NULL); HFUJabberStreamDataTable = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); -- GitLab From 17d39e099e6248a03fa7f3cf79e616b061e69872 Mon Sep 17 00:00:00 2001 From: "Ruslan N. Marchenko" Date: Sun, 8 Dec 2019 22:38:37 +0100 Subject: [PATCH 3/4] Add OOB Data to im messages --- src/jabber_http_file_upload.c | 38 ++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/jabber_http_file_upload.c b/src/jabber_http_file_upload.c index dc2d201..5879bf8 100644 --- a/src/jabber_http_file_upload.c +++ b/src/jabber_http_file_upload.c @@ -43,6 +43,8 @@ static inline PurpleHttpURL *purple_http_url_parse(const gchar *url) { return ret; } +GHashTable *ht_hfu_sending; + #define purple_http_url_get_host(httpurl) (httpurl->host) #define purple_http_url_get_port(httpurl) (httpurl->port) #define purple_http_url_get_path(httpurl) (httpurl->path) @@ -251,6 +253,27 @@ static void jabber_hfu_send_request(PurpleXfer *xfer) g_free(filemime); } +static void +jabber_hfu_xmlnode_send_cb(PurpleConnection *gc, xmlnode **packet, gpointer null) +{ + + if (*packet != NULL && (*packet)->name) { + if (g_strcmp0 ((*packet)->name, "message") == 0) { + xmlnode *node_body = xmlnode_get_child (*packet, "body"); + if (node_body) { + HFUXfer *hfux = g_hash_table_lookup(ht_hfu_sending, xmlnode_get_data(node_body)); + if(hfux) { + xmlnode *x, *url; + x = xmlnode_new_child (*packet, "x"); + xmlnode_set_namespace (x, NS_OOB_X_DATA); + g_debug ("Adding OOB Data to URL: %s", hfux->get_url); + url = xmlnode_new_child(x, "url"); + xmlnode_insert_data(url, hfux->get_url, -1); + } + } + } + } +} static void jabber_hfu_send_url_to_conv(PurpleXfer *xfer) { @@ -280,7 +303,10 @@ static void jabber_hfu_send_url_to_conv(PurpleXfer *xfer) else if (conv_type == PURPLE_CONV_TYPE_IM) { PurpleConvIm *conv_im = purple_conversation_get_im_data(conv); - purple_conv_im_send(conv_im, hfux->get_url); + // Send raw URL to handle it later + g_hash_table_insert(ht_hfu_sending, hfux->get_url, hfux); + purple_conv_im_send_with_flags(conv_im, hfux->get_url, PURPLE_MESSAGE_RAW); + g_hash_table_remove(ht_hfu_sending, hfux->get_url); } } } @@ -333,6 +359,7 @@ static void jabber_hfu_xfer_init(PurpleXfer *xfer) hfux->js_data = js_data; + purple_debug_info("jabber_http_upload", "in jabber_hfu_xfer_init\n"); if (!js_data->ns) { purple_notify_error(hfux->js->gc, _("File Send Failed"), _("File Send Failed"), _("HTTP File Upload is not supported by server")); @@ -471,6 +498,9 @@ static GList *jabber_hfu_blist_node_menu(PurpleBlistNode *node) gboolean plugin_unload(PurplePlugin *plugin) { + purple_signals_disconnect_by_handle(plugin); + g_hash_table_destroy(ht_hfu_sending); + g_hash_table_destroy(HFUJabberStreamDataTable); return TRUE; } @@ -491,10 +521,12 @@ gboolean plugin_load(PurplePlugin *plugin) old_blist_node_menu = jabber_protocol_info->blist_node_menu; jabber_protocol_info->blist_node_menu = jabber_hfu_blist_node_menu; - purple_signal_connect(purple_connections_get_handle(), "signed-on", jabber_plugin, PURPLE_CALLBACK(jabber_hfu_signed_on_cb), NULL); - purple_signal_connect(purple_connections_get_handle(), "signed-off", jabber_plugin, PURPLE_CALLBACK(jabber_hfu_signed_off_cb), NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-on", plugin, PURPLE_CALLBACK(jabber_hfu_signed_on_cb), NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-off", plugin, PURPLE_CALLBACK(jabber_hfu_signed_off_cb), NULL); HFUJabberStreamDataTable = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); + ht_hfu_sending = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + purple_signal_connect(jabber_plugin, "jabber-sending-xmlnode", plugin, PURPLE_CALLBACK(jabber_hfu_xmlnode_send_cb), NULL); return TRUE; } -- GitLab From fab1a75cf37e22da5ebc0fc0cecaaef8baee86bd Mon Sep 17 00:00:00 2001 From: "Ruslan N. Marchenko" Date: Sun, 15 Dec 2019 12:51:08 +0100 Subject: [PATCH 4/4] Fix some memory leaks and premature transfer closure --- src/jabber_http_file_upload.c | 100 ++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/src/jabber_http_file_upload.c b/src/jabber_http_file_upload.c index 5879bf8..7799299 100644 --- a/src/jabber_http_file_upload.c +++ b/src/jabber_http_file_upload.c @@ -36,7 +36,6 @@ typedef struct { gchar *passwd; } PurpleHttpURL; - static inline PurpleHttpURL *purple_http_url_parse(const gchar *url) { PurpleHttpURL *ret = g_new0(PurpleHttpURL, 1); purple_url_parse(url, &(ret->host), &(ret->port), &(ret->path), &(ret->user), &(ret->passwd)); @@ -56,24 +55,41 @@ static inline void purple_http_url_free(PurpleHttpURL *phl) { g_free(phl->host); static void jabber_hfu_http_read(gpointer user_data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) { - gchar buf[1024]; + PurpleXfer *xfer = user_data; + gchar buf[1024] = {0}; + + //Read the server buffer + size_t rl = purple_ssl_read(ssl_connection, buf, 1024); + purple_debug_info("jabber_http_upload", "Server file send response was %ld bytes: %s\n", rl, buf); + + if(rl == (size_t)-1) + return; - //Flush the server buffer - purple_ssl_read(ssl_connection, buf, 1024); - purple_debug_info("jabber_http_upload", "Server file send response was %s\n", buf); + if ((purple_xfer_get_bytes_sent(xfer)) >= purple_xfer_get_size(xfer)) { + // Looking for HTTP/1.1 201 + if(rl > 12 && g_str_has_prefix(buf, "HTTP/1.") && g_str_has_prefix(buf+8, " 20")) { + // 20x statuses are good, should be 201 but who knows those servers + purple_xfer_set_completed(xfer, TRUE); + purple_xfer_end(xfer); + return; + } + } + // We've read everything it seems but didn't understand a word + purple_xfer_cancel_remote(xfer); + g_return_if_reached(); } static void jabber_hfu_http_send_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) { PurpleHttpURL *httpurl; - gchar *headers, *host, *path, *auth = NULL, *expire = NULL, *cookie = NULL; + g_autofree gchar *headers, *auth = NULL, *expire = NULL, *cookie = NULL; PurpleXfer *xfer = data; HFUXfer *hfux = purple_xfer_get_protocol_data(xfer); HFUJabberStreamData *js_data = hfux->js_data; + g_autofree char *filemime = file_get_mime(purple_xfer_get_local_filename(xfer)); httpurl = purple_http_url_parse(hfux->put_url); - path = purple_http_url_get_path(httpurl); if (str_equal(js_data->ns, NS_HTTP_FILE_UPLOAD_V0)) { char *a = g_hash_table_lookup(hfux->put_headers, "Authorisation"); @@ -86,35 +102,30 @@ static void jabber_hfu_http_send_connect_cb(gpointer data, PurpleSslConnection * if(e) expire = g_strdup_printf("Expires: %s\r\n", e); } - host = purple_http_url_get_host(httpurl); - headers = g_strdup_printf("PUT /%s HTTP/1.0\r\n" "Connection: close\r\n" "Host: %s\r\n" "Content-Length: %" G_GSIZE_FORMAT "\r\n" - "Content-Type: application/octet-stream\r\n" + "Content-Type: %s\r\n" "User-Agent: libpurple\r\n" "%s%s%s\r\n", - path, host, (gsize) purple_xfer_get_size(xfer), + purple_http_url_get_path(httpurl), + purple_http_url_get_host(httpurl), + (gsize) purple_xfer_get_size(xfer), + (filemime?:"application/octet-stream"), (auth?:""), (expire?:""), (cookie?:"")); - g_free(auth); - g_free(expire); - g_free(cookie); - - purple_ssl_write(ssl_connection, headers, strlen(headers)); - hfux->ssl_conn = ssl_connection; purple_ssl_input_add(ssl_connection, jabber_hfu_http_read, xfer); + purple_ssl_write(ssl_connection, headers, strlen(headers)); + purple_xfer_ref(xfer); purple_xfer_start(xfer, ssl_connection->fd, NULL, 0); purple_xfer_prpl_ready(xfer); - - g_free(headers); - + purple_http_url_free(httpurl); } static void jabber_hfu_http_error_connect_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType *error_type, gpointer data) @@ -129,13 +140,13 @@ static void jabber_hfu_request_cb(JabberStream *js, const char *from, PurpleAccount *account; xmlnode *slot, *put, *get, *header = NULL; PurpleHttpURL *put_httpurl; - gchar *put_host; PurpleXfer *xfer = data; HFUXfer *hfux = purple_xfer_get_protocol_data(xfer); HFUJabberStreamData *js_data = hfux->js_data; account = purple_connection_get_account(js->gc); + if(!(slot = xmlnode_get_child_with_namespace(packet, "slot", js_data->ns))) { purple_xfer_cancel_remote(xfer); @@ -151,7 +162,7 @@ static void jabber_hfu_request_cb(JabberStream *js, const char *from, for (header = xmlnode_get_child(put, "header") ; header; header = xmlnode_get_next_twin(header)) { - g_hash_table_insert(hfux->put_headers, g_strdup(xmlnode_get_attrib(header, "name")), g_strdup(xmlnode_get_data(header))); + g_hash_table_insert(hfux->put_headers, g_strdup(xmlnode_get_attrib(header, "name")), xmlnode_get_data(header)); } hfux->put_url = g_strdup(xmlnode_get_attrib(put, "url")); @@ -164,9 +175,10 @@ static void jabber_hfu_request_cb(JabberStream *js, const char *from, } put_httpurl = purple_http_url_parse(hfux->put_url); - put_host = purple_http_url_get_host(put_httpurl); - purple_ssl_connect(account, put_host, purple_http_url_get_port(put_httpurl), jabber_hfu_http_send_connect_cb, (PurpleSslErrorFunction)jabber_hfu_http_error_connect_cb, xfer); + g_debug("Connecting to %s:%d for %s", purple_http_url_get_host(put_httpurl), purple_http_url_get_port(put_httpurl), hfux->put_url); + purple_ssl_connect(account, purple_http_url_get_host(put_httpurl), purple_http_url_get_port(put_httpurl), + jabber_hfu_http_send_connect_cb, (PurpleSslErrorFunction)jabber_hfu_http_error_connect_cb, xfer); purple_http_url_free(put_httpurl); } @@ -177,15 +189,11 @@ static void jabber_hfu_xfer_free(PurpleXfer *xfer) g_return_if_fail(hfux != NULL); - if (hfux->put_url) - { - g_free(hfux->put_url); - } + g_free(hfux->put_url); + g_free(hfux->get_url); - if (hfux->get_url) - { - g_free(hfux->get_url); - } + if(hfux->put_headers) + g_hash_table_destroy(hfux->put_headers); if (hfux->ssl_conn) { @@ -261,7 +269,8 @@ jabber_hfu_xmlnode_send_cb(PurpleConnection *gc, xmlnode **packet, gpointer null if (g_strcmp0 ((*packet)->name, "message") == 0) { xmlnode *node_body = xmlnode_get_child (*packet, "body"); if (node_body) { - HFUXfer *hfux = g_hash_table_lookup(ht_hfu_sending, xmlnode_get_data(node_body)); + g_autofree char *url = xmlnode_get_data(node_body); + HFUXfer *hfux = g_hash_table_lookup(ht_hfu_sending, url); if(hfux) { xmlnode *x, *url; x = xmlnode_new_child (*packet, "x"); @@ -313,6 +322,7 @@ static void jabber_hfu_send_url_to_conv(PurpleXfer *xfer) static void jabber_hfu_xfer_end(PurpleXfer *xfer) { + g_debug("This is the end."); jabber_hfu_send_url_to_conv(xfer); jabber_hfu_xfer_free(xfer); @@ -336,21 +346,22 @@ static gssize jabber_hfu_xfer_write(const guchar *buffer, size_t len, PurpleXfer if (tlen == -1) { - if (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer)) - purple_xfer_set_completed(xfer, TRUE); - if ((errno != EAGAIN) && (errno != EINTR)) return -1; return 0; - } - - if ((purple_xfer_get_bytes_sent(xfer) + tlen) >= purple_xfer_get_size(xfer)) - purple_xfer_set_completed(xfer, TRUE); + } else if ((purple_xfer_get_bytes_sent(xfer)+tlen) >= purple_xfer_get_size(xfer)) + xfer->status = PURPLE_XFER_STATUS_DONE; // sneaky cheat return tlen; } +static void jabber_hfu_xfer_ack(PurpleXfer *xfer, const guchar *buffer, size_t len) +{ + if (purple_xfer_is_completed(xfer)) + xfer->status = PURPLE_XFER_STATUS_STARTED; // hideous uncheat +} + static void jabber_hfu_xfer_init(PurpleXfer *xfer) { HFUXfer *hfux = purple_xfer_get_protocol_data(xfer); @@ -386,7 +397,6 @@ static void jabber_hfu_xfer_init(PurpleXfer *xfer) } - PurpleXfer *jabber_hfu_new_xfer(PurpleConnection *gc, const char *who) { JabberStream *js; @@ -403,8 +413,9 @@ PurpleXfer *jabber_hfu_new_xfer(PurpleConnection *gc, const char *who) purple_xfer_set_init_fnc(xfer, jabber_hfu_xfer_init); purple_xfer_set_cancel_send_fnc(xfer, jabber_hfu_xfer_cancel_send); - purple_xfer_set_end_fnc(xfer, jabber_hfu_xfer_end); purple_xfer_set_write_fnc(xfer, jabber_hfu_xfer_write); + purple_xfer_set_ack_fnc(xfer, jabber_hfu_xfer_ack); + purple_xfer_set_end_fnc(xfer, jabber_hfu_xfer_end); return xfer; } @@ -498,6 +509,11 @@ static GList *jabber_hfu_blist_node_menu(PurpleBlistNode *node) gboolean plugin_unload(PurplePlugin *plugin) { + PurplePlugin *jabber_plugin = purple_plugins_find_with_id(JABBER_PLUGIN_ID); + + PurplePluginProtocolInfo *jabber_protocol_info = PURPLE_PLUGIN_PROTOCOL_INFO(jabber_plugin); + jabber_protocol_info->blist_node_menu = old_blist_node_menu; + purple_signals_disconnect_by_handle(plugin); g_hash_table_destroy(ht_hfu_sending); g_hash_table_destroy(HFUJabberStreamDataTable); -- GitLab