Implement XEP-0363: HTTP File Upload

This commit is contained in:
Dominik Heidler
2016-04-11 20:13:18 +02:00
parent 28e260c7da
commit 1b0ce852bb
24 changed files with 954 additions and 29 deletions

View File

@@ -64,6 +64,7 @@
#include "roster_list.h"
#include "xmpp/xmpp.h"
#include "plugins/plugins.h"
#include "tools/http_upload.h"
typedef struct p_room_info_data_t {
char *room;
@@ -88,6 +89,7 @@ static void _ping_get_handler(xmpp_stanza_t *const stanza);
static int _version_result_id_handler(xmpp_stanza_t *const stanza, void *const userdata);
static int _disco_info_response_id_handler(xmpp_stanza_t *const stanza, void *const userdata);
static int _disco_info_response_id_handler_onconnect(xmpp_stanza_t *const stanza, void *const userdata);
static int _http_upload_response_id_handler(xmpp_stanza_t *const stanza, void *const upload_ctx);
static int _last_activity_response_id_handler(xmpp_stanza_t *const stanza, void *const userdata);
static int _room_info_response_id_handler(xmpp_stanza_t *const stanza, void *const userdata);
static int _destroy_room_result_id_handler(xmpp_stanza_t *const stanza, void *const userdata);
@@ -293,6 +295,42 @@ iq_disable_carbons(void)
xmpp_stanza_release(iq);
}
void
iq_http_upload_request(HTTPUpload *upload)
{
GSList *disco_items = jabber_get_disco_items();
DiscoInfo *disco_info;
if (disco_items && (g_slist_length(disco_items) > 0)) {
while (disco_items) {
disco_info = disco_items->data;
if (g_hash_table_lookup_extended(disco_info->features, STANZA_NS_HTTP_UPLOAD, NULL, NULL)) {
break;
}
disco_items = g_slist_next(disco_items);
if (!disco_items) {
cons_show_error("XEP-0363 HTTP File Upload is not supported by the server");
return;
}
}
} else {
cons_show_error("No disco items");
return;
}
xmpp_ctx_t * const ctx = connection_get_ctx();
char *id = create_unique_id("http_upload_request");
xmpp_stanza_t *iq = stanza_create_http_upload_request(ctx, id, disco_info->item, upload);
id_handler_add(id, _http_upload_response_id_handler, upload);
free(id);
send_iq_stanza(iq);
xmpp_stanza_release(iq);
return;
}
void
iq_disco_info_request(gchar *jid)
{
@@ -1904,6 +1942,60 @@ _disco_info_response_id_handler_onconnect(xmpp_stanza_t *const stanza, void *con
return 0;
}
static int
_http_upload_response_id_handler(xmpp_stanza_t *const stanza, void *const userdata)
{
HTTPUpload *upload = (HTTPUpload *)userdata;
const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
const char *type = xmpp_stanza_get_type(stanza);
if (from) {
log_info("Received http_upload response from: %s", from);
} else {
log_info("Received http_upload response");
}
// handle error responses
if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
char *error_message = stanza_get_error_message(stanza);
if (from) {
cons_show_error("Uploading '%s' failed for %s: %s", upload->filename, from, error_message);
} else {
cons_show_error("Uploading '%s' failed: %s", upload->filename, error_message);
}
free(error_message);
return 0;
}
xmpp_stanza_t *slot = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SLOT);
if (slot && g_strcmp0(xmpp_stanza_get_ns(slot), STANZA_NS_HTTP_UPLOAD) == 0) {
xmpp_stanza_t *put = xmpp_stanza_get_child_by_name(slot, STANZA_NAME_PUT);
xmpp_stanza_t *get = xmpp_stanza_get_child_by_name(slot, STANZA_NAME_GET);
if (put && get) {
char *put_url = xmpp_stanza_get_text(put);
char *get_url = xmpp_stanza_get_text(get);
upload->put_url = strdup(put_url);
upload->get_url = strdup(get_url);
xmpp_conn_t *conn = connection_get_conn();
xmpp_ctx_t *ctx = xmpp_conn_get_context(conn);
if (put_url) xmpp_free(ctx, put_url);
if (get_url) xmpp_free(ctx, get_url);
pthread_create(&(upload->worker), NULL, &http_file_put, upload);
upload_processes = g_slist_append(upload_processes, upload);
} else {
log_error("Invalid XML in HTTP Upload slot");
return 1;
}
}
return 0;
}
static void
_disco_items_result_handler(xmpp_stanza_t *const stanza)
{

View File

@@ -163,7 +163,7 @@ _session_state(const char *const barejid)
}
char*
message_send_chat(const char *const barejid, const char *const msg)
message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url)
{
xmpp_ctx_t * const ctx = connection_get_ctx();
@@ -178,6 +178,10 @@ message_send_chat(const char *const barejid, const char *const msg)
stanza_attach_state(ctx, message, state);
}
if (oob_url) {
stanza_attach_x_oob_url(ctx, message, oob_url);
}
if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
stanza_attach_receipt_request(ctx, message);
}
@@ -274,25 +278,33 @@ message_send_chat_otr(const char *const barejid, const char *const msg)
}
void
message_send_private(const char *const fulljid, const char *const msg)
message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url)
{
xmpp_ctx_t * const ctx = connection_get_ctx();
char *id = create_unique_id("prv");
xmpp_stanza_t *message = stanza_create_message(ctx, id, fulljid, STANZA_TYPE_CHAT, msg);
free(id);
if (oob_url) {
stanza_attach_x_oob_url(ctx, message, oob_url);
}
_send_message_stanza(message);
xmpp_stanza_release(message);
}
void
message_send_groupchat(const char *const roomjid, const char *const msg)
message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url)
{
xmpp_ctx_t * const ctx = connection_get_ctx();
char *id = create_unique_id("muc");
xmpp_stanza_t *message = stanza_create_message(ctx, id, roomjid, STANZA_TYPE_GROUPCHAT, msg);
free(id);
if (oob_url) {
stanza_attach_x_oob_url(ctx, message, oob_url);
}
_send_message_stanza(message);
xmpp_stanza_release(message);
}

