mirror of
https://github.com/profanity-im/profanity.git
synced 2024-11-03 19:37:16 -05:00
Implement XEP-0363: HTTP File Upload
This commit is contained in:
parent
28e260c7da
commit
1b0ce852bb
@ -32,6 +32,8 @@ core_sources = \
|
||||
src/command/commands.h src/command/commands.c \
|
||||
src/tools/parser.c \
|
||||
src/tools/parser.h \
|
||||
src/tools/http_upload.c \
|
||||
src/tools/http_upload.h \
|
||||
src/tools/p_sha1.h src/tools/p_sha1.c \
|
||||
src/tools/autocomplete.c src/tools/autocomplete.h \
|
||||
src/tools/tinyurl.c src/tools/tinyurl.h \
|
||||
@ -90,6 +92,7 @@ unittest_sources = \
|
||||
tests/unittests/ui/stub_ui.c \
|
||||
tests/unittests/log/stub_log.c \
|
||||
tests/unittests/config/stub_accounts.c \
|
||||
tests/unittests/tools/stub_http_upload.c \
|
||||
tests/unittests/helpers.c tests/unittests/helpers.h \
|
||||
tests/unittests/test_form.c tests/unittests/test_form.h \
|
||||
tests/unittests/test_common.c tests/unittests/test_common.h \
|
||||
|
@ -116,6 +116,12 @@ else
|
||||
AM_CONDITIONAL([BUILD_C_API], [false])
|
||||
fi
|
||||
|
||||
# threading
|
||||
ACX_PTHREAD
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
CC="$PTHREAD_CC"
|
||||
|
||||
### Check for libmesode, fall back to libstrophe
|
||||
PKG_CHECK_MODULES([libmesode], [libmesode],
|
||||
[LIBS="$libmesode_LIBS $LIBS" CFLAGS="$CFLAGS $libmesode_CFLAGS" AC_DEFINE([HAVE_LIBMESODE], [1], [libmesode])],
|
||||
|
@ -42,6 +42,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@ -120,6 +124,7 @@ static char* _console_autocomplete(ProfWin *window, const char *const input);
|
||||
static char* _win_autocomplete(ProfWin *window, const char *const input);
|
||||
static char* _close_autocomplete(ProfWin *window, const char *const input);
|
||||
static char* _plugins_autocomplete(ProfWin *window, const char *const input);
|
||||
static char* _sendfile_autocomplete(ProfWin *window, const char *const input);
|
||||
|
||||
GHashTable *commands = NULL;
|
||||
|
||||
@ -795,6 +800,22 @@ static struct cmd_t command_defs[] =
|
||||
"/disco info myfriend@server.com/laptop")
|
||||
},
|
||||
|
||||
{ "/sendfile",
|
||||
cmd_sendfile, parse_args_with_freetext, 1, 1, NULL,
|
||||
CMD_TAGS(
|
||||
CMD_TAG_CHAT,
|
||||
CMD_TAG_GROUPCHAT)
|
||||
CMD_SYN(
|
||||
"/sendfile <file>")
|
||||
CMD_DESC(
|
||||
"Send a file using XEP-0363 HTTP file transfer.")
|
||||
CMD_ARGS(
|
||||
{ "<file>", "Path to the file." })
|
||||
CMD_EXAMPLES(
|
||||
"/sendfile /etc/hosts",
|
||||
"/sendfile ~/images/sweet_cat.jpg")
|
||||
},
|
||||
|
||||
{ "/lastactivity",
|
||||
cmd_lastactivity, parse_args, 0, 1, NULL,
|
||||
CMD_TAGS(
|
||||
@ -2043,6 +2064,7 @@ static Autocomplete console_msg_ac;
|
||||
static Autocomplete autoping_ac;
|
||||
static Autocomplete plugins_ac;
|
||||
static Autocomplete plugins_load_ac;
|
||||
static Autocomplete sendfile_ac;
|
||||
|
||||
/*
|
||||
* Initialise command autocompleter and history
|
||||
@ -2597,6 +2619,8 @@ cmd_init(void)
|
||||
|
||||
plugins_ac = autocomplete_new();
|
||||
autocomplete_add(plugins_ac, "load");
|
||||
|
||||
sendfile_ac = autocomplete_new();
|
||||
}
|
||||
|
||||
void
|
||||
@ -2686,6 +2710,7 @@ cmd_uninit(void)
|
||||
autocomplete_free(autoping_ac);
|
||||
autocomplete_free(plugins_ac);
|
||||
autocomplete_free(plugins_load_ac);
|
||||
autocomplete_free(sendfile_ac);
|
||||
}
|
||||
|
||||
gboolean
|
||||
@ -2834,6 +2859,7 @@ cmd_reset_autocomplete(ProfWin *window)
|
||||
autocomplete_reset(notify_mention_ac);
|
||||
autocomplete_reset(notify_trigger_ac);
|
||||
autocomplete_reset(sub_ac);
|
||||
autocomplete_reset(sendfile_ac);
|
||||
|
||||
autocomplete_reset(who_room_ac);
|
||||
autocomplete_reset(who_roster_ac);
|
||||
@ -3191,6 +3217,7 @@ _cmd_complete_parameters(ProfWin *window, const char *const input)
|
||||
g_hash_table_insert(ac_funcs, "/win", _win_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/close", _close_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/plugins", _plugins_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/sendfile", _sendfile_autocomplete);
|
||||
|
||||
int len = strlen(input);
|
||||
char parsed[len+1];
|
||||
@ -4589,6 +4616,110 @@ _close_autocomplete(ProfWin *window, const char *const input)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char*
|
||||
_sendfile_autocomplete(ProfWin *window, const char *const input)
|
||||
{
|
||||
static char* last_directory = NULL;
|
||||
|
||||
unsigned int output_off = 0;
|
||||
|
||||
char *result = NULL;
|
||||
char *tmp;
|
||||
|
||||
// strip command
|
||||
char *inpcp = (char*)input + 9;
|
||||
while (*inpcp == ' ') {
|
||||
inpcp++;
|
||||
}
|
||||
|
||||
inpcp = strdup(inpcp);
|
||||
|
||||
// strip quotes
|
||||
if (*inpcp == '"') {
|
||||
tmp = strchr(inpcp+1, '"');
|
||||
if (tmp) {
|
||||
*tmp = '\0';
|
||||
}
|
||||
tmp = strdup(inpcp+1);
|
||||
free(inpcp);
|
||||
inpcp = tmp;
|
||||
}
|
||||
|
||||
// expand ~ to $HOME
|
||||
if (inpcp[0] == '~' && inpcp[1] == '/') {
|
||||
if (asprintf(&tmp, "%s/%sfoo", getenv("HOME"), inpcp+2) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
output_off = strlen(getenv("HOME"))+1;
|
||||
} else {
|
||||
if (asprintf(&tmp, "%sfoo", inpcp) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
free(inpcp);
|
||||
inpcp = tmp;
|
||||
|
||||
char* inpcp2 = strdup(inpcp);
|
||||
char* foofile = strdup(basename(inpcp2));
|
||||
char* directory = strdup(dirname(inpcp));
|
||||
free(inpcp);
|
||||
free(inpcp2);
|
||||
|
||||
if (!last_directory || strcmp(last_directory, directory) != 0) {
|
||||
free(last_directory);
|
||||
last_directory = directory;
|
||||
autocomplete_reset(sendfile_ac);
|
||||
|
||||
struct dirent *dir;
|
||||
|
||||
DIR *d = opendir(directory);
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (strcmp(dir->d_name, ".") == 0) {
|
||||
continue;
|
||||
} else if (strcmp(dir->d_name, "..") == 0) {
|
||||
continue;
|
||||
} else if (*(dir->d_name) == '.' && *foofile != '.') {
|
||||
// only show hidden files on explicit request
|
||||
continue;
|
||||
}
|
||||
char * acstring;
|
||||
if (output_off) {
|
||||
if (asprintf(&tmp, "%s/%s", directory, dir->d_name) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
if (asprintf(&acstring, "~/%s", tmp+output_off) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
free(tmp);
|
||||
} else if (strcmp(directory, "/") == 0) {
|
||||
if (asprintf(&acstring, "/%s", dir->d_name) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (asprintf(&acstring, "%s/%s", directory, dir->d_name) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
autocomplete_add(sendfile_ac, acstring);
|
||||
free(acstring);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
} else {
|
||||
free(foofile);
|
||||
free(directory);
|
||||
}
|
||||
|
||||
result = autocomplete_param_with_ac(input, "/sendfile", sendfile_ac, TRUE);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
_subject_autocomplete(ProfWin *window, const char *const input)
|
||||
{
|
||||
|
@ -32,10 +32,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
@ -80,6 +83,7 @@
|
||||
#ifdef HAVE_GTK
|
||||
#include "tray.h"
|
||||
#endif
|
||||
#include "tools/http_upload.h"
|
||||
|
||||
static void _update_presence(const resource_presence_t presence,
|
||||
const char *const show, gchar **args);
|
||||
@ -128,21 +132,21 @@ cmd_execute_default(ProfWin *window, const char *inp)
|
||||
{
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
cl_ev_send_msg(chatwin, inp);
|
||||
cl_ev_send_msg(chatwin, inp, NULL);
|
||||
break;
|
||||
}
|
||||
case WIN_PRIVATE:
|
||||
{
|
||||
ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
|
||||
assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
|
||||
cl_ev_send_priv_msg(privatewin, inp);
|
||||
cl_ev_send_priv_msg(privatewin, inp, NULL);
|
||||
break;
|
||||
}
|
||||
case WIN_MUC:
|
||||
{
|
||||
ProfMucWin *mucwin = (ProfMucWin*)window;
|
||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
||||
cl_ev_send_muc_msg(mucwin, inp);
|
||||
cl_ev_send_muc_msg(mucwin, inp, NULL);
|
||||
break;
|
||||
}
|
||||
case WIN_XML:
|
||||
@ -1903,7 +1907,7 @@ cmd_msg(ProfWin *window, const char *const command, gchar **args)
|
||||
ui_focus_win((ProfWin*)privwin);
|
||||
|
||||
if (msg) {
|
||||
cl_ev_send_priv_msg(privwin, msg);
|
||||
cl_ev_send_priv_msg(privwin, msg, NULL);
|
||||
}
|
||||
|
||||
g_string_free(full_jid, TRUE);
|
||||
@ -1928,7 +1932,7 @@ cmd_msg(ProfWin *window, const char *const command, gchar **args)
|
||||
ui_focus_win((ProfWin*)chatwin);
|
||||
|
||||
if (msg) {
|
||||
cl_ev_send_msg(chatwin, msg);
|
||||
cl_ev_send_msg(chatwin, msg, NULL);
|
||||
} else {
|
||||
#ifdef HAVE_LIBOTR
|
||||
if (otr_is_secure(barejid)) {
|
||||
@ -4224,6 +4228,57 @@ cmd_disco(ProfWin *window, const char *const command, gchar **args)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_sendfile(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
jabber_conn_status_t conn_status = jabber_get_connection_status();
|
||||
char *filename = args[0];
|
||||
|
||||
// expand ~ to $HOME
|
||||
if (filename[0] == '~' && filename[1] == '/') {
|
||||
if (asprintf(&filename, "%s/%s", getenv("HOME"), filename+2) == -1) {
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
filename = strdup(filename);
|
||||
}
|
||||
|
||||
if (conn_status != JABBER_CONNECTED) {
|
||||
cons_show("You are not currently connected.");
|
||||
free(filename);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (window->type != WIN_CHAT && window->type != WIN_PRIVATE && window->type != WIN_MUC) {
|
||||
cons_show_error("Unsupported window for file transmission.");
|
||||
free(filename);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (access(filename, R_OK) != 0) {
|
||||
cons_show_error("Uploading '%s' failed: File not found!", filename);
|
||||
free(filename);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!is_regular_file(filename)) {
|
||||
cons_show_error("Uploading '%s' failed: Not a file!", filename);
|
||||
free(filename);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HTTPUpload *upload = malloc(sizeof(HTTPUpload));
|
||||
upload->window = window;
|
||||
|
||||
upload->filename = filename;
|
||||
upload->filesize = file_size(filename);
|
||||
upload->mime_type = file_mime_type(filename);
|
||||
|
||||
iq_http_upload_request(upload);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_lastactivity(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
@ -4375,21 +4430,21 @@ cmd_tiny(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
cl_ev_send_msg(chatwin, tiny);
|
||||
cl_ev_send_msg(chatwin, tiny, NULL);
|
||||
break;
|
||||
}
|
||||
case WIN_PRIVATE:
|
||||
{
|
||||
ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
|
||||
assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
|
||||
cl_ev_send_priv_msg(privatewin, tiny);
|
||||
cl_ev_send_priv_msg(privatewin, tiny, NULL);
|
||||
break;
|
||||
}
|
||||
case WIN_MUC:
|
||||
{
|
||||
ProfMucWin *mucwin = (ProfMucWin*)window;
|
||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
||||
cl_ev_send_muc_msg(mucwin, tiny);
|
||||
cl_ev_send_muc_msg(mucwin, tiny, NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -85,6 +85,7 @@ gboolean cmd_connect(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_tls(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_decline(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_disco(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_sendfile(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_lastactivity(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_disconnect(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_dnd(ProfWin *window, const char *const command, gchar **args);
|
||||
|
@ -106,7 +106,7 @@ cl_ev_presence_send(const resource_presence_t presence_type, const char *const m
|
||||
}
|
||||
|
||||
void
|
||||
cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg)
|
||||
cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oob_url)
|
||||
{
|
||||
chat_state_active(chatwin->state);
|
||||
char *plugin_msg = plugins_pre_chat_message_send(chatwin->barejid, msg);
|
||||
@ -122,7 +122,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg)
|
||||
} else {
|
||||
gboolean handled = otr_on_message_send(chatwin, plugin_msg);
|
||||
if (!handled) {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg);
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN);
|
||||
free(id);
|
||||
@ -140,7 +140,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg)
|
||||
#ifndef HAVE_LIBGPGME
|
||||
gboolean handled = otr_on_message_send(chatwin, plugin_msg);
|
||||
if (!handled) {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg);
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN);
|
||||
free(id);
|
||||
@ -161,7 +161,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg)
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP);
|
||||
free(id);
|
||||
} else {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg);
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN);
|
||||
free(id);
|
||||
@ -176,7 +176,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg)
|
||||
// OTR unsupported, PGP unsupported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg);
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN);
|
||||
free(id);
|
||||
@ -189,18 +189,18 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg)
|
||||
}
|
||||
|
||||
void
|
||||
cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg)
|
||||
cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const oob_url)
|
||||
{
|
||||
char *plugin_msg = plugins_pre_room_message_send(mucwin->roomjid, msg);
|
||||
|
||||
message_send_groupchat(mucwin->roomjid, plugin_msg);
|
||||
message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url);
|
||||
|
||||
plugins_post_room_message_send(mucwin->roomjid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
}
|
||||
|
||||
void
|
||||
cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg)
|
||||
cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg, const char *const oob_url)
|
||||
{
|
||||
if (privwin->occupant_offline) {
|
||||
privwin_message_occupant_offline(privwin);
|
||||
@ -209,7 +209,7 @@ cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg)
|
||||
} else {
|
||||
char *plugin_msg = plugins_pre_priv_message_send(privwin->fulljid, msg);
|
||||
|
||||
message_send_private(privwin->fulljid, plugin_msg);
|
||||
message_send_private(privwin->fulljid, plugin_msg, oob_url);
|
||||
privwin_outgoing_msg(privwin, plugin_msg);
|
||||
|
||||
plugins_post_priv_message_send(privwin->fulljid, plugin_msg);
|
||||
|
@ -42,8 +42,8 @@ void cl_ev_disconnect(void);
|
||||
|
||||
void cl_ev_presence_send(const resource_presence_t presence_type, const char *const msg, const int idle_secs);
|
||||
|
||||
void cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg);
|
||||
void cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg);
|
||||
void cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg);
|
||||
void cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oob_url);
|
||||
void cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const oob_url);
|
||||
void cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg, const char *const oob_url);
|
||||
|
||||
#endif
|
||||
|
@ -323,6 +323,11 @@ _init(char *log_level)
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGWINCH, ui_sigwinch_handler);
|
||||
if (pthread_mutex_init(&lock, NULL) != 0) {
|
||||
log_error("Mutex init failed");
|
||||
exit(1);
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
_create_directories();
|
||||
log_level_t prof_log_level = log_level_from_string(log_level);
|
||||
prefs_load();
|
||||
|
@ -35,6 +35,8 @@
|
||||
#ifndef PROFANITY_H
|
||||
#define PROFANITY_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "resource.h"
|
||||
#include "xmpp/xmpp.h"
|
||||
|
||||
@ -46,4 +48,6 @@ gboolean prof_process_input(char *inp);
|
||||
|
||||
void prof_set_quit(void);
|
||||
|
||||
pthread_mutex_t lock;
|
||||
|
||||
#endif
|
||||
|
337
src/tools/http_upload.c
Normal file
337
src/tools/http_upload.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* http_upload.c
|
||||
*
|
||||
* Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
|
||||
*
|
||||
* This file is part of Profanity.
|
||||
*
|
||||
* Profanity is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Profanity is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Profanity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link the code of portions of this program with the OpenSSL library under
|
||||
* certain conditions as described in each individual source file, and
|
||||
* distribute linked combinations including the two.
|
||||
*
|
||||
* You must obey the GNU General Public License in all respects for all of the
|
||||
* code used other than OpenSSL. If you modify file(s) with this exception, you
|
||||
* may extend this exception to your version of the file(s), but you are not
|
||||
* obligated to do so. If you do not wish to do so, delete this exception
|
||||
* statement from your version. If you delete this exception statement from all
|
||||
* source files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <curl/curl.h>
|
||||
#include <gio/gio.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "profanity.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/window.h"
|
||||
#include "tools/http_upload.h"
|
||||
#include "event/client_events.h"
|
||||
#include "config/preferences.h"
|
||||
|
||||
#define FALLBACK_MIMETYPE "application/octet-stream"
|
||||
#define FALLBACK_MSG ""
|
||||
#define FILE_HEADER_BYTES 512
|
||||
|
||||
struct curl_data_t {
|
||||
char *buffer;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
_xferinfo(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
HTTPUpload *upload = (HTTPUpload *)userdata;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
if (upload->cancel) {
|
||||
pthread_mutex_unlock(&lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (upload->bytes_sent == ulnow) {
|
||||
pthread_mutex_unlock(&lock);
|
||||
return 0;
|
||||
} else {
|
||||
upload->bytes_sent = ulnow;
|
||||
}
|
||||
|
||||
unsigned int ulperc = 0;
|
||||
if (ultotal != 0) {
|
||||
ulperc = (100 * ulnow) / ultotal;
|
||||
}
|
||||
|
||||
char *msg;
|
||||
if (asprintf(&msg, "Uploading '%s': %d%%", upload->filename, ulperc) == -1) {
|
||||
msg = FALLBACK_MSG;
|
||||
}
|
||||
win_update_entry_message(upload->window, upload->put_url, msg);
|
||||
free(msg);
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LIBCURL_VERSION_NUM < 0x072000
|
||||
static int
|
||||
_older_progress(void *p, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
return _xferinfo(p, (curl_off_t)dltotal, (curl_off_t)dlnow, (curl_off_t)ultotal, (curl_off_t)ulnow);
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t
|
||||
_data_callback(void *ptr, size_t size, size_t nmemb, void *data)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
struct curl_data_t *mem = (struct curl_data_t *) data;
|
||||
mem->buffer = realloc(mem->buffer, mem->size + realsize + 1);
|
||||
|
||||
if (mem->buffer)
|
||||
{
|
||||
memcpy( &( mem->buffer[ mem->size ] ), ptr, realsize );
|
||||
mem->size += realsize;
|
||||
mem->buffer[ mem->size ] = 0;
|
||||
}
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
void *
|
||||
http_file_put(void *userdata)
|
||||
{
|
||||
HTTPUpload *upload = (HTTPUpload *)userdata;
|
||||
|
||||
FILE *fd = NULL;
|
||||
|
||||
char *err = NULL;
|
||||
char *content_type_header;
|
||||
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
|
||||
upload->cancel = 0;
|
||||
upload->bytes_sent = 0;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
char* msg;
|
||||
if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) {
|
||||
msg = FALLBACK_MSG;
|
||||
}
|
||||
win_print_with_receipt(upload->window, '!', 0, NULL, 0, THEME_TEXT_ME, NULL, msg, upload->put_url);
|
||||
free(msg);
|
||||
|
||||
char *cert_path = prefs_get_string(PREF_TLS_CERTPATH);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl = curl_easy_init();
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, upload->put_url);
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
|
||||
|
||||
struct curl_slist *headers = NULL;
|
||||
if (asprintf(&content_type_header, "Content-Type: %s", upload->mime_type) == -1) {
|
||||
msg = FALLBACK_MSG;
|
||||
}
|
||||
headers = curl_slist_append(headers, content_type_header);
|
||||
headers = curl_slist_append(headers, "Expect:");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x072000
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, _xferinfo);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, upload);
|
||||
#else
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, _older_progress);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, upload);
|
||||
#endif
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
||||
|
||||
struct curl_data_t output;
|
||||
output.buffer = NULL;
|
||||
output.size = 0;
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _data_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&output);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
|
||||
|
||||
if (!(fd = fopen(upload->filename, "rb"))) {
|
||||
if (asprintf(&err, "failed to open '%s'", upload->filename) == -1) {
|
||||
err = NULL;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (cert_path) {
|
||||
curl_easy_setopt(curl, CURLOPT_CAPATH, cert_path);
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_READDATA, fd);
|
||||
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(upload->filesize));
|
||||
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
||||
|
||||
if ((res = curl_easy_perform(curl)) != CURLE_OK) {
|
||||
err = strdup(curl_easy_strerror(res));
|
||||
} else {
|
||||
long http_code = 0;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||
|
||||
if (output.buffer) {
|
||||
output.buffer[output.size++] = '\0';
|
||||
}
|
||||
|
||||
// XEP-0363 specifies 201 but prosody returns 200
|
||||
if (http_code != 200 && http_code != 201) {
|
||||
if (asprintf(&err, "Server returned %lu", http_code) == -1) {
|
||||
err = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("HTTP Status: %lu\n", http_code);
|
||||
printf("%s\n", output.buffer);
|
||||
printf("%lu bytes retrieved\n", (long)output.size);
|
||||
#endif
|
||||
}
|
||||
|
||||
end:
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
curl_slist_free_all(headers);
|
||||
if (fd) {
|
||||
fclose(fd);
|
||||
}
|
||||
free(content_type_header);
|
||||
free(output.buffer);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
prefs_free_string(cert_path);
|
||||
|
||||
if (err) {
|
||||
char *msg;
|
||||
if (upload->cancel) {
|
||||
if (asprintf(&msg, "Uploading '%s' failed: Upload was canceled", upload->filename) == -1) {
|
||||
msg = FALLBACK_MSG;
|
||||
}
|
||||
} else {
|
||||
if (asprintf(&msg, "Uploading '%s' failed: %s", upload->filename, err) == -1) {
|
||||
msg = FALLBACK_MSG;
|
||||
}
|
||||
win_update_entry_message(upload->window, upload->put_url, msg);
|
||||
}
|
||||
cons_show_error(msg);
|
||||
free(msg);
|
||||
free(err);
|
||||
} else {
|
||||
if (!upload->cancel) {
|
||||
if (asprintf(&msg, "Uploading '%s': 100%%", upload->filename) == -1) {
|
||||
msg = FALLBACK_MSG;
|
||||
}
|
||||
win_update_entry_message(upload->window, upload->put_url, msg);
|
||||
//win_update_entry_theme(upload->window, upload->put_url, THEME_THEM);
|
||||
win_mark_received(upload->window, upload->put_url);
|
||||
free(msg);
|
||||
|
||||
switch (upload->window->type) {
|
||||
case WIN_CHAT:
|
||||
{
|
||||
ProfChatWin *chatwin = (ProfChatWin*)(upload->window);
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
cl_ev_send_msg(chatwin, upload->get_url, upload->get_url);
|
||||
break;
|
||||
}
|
||||
case WIN_PRIVATE:
|
||||
{
|
||||
ProfPrivateWin *privatewin = (ProfPrivateWin*)(upload->window);
|
||||
assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
|
||||
cl_ev_send_priv_msg(privatewin, upload->get_url, upload->get_url);
|
||||
break;
|
||||
}
|
||||
case WIN_MUC:
|
||||
{
|
||||
ProfMucWin *mucwin = (ProfMucWin*)(upload->window);
|
||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
||||
cl_ev_send_muc_msg(mucwin, upload->get_url, upload->get_url);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
upload_processes = g_slist_remove(upload_processes, upload);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
free(upload->filename);
|
||||
free(upload->mime_type);
|
||||
free(upload->get_url);
|
||||
free(upload->put_url);
|
||||
free(upload);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char*
|
||||
file_mime_type(const char* const file_name)
|
||||
{
|
||||
char *out_mime_type;
|
||||
char file_header[FILE_HEADER_BYTES];
|
||||
FILE *fd;
|
||||
if (!(fd = fopen(file_name, "rb"))) {
|
||||
return strdup(FALLBACK_MIMETYPE);
|
||||
}
|
||||
size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fd);
|
||||
fclose(fd);
|
||||
|
||||
char *content_type = g_content_type_guess(file_name, (unsigned char*)file_header, file_header_size, NULL);
|
||||
if (content_type != NULL) {
|
||||
char *mime_type = g_content_type_get_mime_type(content_type);
|
||||
out_mime_type = strdup(mime_type);
|
||||
g_free(mime_type);
|
||||
}
|
||||
else {
|
||||
return strdup(FALLBACK_MIMETYPE);
|
||||
}
|
||||
g_free(content_type);
|
||||
return out_mime_type;
|
||||
}
|
||||
|
||||
off_t file_size(const char* const filename)
|
||||
{
|
||||
struct stat st;
|
||||
stat(filename, &st);
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
int is_regular_file(const char *filename)
|
||||
{
|
||||
struct stat st;
|
||||
stat(filename, &st);
|
||||
return S_ISREG(st.st_mode);
|
||||
}
|
63
src/tools/http_upload.h
Normal file
63
src/tools/http_upload.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* http_upload.h
|
||||
*
|
||||
* Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
|
||||
*
|
||||
* This file is part of Profanity.
|
||||
*
|
||||
* Profanity is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Profanity is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Profanity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link the code of portions of this program with the OpenSSL library under
|
||||
* certain conditions as described in each individual source file, and
|
||||
* distribute linked combinations including the two.
|
||||
*
|
||||
* You must obey the GNU General Public License in all respects for all of the
|
||||
* code used other than OpenSSL. If you modify file(s) with this exception, you
|
||||
* may extend this exception to your version of the file(s), but you are not
|
||||
* obligated to do so. If you do not wish to do so, delete this exception
|
||||
* statement from your version. If you delete this exception statement from all
|
||||
* source files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOOLS_HTTP_UPLOAD_H
|
||||
#define TOOLS_HTTP_UPLOAD_H
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
// forward -> ui/win_types.h
|
||||
typedef struct prof_win_t ProfWin;
|
||||
|
||||
typedef struct http_upload_t {
|
||||
char *filename;
|
||||
off_t filesize;
|
||||
curl_off_t bytes_sent;
|
||||
char *mime_type;
|
||||
char *get_url;
|
||||
char *put_url;
|
||||
ProfWin *window;
|
||||
pthread_t worker;
|
||||
int cancel;
|
||||
} HTTPUpload;
|
||||
|
||||
GSList *upload_processes;
|
||||
|
||||
void* http_file_put(void *userdata);
|
||||
|
||||
char* file_mime_type(const char* const file_name);
|
||||
off_t file_size(const char* const file_name);
|
||||
int is_regular_file(const char *filename);
|
||||
|
||||
#endif
|
@ -125,6 +125,21 @@ buffer_yield_entry(ProfBuff buffer, int entry)
|
||||
return node->data;
|
||||
}
|
||||
|
||||
ProfBuffEntry*
|
||||
buffer_yield_entry_by_id(ProfBuff buffer, const char *const id)
|
||||
{
|
||||
GSList *entries = buffer->entries;
|
||||
while (entries) {
|
||||
ProfBuffEntry *entry = entries->data;
|
||||
if (entry->receipt && g_strcmp0(entry->receipt->id, id) == 0) {
|
||||
return entry;
|
||||
}
|
||||
entries = g_slist_next(entries);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
_free_entry(ProfBuffEntry *entry)
|
||||
{
|
||||
|
@ -64,6 +64,7 @@ void buffer_push(ProfBuff buffer, const char show_char, int pad_indent, GDateTim
|
||||
const char *const from, const char *const message, DeliveryReceipt *receipt);
|
||||
int buffer_size(ProfBuff buffer);
|
||||
ProfBuffEntry* buffer_yield_entry(ProfBuff buffer, int entry);
|
||||
ProfBuffEntry* buffer_yield_entry_by_id(ProfBuff buffer, const char *const id);
|
||||
gboolean buffer_mark_received(ProfBuff buffer, const char *const id);
|
||||
|
||||
#endif
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <wchar.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
@ -145,7 +146,9 @@ inp_readline(void)
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fileno(rl_instream), &fds);
|
||||
errno = 0;
|
||||
pthread_mutex_unlock(&lock);
|
||||
r = select(FD_SETSIZE, &fds, NULL, NULL, &p_rl_timeout);
|
||||
pthread_mutex_lock(&lock);
|
||||
if (r < 0) {
|
||||
if (errno != EINTR) {
|
||||
char *err_msg = strerror(errno);
|
||||
|
@ -1054,6 +1054,27 @@ win_mark_received(ProfWin *window, const char *const id)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win_update_entry_message(ProfWin *window, const char *const id, const char *const message)
|
||||
{
|
||||
ProfBuffEntry *entry = buffer_yield_entry_by_id(window->layout->buffer, id);
|
||||
if (entry) {
|
||||
free(entry->message);
|
||||
entry->message = strdup(message);
|
||||
win_redraw(window);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win_update_entry_theme(ProfWin *window, const char *const id, theme_item_t theme_item)
|
||||
{
|
||||
ProfBuffEntry *entry = buffer_yield_entry_by_id(window->layout->buffer, id);
|
||||
if (entry) {
|
||||
entry->theme_item = theme_item;
|
||||
win_redraw(window);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
win_println(ProfWin *window, int pad, const char *const message)
|
||||
{
|
||||
|
@ -70,6 +70,8 @@ int win_occpuants_cols(void);
|
||||
void win_sub_print(WINDOW *win, char *msg, gboolean newline, gboolean wrap, int indent);
|
||||
void win_sub_newline_lazy(WINDOW *win);
|
||||
void win_mark_received(ProfWin *window, const char *const id);
|
||||
void win_update_entry_message(ProfWin *window, const char *const id, const char *const message);
|
||||
void win_update_entry_theme(ProfWin *window, const char *const id, theme_item_t theme_item);
|
||||
|
||||
gboolean win_has_active_subwin(ProfWin *window);
|
||||
|
||||
|
@ -508,6 +508,17 @@ wins_close_by_num(int i)
|
||||
|
||||
ProfWin *window = wins_get_by_num(i);
|
||||
if (window) {
|
||||
// cancel upload proccesses of this window
|
||||
GSList *upload_process = upload_processes;
|
||||
while (upload_process) {
|
||||
HTTPUpload *upload = upload_process->data;
|
||||
if (upload->window == window) {
|
||||
upload->cancel = 1;
|
||||
break;
|
||||
}
|
||||
upload_process = g_slist_next(upload_process);
|
||||
}
|
||||
|
||||
switch (window->type) {
|
||||
case WIN_CHAT:
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
63
tests/unittests/tools/stub_http_upload.c
Normal file
63
tests/unittests/tools/stub_http_upload.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* http_upload.h
|
||||
*
|
||||
* Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
|
||||
*
|
||||
* This file is part of Profanity.
|
||||
*
|
||||
* Profanity is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Profanity is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Profanity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link the code of portions of this program with the OpenSSL library under
|
||||
* certain conditions as described in each individual source file, and
|
||||
* distribute linked combinations including the two.
|
||||
*
|
||||
* You must obey the GNU General Public License in all respects for all of the
|
||||
* code used other than OpenSSL. If you modify file(s) with this exception, you
|
||||
* may extend this exception to your version of the file(s), but you are not
|
||||
* obligated to do so. If you do not wish to do so, delete this exception
|
||||
* statement from your version. If you delete this exception statement from all
|
||||
* source files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOOLS_HTTP_UPLOAD_H
|
||||
#define TOOLS_HTTP_UPLOAD_H
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
// forward -> ui/win_types.h
|
||||
typedef struct prof_win_t ProfWin;
|
||||
|
||||
typedef struct http_upload_t {
|
||||
char *filename;
|
||||
off_t filesize;
|
||||
curl_off_t bytes_sent;
|
||||
char *mime_type;
|
||||
char *get_url;
|
||||
char *put_url;
|
||||
ProfWin *window;
|
||||
pthread_t worker;
|
||||
int cancel;
|
||||
} HTTPUpload;
|
||||
|
||||
//GSList *upload_processes;
|
||||
|
||||
void* http_file_put(void *userdata) {}
|
||||
|
||||
char* file_mime_type(const char* const file_name) {}
|
||||
off_t file_size(const char* const file_name) {}
|
||||
int is_regular_file(const char *filename) {}
|
||||
|
||||
#endif
|
@ -71,7 +71,7 @@ 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)
|
||||
{
|
||||
check_expected(barejid);
|
||||
check_expected(msg);
|
||||
@ -90,8 +90,8 @@ char* message_send_chat_pgp(const char * const barejid, const char * const msg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 barejid) {}
|
||||
@ -158,6 +158,7 @@ void iq_room_list_request(gchar *conferencejid)
|
||||
void iq_disco_info_request(gchar *jid) {}
|
||||
void iq_disco_items_request(gchar *jid) {}
|
||||
void iq_set_autoping(int seconds) {}
|
||||
void iq_http_upload_request(HTTPUpload *upload) {}
|
||||
void iq_confirm_instant_room(const char * const room_jid) {}
|
||||
void iq_destroy_room(const char * const room_jid) {}
|
||||
void iq_request_room_config_form(const char * const room_jid) {}
|
||||
|
Loading…
Reference in New Issue
Block a user