View File

@@ -32,10 +32,15 @@
*
*/
#define _GNU_SOURCE 1
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <libgen.h>
#include <inttypes.h>
#include <glib.h>
@@ -206,6 +211,61 @@ stanza_create_bookmarks_pubsub_add(xmpp_ctx_t *ctx, const char *const jid,
}
#endif
xmpp_stanza_t*
stanza_create_http_upload_request(xmpp_ctx_t *ctx, const char *const id,
const char *const jid, HTTPUpload *upload)
{
int i;
xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
xmpp_stanza_set_type(iq, STANZA_TYPE_GET);
xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, jid);
xmpp_stanza_set_id(iq, id);
xmpp_stanza_t *request = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(request, STANZA_NAME_REQUEST);
xmpp_stanza_set_ns(request, STANZA_NS_HTTP_UPLOAD);
xmpp_stanza_t *filename = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(filename, STANZA_NAME_FILENAME);
xmpp_stanza_t *filename_txt = xmpp_stanza_new(ctx);
char* filename_cpy = strdup(upload->filename);
// strip spaces from filename (servers don't spaces)
for (i=0; i<strlen(filename_cpy); i++) {
if (filename_cpy[i] == ' ') {
filename_cpy[i] = '_';
}
}
xmpp_stanza_set_text(filename_txt, basename(filename_cpy));
free(filename_cpy);
xmpp_stanza_add_child(filename, filename_txt);
xmpp_stanza_add_child(request, filename);
xmpp_stanza_t *size = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(size, STANZA_NAME_SIZE);
xmpp_stanza_t *size_txt = xmpp_stanza_new(ctx);
char* filesize = NULL;
if (asprintf(&filesize, "%jd", (intmax_t)(upload->filesize)) != -1) {
xmpp_stanza_set_text(size_txt, filesize);
free(filesize);
}
xmpp_stanza_add_child(size, size_txt);
xmpp_stanza_add_child(request, size);
xmpp_stanza_t *content_type = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(content_type, STANZA_NAME_CONTENT_TYPE);
xmpp_stanza_t *content_type_txt = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(content_type_txt, upload->mime_type);
xmpp_stanza_add_child(content_type, content_type_txt);
xmpp_stanza_add_child(request, content_type);
xmpp_stanza_add_child(iq, request);
xmpp_stanza_release(request);
return iq;
}
xmpp_stanza_t*
stanza_enable_carbons(xmpp_ctx_t *ctx)
{
@@ -353,6 +413,30 @@ stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
return stanza;
}
xmpp_stanza_t*
stanza_attach_x_oob_url(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const url)
{
xmpp_stanza_t *x_oob = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(x_oob, STANZA_NAME_X);
xmpp_stanza_set_ns(x_oob, STANZA_NS_X_OOB);
xmpp_stanza_t *surl = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(surl, STANZA_NAME_URL);
xmpp_stanza_t *surl_txt = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(surl_txt, url);
xmpp_stanza_add_child(surl, surl_txt);
xmpp_stanza_release(surl_txt);
xmpp_stanza_add_child(x_oob, surl);
xmpp_stanza_release(surl);
xmpp_stanza_add_child(stanza, x_oob);
xmpp_stanza_release(x_oob);
return stanza;
}
xmpp_stanza_t*
stanza_create_message(xmpp_ctx_t *ctx, char *id, const char *const recipient,
const char *const type, const char *const message)

View File

@@ -61,6 +61,7 @@
#define STANZA_NAME_STATUS "status"
#define STANZA_NAME_IQ "iq"
#define STANZA_NAME_QUERY "query"
#define STANZA_NAME_REQUEST "request"
#define STANZA_NAME_DELAY "delay"
#define STANZA_NAME_ERROR "error"
#define STANZA_NAME_PING "ping"
@@ -87,6 +88,13 @@
#define STANZA_NAME_ACTOR "actor"
#define STANZA_NAME_ENABLE "enable"
#define STANZA_NAME_DISABLE "disable"
#define STANZA_NAME_FILENAME "filename"
#define STANZA_NAME_SIZE "size"
#define STANZA_NAME_CONTENT_TYPE "content-type"
#define STANZA_NAME_SLOT "slot"
#define STANZA_NAME_PUT "put"
#define STANZA_NAME_GET "get"
#define STANZA_NAME_URL "url"
// error conditions
#define STANZA_NAME_BAD_REQUEST "bad-request"
@@ -171,6 +179,8 @@
#define STANZA_NS_RECEIPTS "urn:xmpp:receipts"
#define STANZA_NS_SIGNED "jabber:x:signed"
#define STANZA_NS_ENCRYPTED "jabber:x:encrypted"
#define STANZA_NS_HTTP_UPLOAD "urn:xmpp:http:upload"
#define STANZA_NS_X_OOB "jabber:x:oob"
#define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
@@ -195,6 +205,8 @@ typedef enum {
xmpp_stanza_t* stanza_create_bookmarks_storage_request(xmpp_ctx_t *ctx);
xmpp_stanza_t* stanza_create_http_upload_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, HTTPUpload *upload);
xmpp_stanza_t* stanza_enable_carbons(xmpp_ctx_t *ctx);
xmpp_stanza_t* stanza_disable_carbons(xmpp_ctx_t *ctx);
@@ -207,6 +219,7 @@ xmpp_stanza_t* stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *sta
xmpp_stanza_t* stanza_attach_hints_no_copy(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
xmpp_stanza_t* stanza_attach_hints_no_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
xmpp_stanza_t* stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
xmpp_stanza_t* stanza_attach_x_oob_url(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const url);
xmpp_stanza_t* stanza_create_message(xmpp_ctx_t *ctx, char *id,
const char *const recipient, const char *const type, const char *const message);

View File

@@ -49,6 +49,7 @@
#include "contact.h"
#include "jid.h"
#include "tools/autocomplete.h"
#include "tools/http_upload.h"
#define JABBER_PRIORITY_MIN -128
#define JABBER_PRIORITY_MAX 127
@@ -169,11 +170,11 @@ gboolean jabber_conn_is_secured(void);
gboolean jabber_send_stanza(const char *const stanza);
// message functions
char* message_send_chat(const char *const barejid, const char *const msg);
char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url);
char* message_send_chat_otr(const char *const barejid, const char *const msg);
char* message_send_chat_pgp(const char *const barejid, const char *const msg);
void message_send_private(const char *const fulljid, const char *const msg);
void message_send_groupchat(const char *const roomjid, const char *const msg);
void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url);
void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url);
void message_send_groupchat_subject(const char *const roomjid, const char *const subject);
void message_send_inactive(const char *const jid);
@@ -225,6 +226,7 @@ void iq_room_kick_occupant(const char *const room, const char *const nick, const
void iq_room_role_set(const char *const room, const char *const nick, char *role, const char *const reason);
void iq_room_role_list(const char * const room, char *role);
void iq_autoping_check(void);
void iq_http_upload_request(HTTPUpload *upload);
// caps functions
Capabilities* caps_lookup(const char *const jid);