mirror of
https://github.com/profanity-im/profanity.git
synced 2024-12-04 14:46:46 -05:00
Merge pull request #1375 from wstrm/feature/omemo-sendfile
Send and retrieve encrypted files when using OMEMO
This commit is contained in:
commit
7a319df6c8
1
.gitignore
vendored
1
.gitignore
vendored
@ -86,3 +86,4 @@ breaks
|
|||||||
|
|
||||||
*.tar.*
|
*.tar.*
|
||||||
*.zip
|
*.zip
|
||||||
|
*.log*
|
||||||
|
@ -41,8 +41,12 @@ core_sources = \
|
|||||||
src/command/cmd_ac.h src/command/cmd_ac.c \
|
src/command/cmd_ac.h src/command/cmd_ac.c \
|
||||||
src/tools/parser.c \
|
src/tools/parser.c \
|
||||||
src/tools/parser.h \
|
src/tools/parser.h \
|
||||||
|
src/tools/http_common.c \
|
||||||
|
src/tools/http_common.h \
|
||||||
src/tools/http_upload.c \
|
src/tools/http_upload.c \
|
||||||
src/tools/http_upload.h \
|
src/tools/http_upload.h \
|
||||||
|
src/tools/http_download.c \
|
||||||
|
src/tools/http_download.h \
|
||||||
src/tools/bookmark_ignore.c \
|
src/tools/bookmark_ignore.c \
|
||||||
src/tools/bookmark_ignore.h \
|
src/tools/bookmark_ignore.h \
|
||||||
src/tools/autocomplete.c src/tools/autocomplete.h \
|
src/tools/autocomplete.c src/tools/autocomplete.h \
|
||||||
@ -119,6 +123,8 @@ unittest_sources = \
|
|||||||
tests/unittests/database/stub_database.c \
|
tests/unittests/database/stub_database.c \
|
||||||
tests/unittests/config/stub_accounts.c \
|
tests/unittests/config/stub_accounts.c \
|
||||||
tests/unittests/tools/stub_http_upload.c \
|
tests/unittests/tools/stub_http_upload.c \
|
||||||
|
tests/unittests/tools/stub_http_download.c \
|
||||||
|
tests/unittests/tools/stub_aesgcm_download.c \
|
||||||
tests/unittests/helpers.c tests/unittests/helpers.h \
|
tests/unittests/helpers.c tests/unittests/helpers.h \
|
||||||
tests/unittests/test_form.c tests/unittests/test_form.h \
|
tests/unittests/test_form.c tests/unittests/test_form.h \
|
||||||
tests/unittests/test_common.c tests/unittests/test_common.h \
|
tests/unittests/test_common.c tests/unittests/test_common.h \
|
||||||
@ -189,7 +195,8 @@ otr4_sources = \
|
|||||||
|
|
||||||
omemo_sources = \
|
omemo_sources = \
|
||||||
src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \
|
src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \
|
||||||
src/omemo/store.h src/omemo/store.c src/xmpp/omemo.h src/xmpp/omemo.c
|
src/omemo/store.h src/omemo/store.c src/xmpp/omemo.h src/xmpp/omemo.c \
|
||||||
|
src/tools/aesgcm_download.h src/tools/aesgcm_download.c
|
||||||
|
|
||||||
omemo_unittest_sources = \
|
omemo_unittest_sources = \
|
||||||
tests/unittests/omemo/stub_omemo.c
|
tests/unittests/omemo/stub_omemo.c
|
||||||
|
@ -193,7 +193,6 @@ static Autocomplete otr_sendfile_ac;
|
|||||||
static Autocomplete omemo_ac;
|
static Autocomplete omemo_ac;
|
||||||
static Autocomplete omemo_log_ac;
|
static Autocomplete omemo_log_ac;
|
||||||
static Autocomplete omemo_policy_ac;
|
static Autocomplete omemo_policy_ac;
|
||||||
static Autocomplete omemo_sendfile_ac;
|
|
||||||
#endif
|
#endif
|
||||||
static Autocomplete connect_property_ac;
|
static Autocomplete connect_property_ac;
|
||||||
static Autocomplete tls_property_ac;
|
static Autocomplete tls_property_ac;
|
||||||
@ -683,7 +682,6 @@ cmd_ac_init(void)
|
|||||||
autocomplete_add(omemo_ac, "clear_device_list");
|
autocomplete_add(omemo_ac, "clear_device_list");
|
||||||
autocomplete_add(omemo_ac, "policy");
|
autocomplete_add(omemo_ac, "policy");
|
||||||
autocomplete_add(omemo_ac, "char");
|
autocomplete_add(omemo_ac, "char");
|
||||||
autocomplete_add(omemo_ac, "sendfile");
|
|
||||||
|
|
||||||
omemo_log_ac = autocomplete_new();
|
omemo_log_ac = autocomplete_new();
|
||||||
autocomplete_add(omemo_log_ac, "on");
|
autocomplete_add(omemo_log_ac, "on");
|
||||||
@ -694,10 +692,6 @@ cmd_ac_init(void)
|
|||||||
autocomplete_add(omemo_policy_ac, "manual");
|
autocomplete_add(omemo_policy_ac, "manual");
|
||||||
autocomplete_add(omemo_policy_ac, "automatic");
|
autocomplete_add(omemo_policy_ac, "automatic");
|
||||||
autocomplete_add(omemo_policy_ac, "always");
|
autocomplete_add(omemo_policy_ac, "always");
|
||||||
|
|
||||||
omemo_sendfile_ac = autocomplete_new();
|
|
||||||
autocomplete_add(omemo_sendfile_ac, "on");
|
|
||||||
autocomplete_add(omemo_sendfile_ac, "off");
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect_property_ac = autocomplete_new();
|
connect_property_ac = autocomplete_new();
|
||||||
@ -1292,7 +1286,6 @@ cmd_ac_reset(ProfWin* window)
|
|||||||
autocomplete_reset(omemo_ac);
|
autocomplete_reset(omemo_ac);
|
||||||
autocomplete_reset(omemo_log_ac);
|
autocomplete_reset(omemo_log_ac);
|
||||||
autocomplete_reset(omemo_policy_ac);
|
autocomplete_reset(omemo_policy_ac);
|
||||||
autocomplete_reset(omemo_sendfile_ac);
|
|
||||||
#endif
|
#endif
|
||||||
autocomplete_reset(connect_property_ac);
|
autocomplete_reset(connect_property_ac);
|
||||||
autocomplete_reset(tls_property_ac);
|
autocomplete_reset(tls_property_ac);
|
||||||
@ -1450,7 +1443,6 @@ cmd_ac_uninit(void)
|
|||||||
autocomplete_free(omemo_ac);
|
autocomplete_free(omemo_ac);
|
||||||
autocomplete_free(omemo_log_ac);
|
autocomplete_free(omemo_log_ac);
|
||||||
autocomplete_free(omemo_policy_ac);
|
autocomplete_free(omemo_policy_ac);
|
||||||
autocomplete_free(omemo_sendfile_ac);
|
|
||||||
#endif
|
#endif
|
||||||
autocomplete_free(connect_property_ac);
|
autocomplete_free(connect_property_ac);
|
||||||
autocomplete_free(tls_property_ac);
|
autocomplete_free(tls_property_ac);
|
||||||
@ -2510,11 +2502,6 @@ _omemo_autocomplete(ProfWin* window, const char* const input, gboolean previous)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
found = autocomplete_param_with_ac(input, "/omemo sendfile", omemo_sendfile_ac, TRUE, previous);
|
|
||||||
if (found) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
jabber_conn_status_t conn_status = connection_get_status();
|
jabber_conn_status_t conn_status = connection_get_status();
|
||||||
|
|
||||||
if (conn_status == JABBER_CONNECTED) {
|
if (conn_status == JABBER_CONNECTED) {
|
||||||
|
@ -2285,8 +2285,7 @@ static struct cmd_t command_defs[] = {
|
|||||||
{ "fingerprint", cmd_omemo_fingerprint },
|
{ "fingerprint", cmd_omemo_fingerprint },
|
||||||
{ "char", cmd_omemo_char },
|
{ "char", cmd_omemo_char },
|
||||||
{ "policy", cmd_omemo_policy },
|
{ "policy", cmd_omemo_policy },
|
||||||
{ "clear_device_list", cmd_omemo_clear_device_list },
|
{ "clear_device_list", cmd_omemo_clear_device_list })
|
||||||
{ "sendfile", cmd_omemo_sendfile })
|
|
||||||
CMD_NOMAINFUNC
|
CMD_NOMAINFUNC
|
||||||
CMD_TAGS(
|
CMD_TAGS(
|
||||||
CMD_TAG_CHAT,
|
CMD_TAG_CHAT,
|
||||||
@ -2300,7 +2299,6 @@ static struct cmd_t command_defs[] = {
|
|||||||
"/omemo fingerprint [<contact>]",
|
"/omemo fingerprint [<contact>]",
|
||||||
"/omemo char <char>",
|
"/omemo char <char>",
|
||||||
"/omemo policy manual|automatic|always",
|
"/omemo policy manual|automatic|always",
|
||||||
"/omemo sendfile on|off",
|
|
||||||
"/omemo clear_device_list")
|
"/omemo clear_device_list")
|
||||||
CMD_DESC(
|
CMD_DESC(
|
||||||
"OMEMO commands to manage keys, and perform encryption during chat sessions.")
|
"OMEMO commands to manage keys, and perform encryption during chat sessions.")
|
||||||
@ -2315,7 +2313,6 @@ static struct cmd_t command_defs[] = {
|
|||||||
{ "policy manual", "Set the global OMEMO policy to manual, OMEMO sessions must be started manually." },
|
{ "policy manual", "Set the global OMEMO policy to manual, OMEMO sessions must be started manually." },
|
||||||
{ "policy automatic", "Set the global OMEMO policy to opportunistic, an OMEMO session will be attempted upon starting a conversation." },
|
{ "policy automatic", "Set the global OMEMO policy to opportunistic, an OMEMO session will be attempted upon starting a conversation." },
|
||||||
{ "policy always", "Set the global OMEMO policy to always, an error will be displayed if an OMEMO session cannot be initiated upon starting a conversation." },
|
{ "policy always", "Set the global OMEMO policy to always, an error will be displayed if an OMEMO session cannot be initiated upon starting a conversation." },
|
||||||
{ "sendfile on|off", "Allow /sendfile to send unencrypted files while in an OMEMO session." },
|
|
||||||
{ "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."})
|
{ "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."})
|
||||||
CMD_EXAMPLES(
|
CMD_EXAMPLES(
|
||||||
"/omemo gen",
|
"/omemo gen",
|
||||||
@ -2377,7 +2374,7 @@ static struct cmd_t command_defs[] = {
|
|||||||
"Settings for consistent color generation for nicks (XEP-0392). Including corrections for Color Vision Deficiencies. "
|
"Settings for consistent color generation for nicks (XEP-0392). Including corrections for Color Vision Deficiencies. "
|
||||||
"Your terminal needs to support 256 colors.")
|
"Your terminal needs to support 256 colors.")
|
||||||
CMD_ARGS(
|
CMD_ARGS(
|
||||||
{ "on|off|redgreen|blue", "Enable or disable nick colorization for MUC nicks. 'redgreen' is for people with red/green blindess and 'blue' for people with blue blindness." },
|
{ "on|off|redgreen|blue", "Enable or disable nick colorization for MUC nicks. 'redgreen' is for people with red/green blindness and 'blue' for people with blue blindness." },
|
||||||
{ "own on|off", "Enable color generation for own nick. If disabled the color from the color from the theme ('me') will get used." })
|
{ "own on|off", "Enable color generation for own nick. If disabled the color from the color from the theme ('me') will get used." })
|
||||||
CMD_EXAMPLES(
|
CMD_EXAMPLES(
|
||||||
"/color off",
|
"/color off",
|
||||||
@ -2496,26 +2493,35 @@ static struct cmd_t command_defs[] = {
|
|||||||
|
|
||||||
{ "/executable",
|
{ "/executable",
|
||||||
parse_args, 2, 4, &cons_executable_setting,
|
parse_args, 2, 4, &cons_executable_setting,
|
||||||
CMD_NOSUBFUNCS
|
CMD_SUBFUNCS(
|
||||||
CMD_MAINFUNC(cmd_executable)
|
{ "avatar", cmd_executable_avatar },
|
||||||
|
{ "urlopen", cmd_executable_urlopen },
|
||||||
|
{ "urlsave", cmd_executable_urlsave })
|
||||||
|
CMD_NOMAINFUNC
|
||||||
CMD_TAGS(
|
CMD_TAGS(
|
||||||
CMD_TAG_DISCOVERY)
|
CMD_TAG_DISCOVERY)
|
||||||
CMD_SYN(
|
CMD_SYN(
|
||||||
"/executable avatar <cmd>",
|
"/executable avatar <cmd>",
|
||||||
"/executable urlopen (<fileType>|DEF <require_save> <cmd>",
|
"/executable urlopen set <cmdtemplate>",
|
||||||
"/executable urlsave (<protocol>|DEF) <cmd>")
|
"/executable urlopen default",
|
||||||
|
"/executable urlsave set <cmdtemplate>",
|
||||||
|
"/executable urlsave default")
|
||||||
CMD_DESC(
|
CMD_DESC(
|
||||||
"Configure executable that should be called upon a certain command."
|
"Configure executable that should be called upon a certain command.")
|
||||||
"Default is xdg-open.")
|
|
||||||
CMD_ARGS(
|
CMD_ARGS(
|
||||||
{ "avatar", "Set executable that is run in /avatar open. Use your favourite image viewer." },
|
{ "avatar", "Set executable that is run by /avatar open. Use your favorite image viewer." },
|
||||||
{ "urlopen", "Set executable that is run in /url open for a given file type. It may be your favorite browser or a specific viewer. Use DEF to set default command for undefined file type." },
|
{ "urlopen set", "Set executable that is run by /url open. It may be your favorite browser or a specific viewer." },
|
||||||
{ "urlsave", "Set executable that is run in /url save for a given protocol. Use your favourite downloader. Use DEF to set default command for undefined protocol." })
|
{ "urlopen default", "Restore to default settings." },
|
||||||
|
{ "urlsave set", "Set executable that is run by /url save. It may be your favorite downloader.'" },
|
||||||
|
{ "urlsave default", "Use the built-in download method for saving." })
|
||||||
CMD_EXAMPLES(
|
CMD_EXAMPLES(
|
||||||
"/executable avatar xdg-open",
|
"/executable avatar xdg-open",
|
||||||
"/executable urlopen DEF false \"xdg-open %u\"",
|
"/executable urlopen set \"xdg-open %u\"",
|
||||||
"/executable urlopen html false \"firefox %u\"",
|
"/executable urlopen set \"firefox %u\"",
|
||||||
"/executable urlsave aesgcm \"omut -d -o %p %u\"")
|
"/executable urlopen default",
|
||||||
|
"/executable urlsave set \"wget %u -O %p\"",
|
||||||
|
"/executable urlsave set \"curl %u -o %p\"",
|
||||||
|
"/executable urlsave default")
|
||||||
},
|
},
|
||||||
|
|
||||||
{ "/url",
|
{ "/url",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||||
* Copyright (C) 2019 Michael Vetter <jubalh@iodoru.org>
|
* Copyright (C) 2019 Michael Vetter <jubalh@iodoru.org>
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
*
|
*
|
||||||
* This file is part of Profanity.
|
* This file is part of Profanity.
|
||||||
*
|
*
|
||||||
@ -59,6 +60,7 @@
|
|||||||
#include "command/cmd_funcs.h"
|
#include "command/cmd_funcs.h"
|
||||||
#include "command/cmd_defs.h"
|
#include "command/cmd_defs.h"
|
||||||
#include "command/cmd_ac.h"
|
#include "command/cmd_ac.h"
|
||||||
|
#include "config/files.h"
|
||||||
#include "config/accounts.h"
|
#include "config/accounts.h"
|
||||||
#include "config/account.h"
|
#include "config/account.h"
|
||||||
#include "config/preferences.h"
|
#include "config/preferences.h"
|
||||||
@ -67,6 +69,7 @@
|
|||||||
#include "config/scripts.h"
|
#include "config/scripts.h"
|
||||||
#include "event/client_events.h"
|
#include "event/client_events.h"
|
||||||
#include "tools/http_upload.h"
|
#include "tools/http_upload.h"
|
||||||
|
#include "tools/http_download.h"
|
||||||
#include "tools/autocomplete.h"
|
#include "tools/autocomplete.h"
|
||||||
#include "tools/parser.h"
|
#include "tools/parser.h"
|
||||||
#include "tools/bookmark_ignore.h"
|
#include "tools/bookmark_ignore.h"
|
||||||
@ -94,6 +97,7 @@
|
|||||||
#ifdef HAVE_OMEMO
|
#ifdef HAVE_OMEMO
|
||||||
#include "omemo/omemo.h"
|
#include "omemo/omemo.h"
|
||||||
#include "xmpp/omemo.h"
|
#include "xmpp/omemo.h"
|
||||||
|
#include "tools/aesgcm_download.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_GTK
|
#ifdef HAVE_GTK
|
||||||
@ -4805,11 +4809,54 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move this into its own tools such as HTTPUpload or AESGCMDownload.
|
||||||
|
#ifdef HAVE_OMEMO
|
||||||
|
char*
|
||||||
|
_add_omemo_stream(int* fd, FILE** fh, char** err)
|
||||||
|
{
|
||||||
|
// Create temporary file for writing ciphertext.
|
||||||
|
int tmpfd;
|
||||||
|
char* tmpname = NULL;
|
||||||
|
if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) {
|
||||||
|
*err = "Unable to create temporary file for encrypted transfer.";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
FILE* tmpfh = fdopen(tmpfd, "wb");
|
||||||
|
|
||||||
|
// The temporary ciphertext file should be removed after it has
|
||||||
|
// been closed.
|
||||||
|
remove(tmpname);
|
||||||
|
free(tmpname);
|
||||||
|
|
||||||
|
int crypt_res;
|
||||||
|
char* fragment;
|
||||||
|
fragment = omemo_encrypt_file(*fh, tmpfh, file_size(*fd), &crypt_res);
|
||||||
|
if (crypt_res != 0) {
|
||||||
|
fclose(tmpfh);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force flush as the upload will read from the same stream.
|
||||||
|
fflush(tmpfh);
|
||||||
|
rewind(tmpfh);
|
||||||
|
|
||||||
|
fclose(*fh); // Also closes descriptor.
|
||||||
|
|
||||||
|
// Switch original stream with temporary ciphertext stream.
|
||||||
|
*fd = tmpfd;
|
||||||
|
*fh = tmpfh;
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
jabber_conn_status_t conn_status = connection_get_status();
|
jabber_conn_status_t conn_status = connection_get_status();
|
||||||
char* filename = args[0];
|
char* filename = args[0];
|
||||||
|
char* alt_scheme = NULL;
|
||||||
|
char* alt_fragment = NULL;
|
||||||
|
|
||||||
// expand ~ to $HOME
|
// expand ~ to $HOME
|
||||||
if (filename[0] == '~' && filename[1] == '/') {
|
if (filename[0] == '~' && filename[1] == '/') {
|
||||||
@ -4820,80 +4867,101 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
|||||||
filename = strdup(filename);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (window->type) {
|
|
||||||
case WIN_MUC:
|
|
||||||
{
|
|
||||||
ProfMucWin* mucwin = (ProfMucWin*)window;
|
|
||||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
|
||||||
|
|
||||||
// only omemo, no pgp/otr available in MUCs
|
|
||||||
if (mucwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) {
|
|
||||||
cons_show_error("Uploading unencrypted files disabled. See /omemo sendfile, /otr sendfile, /pgp sendfile.");
|
|
||||||
win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
|
|
||||||
free(filename);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WIN_CHAT:
|
|
||||||
{
|
|
||||||
ProfChatWin* chatwin = (ProfChatWin*)window;
|
|
||||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
|
||||||
|
|
||||||
if ((chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE))
|
|
||||||
|| (chatwin->pgp_send && !prefs_get_boolean(PREF_PGP_SENDFILE))
|
|
||||||
|| (chatwin->is_otr && !prefs_get_boolean(PREF_OTR_SENDFILE))) {
|
|
||||||
cons_show_error("Uploading unencrypted files disabled. See /omemo sendfile, /otr sendfile, /pgp sendfile.");
|
|
||||||
win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
|
|
||||||
free(filename);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WIN_PRIVATE:
|
|
||||||
{
|
|
||||||
//we don't support encryption in private muc windows
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
cons_show_error("Unsupported window for file transmission.");
|
|
||||||
free(filename);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(filename, R_OK) != 0) {
|
if (access(filename, R_OK) != 0) {
|
||||||
cons_show_error("Uploading '%s' failed: File not found!", filename);
|
cons_show_error("Uploading '%s' failed: File not found!", filename);
|
||||||
free(filename);
|
goto out;
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_regular_file(filename)) {
|
if (!is_regular_file(filename)) {
|
||||||
cons_show_error("Uploading '%s' failed: Not a file!", filename);
|
cons_show_error("Uploading '%s' failed: Not a file!", filename);
|
||||||
free(filename);
|
goto out;
|
||||||
return TRUE;
|
}
|
||||||
|
|
||||||
|
if (conn_status != JABBER_CONNECTED) {
|
||||||
|
cons_show("You are not currently connected.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window->type != WIN_CHAT && window->type != WIN_PRIVATE && window->type != WIN_MUC) {
|
||||||
|
cons_show_error("Unsupported window for file transmission.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
if ((fd = open(filename, O_RDONLY)) == -1) {
|
||||||
|
cons_show_error("Unable to open file descriptor for '%s'.", filename);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fh = fdopen(fd, "rb");
|
||||||
|
|
||||||
|
switch (window->type) {
|
||||||
|
case WIN_MUC:
|
||||||
|
case WIN_CHAT:
|
||||||
|
{
|
||||||
|
ProfChatWin* chatwin = (ProfChatWin*)window;
|
||||||
|
|
||||||
|
#ifdef HAVE_OMEMO
|
||||||
|
if (chatwin->is_omemo) {
|
||||||
|
char* err = NULL;
|
||||||
|
alt_scheme = OMEMO_AESGCM_URL_SCHEME;
|
||||||
|
alt_fragment = _add_omemo_stream(&fd, &fh, &err);
|
||||||
|
if (err != NULL) {
|
||||||
|
cons_show_error(err);
|
||||||
|
win_println(window, THEME_ERROR, "-", err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (window->type == WIN_CHAT) {
|
||||||
|
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||||
|
if ((chatwin->pgp_send && !prefs_get_boolean(PREF_PGP_SENDFILE))
|
||||||
|
|| (chatwin->is_otr && !prefs_get_boolean(PREF_OTR_SENDFILE))) {
|
||||||
|
cons_show_error("Uploading unencrypted files disabled. See /otr sendfile or /pgp sendfile.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WIN_PRIVATE: // We don't support encryption in private MUC windows.
|
||||||
|
default:
|
||||||
|
cons_show_error("Unsupported window for file transmission.");
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPUpload* upload = malloc(sizeof(HTTPUpload));
|
HTTPUpload* upload = malloc(sizeof(HTTPUpload));
|
||||||
upload->window = window;
|
upload->window = window;
|
||||||
|
|
||||||
upload->filename = filename;
|
upload->filename = strdup(filename);
|
||||||
upload->filesize = file_size(filename);
|
upload->filehandle = fh;
|
||||||
|
upload->filesize = file_size(fd);
|
||||||
upload->mime_type = file_mime_type(filename);
|
upload->mime_type = file_mime_type(filename);
|
||||||
|
|
||||||
|
if (alt_scheme != NULL) {
|
||||||
|
upload->alt_scheme = strdup(alt_scheme);
|
||||||
|
} else {
|
||||||
|
upload->alt_scheme = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alt_fragment != NULL) {
|
||||||
|
upload->alt_fragment = strdup(alt_fragment);
|
||||||
|
} else {
|
||||||
|
upload->alt_fragment = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
iq_http_upload_request(upload);
|
iq_http_upload_request(upload);
|
||||||
|
|
||||||
|
out:
|
||||||
|
#ifdef HAVE_OMEMO
|
||||||
|
if (alt_fragment != NULL)
|
||||||
|
omemo_free(alt_fragment);
|
||||||
|
#endif
|
||||||
|
if (filename != NULL)
|
||||||
|
free(filename);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8772,19 +8840,6 @@ cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
|
||||||
cmd_omemo_sendfile(ProfWin* window, const char* const command, gchar** args)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_OMEMO
|
|
||||||
_cmd_set_boolean_preference(args[1], command, "Sending unencrypted files in an OMEMO session via /sendfile", PREF_OMEMO_SENDFILE);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
#else
|
|
||||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
|
||||||
return TRUE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
cmd_save(ProfWin* window, const char* const command, gchar** args)
|
cmd_save(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
@ -9005,6 +9060,57 @@ cmd_slashguard(ProfWin* window, const char* const command, gchar** args)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_OMEMO
|
||||||
|
void
|
||||||
|
_url_aesgcm_method(ProfWin* window, const char* cmd_template, const char* url, const char* filename)
|
||||||
|
{
|
||||||
|
AESGCMDownload* download = malloc(sizeof(AESGCMDownload));
|
||||||
|
download->window = window;
|
||||||
|
download->url = strdup(url);
|
||||||
|
download->filename = strdup(filename);
|
||||||
|
if (cmd_template != NULL) {
|
||||||
|
download->cmd_template = strdup(cmd_template);
|
||||||
|
} else {
|
||||||
|
download->cmd_template = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_create(&(download->worker), NULL, &aesgcm_file_get, download);
|
||||||
|
aesgcm_download_add_download(download);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
_url_http_method(ProfWin* window, const char* cmd_template, const char* url, const char* filename)
|
||||||
|
{
|
||||||
|
|
||||||
|
HTTPDownload* download = malloc(sizeof(HTTPDownload));
|
||||||
|
download->window = window;
|
||||||
|
download->url = strdup(url);
|
||||||
|
download->filename = strdup(filename);
|
||||||
|
if (cmd_template != NULL) {
|
||||||
|
download->cmd_template = strdup(cmd_template);
|
||||||
|
} else {
|
||||||
|
download->cmd_template = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_create(&(download->worker), NULL, &http_file_get, download);
|
||||||
|
http_download_add_download(download);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_url_external_method(const char* cmd_template, const char* url, const char* filename)
|
||||||
|
{
|
||||||
|
gchar** argv = format_call_external_argv(cmd_template, url, filename);
|
||||||
|
|
||||||
|
if (!call_external(argv, NULL, NULL)) {
|
||||||
|
cons_show_error("Unable to call external executable for url: check the logs for more information.");
|
||||||
|
} else {
|
||||||
|
cons_show("URL '%s' has been called with '%s'.", url, cmd_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(argv);
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
cmd_url_open(ProfWin* window, const char* const command, gchar** args)
|
cmd_url_open(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
@ -9013,92 +9119,46 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args[1] == NULL) {
|
gchar* url = args[1];
|
||||||
|
if (url == NULL) {
|
||||||
cons_bad_cmd_usage(command);
|
cons_bad_cmd_usage(command);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean require_save = false;
|
gchar* scheme = NULL;
|
||||||
|
char* cmd_template = NULL;
|
||||||
|
char* filename = NULL;
|
||||||
|
|
||||||
gchar* fileStart = g_strrstr(args[1], "/");
|
scheme = g_uri_parse_scheme(url);
|
||||||
if (fileStart == NULL) {
|
if (scheme == NULL) {
|
||||||
cons_show("URL '%s' is not valid.", args[1]);
|
cons_show("URL '%s' is not valid.", args[1]);
|
||||||
return TRUE;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileStart++;
|
cmd_template = prefs_get_string(PREF_URL_OPEN_CMD);
|
||||||
if (((char*)(fileStart - 2))[0] == '/' && ((char*)(fileStart - 3))[0] == ':') {
|
if (cmd_template == NULL) {
|
||||||
// If the '/' is last character of the '://' string, there will be no suffix
|
cons_show("No default `url open` command found in executables preferences.");
|
||||||
// Therefore, it is considered that there is no file name in the URL and
|
goto out;
|
||||||
// fileStart is set to the end of the URL.
|
|
||||||
fileStart = args[1] + strlen(args[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar* suffix = NULL;
|
#ifdef HAVE_OMEMO
|
||||||
gchar* suffixStart = g_strrstr(fileStart, ".");
|
// OMEMO URLs (aesgcm://) must be saved and decrypted before being opened.
|
||||||
if (suffixStart != NULL) {
|
|
||||||
suffixStart++;
|
|
||||||
gchar* suffixEnd = g_strrstr(suffixStart, "#");
|
|
||||||
if (suffixEnd == NULL) {
|
|
||||||
suffix = g_strdup(suffixStart);
|
|
||||||
} else {
|
|
||||||
suffix = g_strndup(suffixStart, suffixEnd - suffixStart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar** suffix_cmd_pref = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, NULL);
|
|
||||||
if (suffix != NULL) {
|
|
||||||
gchar* lowercase_suffix = g_ascii_strdown(suffix, -1);
|
|
||||||
g_strfreev(suffix_cmd_pref);
|
|
||||||
suffix_cmd_pref = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, lowercase_suffix);
|
|
||||||
g_free(lowercase_suffix);
|
|
||||||
g_free(suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 == g_strcmp0(suffix_cmd_pref[0], "true")) {
|
|
||||||
require_save = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar* suffix_cmd = g_strdup(suffix_cmd_pref[1]);
|
|
||||||
g_strfreev(suffix_cmd_pref);
|
|
||||||
|
|
||||||
gchar* scheme = g_uri_parse_scheme(args[1]);
|
|
||||||
if (0 == g_strcmp0(scheme, "aesgcm")) {
|
if (0 == g_strcmp0(scheme, "aesgcm")) {
|
||||||
require_save = true;
|
filename = unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS));
|
||||||
|
_url_aesgcm_method(window, cmd_template, url, filename);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_url_external_method(cmd_template, url, NULL);
|
||||||
|
|
||||||
|
out:
|
||||||
|
|
||||||
|
free(cmd_template);
|
||||||
|
free(filename);
|
||||||
|
|
||||||
g_free(scheme);
|
g_free(scheme);
|
||||||
|
|
||||||
if (require_save) {
|
|
||||||
gchar* save_args[] = { "open", args[1], "/tmp/profanity.tmp", NULL };
|
|
||||||
cmd_url_save(window, command, save_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar** argv = g_strsplit(suffix_cmd, " ", 0);
|
|
||||||
guint num_args = 0;
|
|
||||||
while (argv[num_args]) {
|
|
||||||
if (0 == g_strcmp0(argv[num_args], "%u")) {
|
|
||||||
g_free(argv[num_args]);
|
|
||||||
if (require_save) {
|
|
||||||
argv[num_args] = g_strdup("/tmp/profanity.tmp");
|
|
||||||
} else {
|
|
||||||
argv[num_args] = g_strdup(args[1]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
num_args++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!call_external(argv, NULL, NULL)) {
|
|
||||||
cons_show_error("Unable to open url: check the logs for more information.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require_save) {
|
|
||||||
g_unlink("/tmp/profanity.tmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev(argv);
|
|
||||||
g_free(suffix_cmd);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9106,7 +9166,7 @@ gboolean
|
|||||||
cmd_url_save(ProfWin* window, const char* const command, gchar** args)
|
cmd_url_save(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) {
|
if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) {
|
||||||
cons_show("url save not supported in this window");
|
cons_show_error("`/url save` is not supported in this window.");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9115,121 +9175,104 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar* uri = args[1];
|
gchar* url = args[1];
|
||||||
gchar* target_path = g_strdup(args[2]);
|
gchar* path = g_strdup(args[2]);
|
||||||
|
gchar* scheme = NULL;
|
||||||
|
char* filename = NULL;
|
||||||
|
char* cmd_template = NULL;
|
||||||
|
|
||||||
GFile* file = g_file_new_for_uri(uri);
|
scheme = g_uri_parse_scheme(url);
|
||||||
|
|
||||||
gchar* target_dir = NULL;
|
|
||||||
gchar* base_name = NULL;
|
|
||||||
|
|
||||||
if (target_path == NULL) {
|
|
||||||
target_dir = g_strdup("./");
|
|
||||||
base_name = g_file_get_basename(file);
|
|
||||||
if (0 == g_strcmp0(base_name, ".")) {
|
|
||||||
g_free(base_name);
|
|
||||||
base_name = g_strdup("saved_url_content.html");
|
|
||||||
}
|
|
||||||
target_path = g_strconcat(target_dir, base_name, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_file_test(target_path, G_FILE_TEST_EXISTS) && g_file_test(target_path, G_FILE_TEST_IS_DIR)) {
|
|
||||||
target_dir = g_strdup(target_path);
|
|
||||||
base_name = g_file_get_basename(file);
|
|
||||||
g_free(target_path);
|
|
||||||
target_path = g_strconcat(target_dir, "/", base_name, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_unref(file);
|
|
||||||
file = NULL;
|
|
||||||
|
|
||||||
if (base_name == NULL) {
|
|
||||||
base_name = g_path_get_basename(target_path);
|
|
||||||
target_dir = g_path_get_dirname(target_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_file_test(target_dir, G_FILE_TEST_EXISTS) || !g_file_test(target_dir, G_FILE_TEST_IS_DIR)) {
|
|
||||||
cons_show("%s does not exist or is not a directory.", target_dir);
|
|
||||||
g_free(target_path);
|
|
||||||
g_free(target_dir);
|
|
||||||
g_free(base_name);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar* scheme = g_uri_parse_scheme(uri);
|
|
||||||
if (scheme == NULL) {
|
if (scheme == NULL) {
|
||||||
cons_show("URL '%s' is not valid.", uri);
|
cons_show("URL '%s' is not valid.", args[1]);
|
||||||
g_free(target_path);
|
goto out;
|
||||||
g_free(target_dir);
|
|
||||||
g_free(base_name);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar* scheme_cmd = NULL;
|
filename = unique_filename_from_url(url, path);
|
||||||
|
if (filename == NULL) {
|
||||||
if (0 == g_strcmp0(scheme, "http")
|
cons_show("Failed to generate unique filename"
|
||||||
|| 0 == g_strcmp0(scheme, "https")
|
"from URL '%s' for path '%s'",
|
||||||
|| 0 == g_strcmp0(scheme, "aesgcm")) {
|
url, path);
|
||||||
scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme);
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_template = prefs_get_string(PREF_URL_SAVE_CMD);
|
||||||
|
if (cmd_template == NULL && (g_strcmp0(scheme, "http") == 0 || g_strcmp0(scheme, "https") == 0)) {
|
||||||
|
_url_http_method(window, cmd_template, url, filename);
|
||||||
|
#ifdef HAVE_OMEMO
|
||||||
|
} else if (g_strcmp0(scheme, "aesgcm") == 0) {
|
||||||
|
_url_aesgcm_method(window, cmd_template, url, filename);
|
||||||
|
#endif
|
||||||
|
} else if (cmd_template != NULL) {
|
||||||
|
_url_external_method(cmd_template, url, filename);
|
||||||
|
} else {
|
||||||
|
cons_show_error("No download method defined for the scheme '%s'.", scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
|
||||||
|
free(filename);
|
||||||
|
free(cmd_template);
|
||||||
|
|
||||||
g_free(scheme);
|
g_free(scheme);
|
||||||
|
g_free(path);
|
||||||
gchar** argv = g_strsplit(scheme_cmd, " ", 0);
|
|
||||||
g_free(scheme_cmd);
|
|
||||||
|
|
||||||
guint num_args = 0;
|
|
||||||
while (argv[num_args]) {
|
|
||||||
if (0 == g_strcmp0(argv[num_args], "%u")) {
|
|
||||||
g_free(argv[num_args]);
|
|
||||||
argv[num_args] = g_strdup(uri);
|
|
||||||
} else if (0 == g_strcmp0(argv[num_args], "%p")) {
|
|
||||||
g_free(argv[num_args]);
|
|
||||||
argv[num_args] = target_path;
|
|
||||||
}
|
|
||||||
num_args++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!call_external(argv, NULL, NULL)) {
|
|
||||||
cons_show_error("Unable to save url: check the logs for more information.");
|
|
||||||
} else {
|
|
||||||
cons_show("URL '%s' has been saved into '%s'.", uri, target_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(target_dir);
|
|
||||||
g_free(base_name);
|
|
||||||
g_strfreev(argv);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
cmd_executable(ProfWin* window, const char* const command, gchar** args)
|
cmd_executable_avatar(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
if (g_strcmp0(args[0], "avatar") == 0) {
|
|
||||||
prefs_set_string(PREF_AVATAR_CMD, args[1]);
|
prefs_set_string(PREF_AVATAR_CMD, args[1]);
|
||||||
cons_show("Avatar command set to: %s", args[1]);
|
cons_show("`avatar` command set to invoke '%s'", args[1]);
|
||||||
} else if (g_strcmp0(args[0], "urlopen") == 0) {
|
return TRUE;
|
||||||
if (g_strv_length(args) < 4) {
|
}
|
||||||
cons_bad_cmd_usage(command);
|
|
||||||
return TRUE;
|
gboolean
|
||||||
}
|
cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args)
|
||||||
|
{
|
||||||
gchar* str = g_strjoinv(" ", &args[3]);
|
guint num_args = g_strv_length(args);
|
||||||
const gchar* const list[] = { args[2], str, NULL };
|
if (num_args < 2) {
|
||||||
prefs_set_string_list_with_option(PREF_URL_OPEN_CMD, args[1], list);
|
|
||||||
cons_show("`url open` command set to: %s for %s files", str, args[1]);
|
|
||||||
g_free(str);
|
|
||||||
} else if (g_strcmp0(args[0], "urlsave") == 0) {
|
|
||||||
if (g_strv_length(args) < 3) {
|
|
||||||
cons_bad_cmd_usage(command);
|
cons_bad_cmd_usage(command);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
|
||||||
gchar* str = g_strjoinv(" ", &args[2]);
|
gchar* str = g_strjoinv(" ", &args[2]);
|
||||||
prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[1], str);
|
prefs_set_string(PREF_URL_OPEN_CMD, str);
|
||||||
cons_show("`url save` command set to: %s for scheme %s", str, args[1]);
|
cons_show("`url open` command set to invoke '%s'", str);
|
||||||
g_free(str);
|
g_free(str);
|
||||||
|
|
||||||
|
} else if (g_strcmp0(args[1], "default") == 0) {
|
||||||
|
prefs_set_string(PREF_URL_SAVE_CMD, NULL);
|
||||||
|
gchar* def = prefs_get_string(PREF_URL_SAVE_CMD);
|
||||||
|
cons_show("`url open` command set to invoke %s (default)", def);
|
||||||
|
g_free(def);
|
||||||
|
} else {
|
||||||
|
cons_bad_cmd_usage(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
cmd_executable_urlsave(ProfWin* window, const char* const command, gchar** args)
|
||||||
|
{
|
||||||
|
|
||||||
|
guint num_args = g_strv_length(args);
|
||||||
|
if (num_args < 2) {
|
||||||
|
cons_bad_cmd_usage(command);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
|
||||||
|
gchar* str = g_strjoinv(" ", &args[2]);
|
||||||
|
prefs_set_string(PREF_URL_SAVE_CMD, str);
|
||||||
|
cons_show("`url save` command set to invoke '%s'", str);
|
||||||
|
g_free(str);
|
||||||
|
|
||||||
|
} else if (g_strcmp0(args[1], "default") == 0) {
|
||||||
|
prefs_set_string(PREF_URL_SAVE_CMD, NULL);
|
||||||
|
cons_show("`url save` will use built-in download method (default)");
|
||||||
} else {
|
} else {
|
||||||
cons_bad_cmd_usage(command);
|
cons_bad_cmd_usage(command);
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,6 @@ gboolean cmd_omemo_trust(ProfWin* window, const char* const command, gchar** arg
|
|||||||
gboolean cmd_omemo_untrust(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_omemo_untrust(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_omemo_clear_device_list(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_omemo_clear_device_list(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_omemo_sendfile(ProfWin* window, const char* const command, gchar** args);
|
|
||||||
|
|
||||||
gboolean cmd_save(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_save(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_reload(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_reload(ProfWin* window, const char* const command, gchar** args);
|
||||||
@ -238,6 +237,8 @@ gboolean cmd_slashguard(ProfWin* window, const char* const command, gchar** args
|
|||||||
gboolean cmd_serversoftware(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_serversoftware(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_url_open(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_url_open(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_url_save(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_url_save(ProfWin* window, const char* const command, gchar** args);
|
||||||
gboolean cmd_executable(ProfWin* window, const char* const command, gchar** args);
|
gboolean cmd_executable_avatar(ProfWin* window, const char* const command, gchar** args);
|
||||||
|
gboolean cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args);
|
||||||
|
gboolean cmd_executable_urlsave(ProfWin* window, const char* const command, gchar** args);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
107
src/common.c
107
src/common.c
@ -33,6 +33,9 @@
|
|||||||
* source files in the program, then also delete it here.
|
* source files in the program, then also delete it here.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE 1
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -555,3 +558,107 @@ call_external(gchar** argv, gchar*** const output_ptr, gchar*** const error_ptr)
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gchar**
|
||||||
|
format_call_external_argv(const char* template, const char* url, const char* filename)
|
||||||
|
{
|
||||||
|
gchar** argv = g_strsplit(template, " ", 0);
|
||||||
|
|
||||||
|
guint num_args = 0;
|
||||||
|
while (argv[num_args]) {
|
||||||
|
if (0 == g_strcmp0(argv[num_args], "%u") && url != NULL) {
|
||||||
|
g_free(argv[num_args]);
|
||||||
|
argv[num_args] = g_strdup(url);
|
||||||
|
} else if (0 == g_strcmp0(argv[num_args], "%p") && filename != NULL) {
|
||||||
|
g_free(argv[num_args]);
|
||||||
|
argv[num_args] = strdup(filename);
|
||||||
|
}
|
||||||
|
num_args++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
_unique_filename(const char* filename)
|
||||||
|
{
|
||||||
|
gchar* unique = g_strdup(filename);
|
||||||
|
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (g_file_test(unique, G_FILE_TEST_EXISTS)) {
|
||||||
|
free(unique);
|
||||||
|
|
||||||
|
if (i > 1000) { // Give up after 1000 attempts.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(&unique, "%s.%u", filename, i) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return unique;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
_has_directory_suffix(const char* path)
|
||||||
|
{
|
||||||
|
return (g_str_has_suffix(path, ".")
|
||||||
|
|| g_str_has_suffix(path, "..")
|
||||||
|
|| g_str_has_suffix(path, G_DIR_SEPARATOR_S));
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
_basename_from_url(const char* url)
|
||||||
|
{
|
||||||
|
const char* default_name = "index";
|
||||||
|
|
||||||
|
GFile* file = g_file_new_for_commandline_arg(url);
|
||||||
|
char* basename = g_file_get_basename(file);
|
||||||
|
|
||||||
|
if (_has_directory_suffix(basename)) {
|
||||||
|
g_free(basename);
|
||||||
|
basename = strdup(default_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(file);
|
||||||
|
|
||||||
|
return basename;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
unique_filename_from_url(const char* url, const char* path)
|
||||||
|
{
|
||||||
|
// Default to './' as path when none has been provided.
|
||||||
|
if (path == NULL) {
|
||||||
|
path = "./";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolves paths such as './../.' for path.
|
||||||
|
GFile* target = g_file_new_for_commandline_arg(path);
|
||||||
|
gchar* filename = NULL;
|
||||||
|
|
||||||
|
if (_has_directory_suffix(path) || g_file_test(path, G_FILE_TEST_IS_DIR)) {
|
||||||
|
// The target should be used as a directory. Assume that the basename
|
||||||
|
// should be derived from the URL.
|
||||||
|
char* basename = _basename_from_url(url);
|
||||||
|
filename = g_build_filename(g_file_peek_path(target), basename, NULL);
|
||||||
|
g_free(basename);
|
||||||
|
} else {
|
||||||
|
// Just use the target as filename.
|
||||||
|
filename = g_build_filename(g_file_peek_path(target), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar* unique_filename = _unique_filename(filename);
|
||||||
|
if (unique_filename == NULL) {
|
||||||
|
g_free(filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(target);
|
||||||
|
g_free(filename);
|
||||||
|
|
||||||
|
return unique_filename;
|
||||||
|
}
|
||||||
|
@ -105,5 +105,8 @@ void get_file_paths_recursive(const char* directory, GSList** contents);
|
|||||||
char* get_random_string(int length);
|
char* get_random_string(int length);
|
||||||
|
|
||||||
gboolean call_external(gchar** argv, gchar*** const output_ptr, gchar*** const error_ptr);
|
gboolean call_external(gchar** argv, gchar*** const output_ptr, gchar*** const error_ptr);
|
||||||
|
gchar** format_call_external_argv(const char* template, const char* url, const char* filename);
|
||||||
|
|
||||||
|
gchar* unique_filename_from_url(const char* url, const char* path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
#define DIR_OMEMO "omemo"
|
#define DIR_OMEMO "omemo"
|
||||||
#define DIR_PLUGINS "plugins"
|
#define DIR_PLUGINS "plugins"
|
||||||
#define DIR_DATABASE "database"
|
#define DIR_DATABASE "database"
|
||||||
|
#define DIR_DOWNLOADS "downloads"
|
||||||
|
|
||||||
void files_create_directories(void);
|
void files_create_directories(void);
|
||||||
|
|
||||||
|
@ -81,7 +81,6 @@ static const char* _get_group(preference_t pref);
|
|||||||
static const char* _get_key(preference_t pref);
|
static const char* _get_key(preference_t pref);
|
||||||
static gboolean _get_default_boolean(preference_t pref);
|
static gboolean _get_default_boolean(preference_t pref);
|
||||||
static char* _get_default_string(preference_t pref);
|
static char* _get_default_string(preference_t pref);
|
||||||
static char** _get_default_string_list(preference_t pref);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_prefs_load(void)
|
_prefs_load(void)
|
||||||
@ -171,7 +170,7 @@ _prefs_load(void)
|
|||||||
value = g_string_append(value, val);
|
value = g_string_append(value, val);
|
||||||
value = g_string_append(value, " %u;");
|
value = g_string_append(value, " %u;");
|
||||||
|
|
||||||
g_key_file_set_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "DEF", value->str);
|
g_key_file_set_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "*", value->str);
|
||||||
g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
|
g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
|
||||||
|
|
||||||
g_string_free(value, TRUE);
|
g_string_free(value, TRUE);
|
||||||
@ -530,7 +529,7 @@ prefs_get_string_with_option(preference_t pref, gchar* option)
|
|||||||
|
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
// check for user set default
|
// check for user set default
|
||||||
result = g_key_file_get_locale_string(prefs, group, key, "DEF", NULL);
|
result = g_key_file_get_locale_string(prefs, group, key, "*", NULL);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
if (def) {
|
if (def) {
|
||||||
// use hardcoded profanity default
|
// use hardcoded profanity default
|
||||||
@ -544,33 +543,6 @@ prefs_get_string_with_option(preference_t pref, gchar* option)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar**
|
|
||||||
prefs_get_string_list_with_option(preference_t pref, gchar* option)
|
|
||||||
{
|
|
||||||
const char* group = _get_group(pref);
|
|
||||||
const char* key = _get_key(pref);
|
|
||||||
char** def = _get_default_string_list(pref);
|
|
||||||
|
|
||||||
gchar** result = g_key_file_get_locale_string_list(prefs, group, key, option, NULL, NULL);
|
|
||||||
if (result) {
|
|
||||||
g_strfreev(def);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = g_key_file_get_string_list(prefs, group, key, NULL, NULL);
|
|
||||||
if (result) {
|
|
||||||
g_strfreev(def);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def) {
|
|
||||||
return def;
|
|
||||||
} else {
|
|
||||||
g_strfreev(def);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
prefs_set_string(preference_t pref, char* value)
|
prefs_set_string(preference_t pref, char* value)
|
||||||
{
|
{
|
||||||
@ -1925,7 +1897,6 @@ _get_group(preference_t pref)
|
|||||||
return PREF_GROUP_PLUGINS;
|
return PREF_GROUP_PLUGINS;
|
||||||
case PREF_OMEMO_LOG:
|
case PREF_OMEMO_LOG:
|
||||||
case PREF_OMEMO_POLICY:
|
case PREF_OMEMO_POLICY:
|
||||||
case PREF_OMEMO_SENDFILE:
|
|
||||||
return PREF_GROUP_OMEMO;
|
return PREF_GROUP_OMEMO;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2172,8 +2143,6 @@ _get_key(preference_t pref)
|
|||||||
return "log";
|
return "log";
|
||||||
case PREF_OMEMO_POLICY:
|
case PREF_OMEMO_POLICY:
|
||||||
return "policy";
|
return "policy";
|
||||||
case PREF_OMEMO_SENDFILE:
|
|
||||||
return "sendfile";
|
|
||||||
case PREF_CORRECTION_ALLOW:
|
case PREF_CORRECTION_ALLOW:
|
||||||
return "correction.allow";
|
return "correction.allow";
|
||||||
case PREF_AVATAR_CMD:
|
case PREF_AVATAR_CMD:
|
||||||
@ -2321,26 +2290,10 @@ _get_default_string(preference_t pref)
|
|||||||
return "false";
|
return "false";
|
||||||
case PREF_AVATAR_CMD:
|
case PREF_AVATAR_CMD:
|
||||||
return "xdg-open";
|
return "xdg-open";
|
||||||
case PREF_URL_SAVE_CMD:
|
|
||||||
return "curl -o %p %u";
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the default setting for a string list type preference
|
|
||||||
// if it is not specified in .profrc
|
|
||||||
static char**
|
|
||||||
_get_default_string_list(preference_t pref)
|
|
||||||
{
|
|
||||||
char** str_array = NULL;
|
|
||||||
|
|
||||||
switch (pref) {
|
|
||||||
case PREF_URL_OPEN_CMD:
|
case PREF_URL_OPEN_CMD:
|
||||||
str_array = g_malloc0(3);
|
return "xdg-open %u";
|
||||||
str_array[0] = g_strdup("false");
|
case PREF_URL_SAVE_CMD:
|
||||||
str_array[1] = g_strdup("xdg-open %u");
|
return NULL; // Default to built-in method.
|
||||||
return str_array;
|
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,6 @@ typedef enum {
|
|||||||
PREF_STATUSBAR_ROOM,
|
PREF_STATUSBAR_ROOM,
|
||||||
PREF_OMEMO_LOG,
|
PREF_OMEMO_LOG,
|
||||||
PREF_OMEMO_POLICY,
|
PREF_OMEMO_POLICY,
|
||||||
PREF_OMEMO_SENDFILE,
|
|
||||||
PREF_OCCUPANTS_WRAP,
|
PREF_OCCUPANTS_WRAP,
|
||||||
PREF_CORRECTION_ALLOW,
|
PREF_CORRECTION_ALLOW,
|
||||||
PREF_AVATAR_CMD,
|
PREF_AVATAR_CMD,
|
||||||
@ -321,7 +320,6 @@ gboolean prefs_get_boolean(preference_t pref);
|
|||||||
void prefs_set_boolean(preference_t pref, gboolean value);
|
void prefs_set_boolean(preference_t pref, gboolean value);
|
||||||
char* prefs_get_string(preference_t pref);
|
char* prefs_get_string(preference_t pref);
|
||||||
char* prefs_get_string_with_option(preference_t pref, gchar* option);
|
char* prefs_get_string_with_option(preference_t pref, gchar* option);
|
||||||
gchar** prefs_get_string_list_with_option(preference_t pref, gchar* option);
|
|
||||||
void prefs_set_string(preference_t pref, char* value);
|
void prefs_set_string(preference_t pref, char* value);
|
||||||
void prefs_set_string_with_option(preference_t pref, char* option, char* value);
|
void prefs_set_string_with_option(preference_t pref, char* option, char* value);
|
||||||
void prefs_set_string_list_with_option(preference_t pref, char* option, const gchar* const* values);
|
void prefs_set_string_list_with_option(preference_t pref, char* option, const gchar* const* values);
|
||||||
|
@ -35,12 +35,14 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <signal/signal_protocol.h>
|
#include <signal/signal_protocol.h>
|
||||||
#include <signal/signal_protocol_types.h>
|
#include <signal/signal_protocol_types.h>
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "omemo/omemo.h"
|
#include "omemo/omemo.h"
|
||||||
#include "omemo/crypto.h"
|
#include "omemo/crypto.h"
|
||||||
|
|
||||||
|
#define AES256_GCM_TAG_LENGTH 16
|
||||||
|
#define AES256_GCM_BUFFER_SIZE 1024
|
||||||
|
|
||||||
int
|
int
|
||||||
omemo_crypto_init(void)
|
omemo_crypto_init(void)
|
||||||
{
|
{
|
||||||
@ -373,3 +375,110 @@ out:
|
|||||||
gcry_cipher_close(hd);
|
gcry_cipher_close(hd);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gcry_error_t
|
||||||
|
aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[], bool encrypt)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
|
||||||
|
fputs("libgcrypt has not been initialized\n", stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encrypt) {
|
||||||
|
file_size -= AES256_GCM_TAG_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcry_error_t res;
|
||||||
|
gcry_cipher_hd_t hd;
|
||||||
|
|
||||||
|
res = gcry_cipher_open(&hd, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_GCM,
|
||||||
|
GCRY_CIPHER_SECURE);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = gcry_cipher_setkey(hd, key, OMEMO_AESGCM_KEY_LENGTH);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = gcry_cipher_setiv(hd, nonce, OMEMO_AESGCM_NONCE_LENGTH);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char buffer[AES256_GCM_BUFFER_SIZE];
|
||||||
|
|
||||||
|
int bytes = 0;
|
||||||
|
off_t bytes_read = 0, bytes_available = 0, read_size = 0;
|
||||||
|
while (bytes_read < file_size) {
|
||||||
|
bytes_available = file_size - bytes_read;
|
||||||
|
if (!bytes_available || ferror(in) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_available < AES256_GCM_BUFFER_SIZE) {
|
||||||
|
read_size = bytes_available;
|
||||||
|
gcry_cipher_final(hd); // Signal last round of bytes.
|
||||||
|
} else {
|
||||||
|
read_size = AES256_GCM_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = fread(buffer, 1, read_size, in);
|
||||||
|
bytes_read += bytes;
|
||||||
|
|
||||||
|
if (encrypt) {
|
||||||
|
res = gcry_cipher_encrypt(hd, buffer, bytes, NULL, 0);
|
||||||
|
} else {
|
||||||
|
res = gcry_cipher_decrypt(hd, buffer, bytes, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(buffer, 1, bytes, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char tag[AES256_GCM_TAG_LENGTH];
|
||||||
|
|
||||||
|
if (encrypt) {
|
||||||
|
// Append authentication tag at the end of the file.
|
||||||
|
res = gcry_cipher_gettag(hd, tag, AES256_GCM_TAG_LENGTH);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(tag, 1, AES256_GCM_TAG_LENGTH, out);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Read and verify authentication tag stored at the end of the file.
|
||||||
|
bytes = fread(tag, 1, AES256_GCM_TAG_LENGTH, in);
|
||||||
|
res = gcry_cipher_checktag(hd, tag, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_cipher_close(hd);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
aes256gcm_create_secure_fragment(unsigned char* key, unsigned char* nonce)
|
||||||
|
{
|
||||||
|
int key_size = OMEMO_AESGCM_KEY_LENGTH;
|
||||||
|
int nonce_size = OMEMO_AESGCM_NONCE_LENGTH;
|
||||||
|
|
||||||
|
char* fragment = gcry_malloc_secure((nonce_size + key_size) * 2 + 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < nonce_size; i++) {
|
||||||
|
sprintf(&(fragment[i * 2]), "%02x", nonce[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < key_size; i++) {
|
||||||
|
sprintf(&(fragment[(i + nonce_size) * 2]), "%02x", key[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
@ -32,7 +32,10 @@
|
|||||||
* source files in the program, then also delete it here.
|
* source files in the program, then also delete it here.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <signal/signal_protocol_types.h>
|
#include <signal/signal_protocol_types.h>
|
||||||
|
#include <gcrypt.h>
|
||||||
|
|
||||||
#define AES128_GCM_KEY_LENGTH 16
|
#define AES128_GCM_KEY_LENGTH 16
|
||||||
#define AES128_GCM_IV_LENGTH 12
|
#define AES128_GCM_IV_LENGTH 12
|
||||||
@ -180,3 +183,9 @@ int aes128gcm_decrypt(unsigned char* plaintext,
|
|||||||
size_t* plaintext_len, const unsigned char* const ciphertext,
|
size_t* plaintext_len, const unsigned char* const ciphertext,
|
||||||
size_t ciphertext_len, const unsigned char* const iv, size_t iv_len,
|
size_t ciphertext_len, const unsigned char* const iv, size_t iv_len,
|
||||||
const unsigned char* const key, const unsigned char* const tag);
|
const unsigned char* const key, const unsigned char* const tag);
|
||||||
|
|
||||||
|
gcry_error_t aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[], bool encrypt);
|
||||||
|
|
||||||
|
char* aes256gcm_create_secure_fragment(unsigned char* key,
|
||||||
|
unsigned char* nonce);
|
||||||
|
@ -45,7 +45,6 @@
|
|||||||
#include <signal/signal_protocol.h>
|
#include <signal/signal_protocol.h>
|
||||||
#include <signal/session_builder.h>
|
#include <signal/session_builder.h>
|
||||||
#include <signal/session_cipher.h>
|
#include <signal/session_cipher.h>
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
#include "config/account.h"
|
#include "config/account.h"
|
||||||
#include "config/files.h"
|
#include "config/files.h"
|
||||||
@ -62,6 +61,9 @@
|
|||||||
#include "xmpp/roster_list.h"
|
#include "xmpp/roster_list.h"
|
||||||
#include "xmpp/xmpp.h"
|
#include "xmpp/xmpp.h"
|
||||||
|
|
||||||
|
#define AESGCM_URL_NONCE_LEN (2 * OMEMO_AESGCM_NONCE_LENGTH)
|
||||||
|
#define AESGCM_URL_KEY_LEN (2 * OMEMO_AESGCM_KEY_LENGTH)
|
||||||
|
|
||||||
static gboolean loaded;
|
static gboolean loaded;
|
||||||
|
|
||||||
static void _generate_pre_keys(int count);
|
static void _generate_pre_keys(int count);
|
||||||
@ -1653,3 +1655,134 @@ _generate_signed_pre_key(void)
|
|||||||
signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key);
|
signal_protocol_signed_pre_key_store_key(omemo_ctx.store, signed_pre_key);
|
||||||
SIGNAL_UNREF(signed_pre_key);
|
SIGNAL_UNREF(signed_pre_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
omemo_free(void* a)
|
||||||
|
{
|
||||||
|
gcry_free(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res)
|
||||||
|
{
|
||||||
|
unsigned char* key = gcry_random_bytes_secure(
|
||||||
|
OMEMO_AESGCM_KEY_LENGTH,
|
||||||
|
GCRY_VERY_STRONG_RANDOM);
|
||||||
|
|
||||||
|
// Create nonce/IV with random bytes.
|
||||||
|
unsigned char nonce[OMEMO_AESGCM_NONCE_LENGTH];
|
||||||
|
gcry_create_nonce(nonce, OMEMO_AESGCM_NONCE_LENGTH);
|
||||||
|
|
||||||
|
char* fragment = aes256gcm_create_secure_fragment(key, nonce);
|
||||||
|
*gcry_res = aes256gcm_crypt_file(in, out, file_size, key, nonce, true);
|
||||||
|
|
||||||
|
if (*gcry_res != GPG_ERR_NO_ERROR) {
|
||||||
|
gcry_free(fragment);
|
||||||
|
fragment = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcry_free(key);
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_bytes_from_hex(const char* hex, size_t hex_size,
|
||||||
|
unsigned char* bytes, size_t bytes_size)
|
||||||
|
{
|
||||||
|
const unsigned char ht[] = {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
|
||||||
|
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
|
||||||
|
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
|
||||||
|
};
|
||||||
|
const size_t ht_size = sizeof(ht);
|
||||||
|
|
||||||
|
unsigned char b0;
|
||||||
|
unsigned char b1;
|
||||||
|
|
||||||
|
memset(bytes, 0, bytes_size);
|
||||||
|
|
||||||
|
for (int i = 0; (i < hex_size) && (i / 2 < bytes_size); i += 2) {
|
||||||
|
b0 = ((unsigned char)hex[i + 0] & 0x1f) ^ 0x10;
|
||||||
|
b1 = ((unsigned char)hex[i + 1] & 0x1f) ^ 0x10;
|
||||||
|
|
||||||
|
if (b0 <= ht_size && b1 <= ht_size) {
|
||||||
|
bytes[i / 2] = (unsigned char)(ht[b0] << 4) | ht[b1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gcry_error_t
|
||||||
|
omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment)
|
||||||
|
{
|
||||||
|
char nonce_hex[AESGCM_URL_NONCE_LEN];
|
||||||
|
char key_hex[AESGCM_URL_KEY_LEN];
|
||||||
|
|
||||||
|
const int nonce_pos = 0;
|
||||||
|
const int key_pos = AESGCM_URL_NONCE_LEN;
|
||||||
|
|
||||||
|
memcpy(nonce_hex, &(fragment[nonce_pos]), AESGCM_URL_NONCE_LEN);
|
||||||
|
memcpy(key_hex, &(fragment[key_pos]), AESGCM_URL_KEY_LEN);
|
||||||
|
|
||||||
|
unsigned char nonce[OMEMO_AESGCM_NONCE_LENGTH];
|
||||||
|
unsigned char* key = gcry_malloc_secure(OMEMO_AESGCM_KEY_LENGTH);
|
||||||
|
|
||||||
|
_bytes_from_hex(nonce_hex, AESGCM_URL_NONCE_LEN,
|
||||||
|
nonce, OMEMO_AESGCM_NONCE_LENGTH);
|
||||||
|
_bytes_from_hex(key_hex, AESGCM_URL_KEY_LEN,
|
||||||
|
key, OMEMO_AESGCM_KEY_LENGTH);
|
||||||
|
|
||||||
|
gcry_error_t crypt_res;
|
||||||
|
crypt_res = aes256gcm_crypt_file(in, out, file_size, key, nonce, false);
|
||||||
|
|
||||||
|
gcry_free(key);
|
||||||
|
|
||||||
|
return crypt_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
omemo_parse_aesgcm_url(const char* aesgcm_url,
|
||||||
|
char** https_url,
|
||||||
|
char** fragment)
|
||||||
|
{
|
||||||
|
CURLUcode ret;
|
||||||
|
CURLU* url = curl_url();
|
||||||
|
|
||||||
|
// Required to allow for the "aesgcm://" scheme that OMEMO Media Sharing
|
||||||
|
// uses.
|
||||||
|
unsigned int curl_flags = CURLU_NON_SUPPORT_SCHEME;
|
||||||
|
|
||||||
|
ret = curl_url_set(url, CURLUPART_URL, aesgcm_url, curl_flags);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_url_get(url, CURLUPART_FRAGMENT, fragment, curl_flags);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(*fragment) != AESGCM_URL_NONCE_LEN + AESGCM_URL_KEY_LEN) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear fragment from HTTPS URL as it's not required for download.
|
||||||
|
ret = curl_url_set(url, CURLUPART_FRAGMENT, NULL, curl_flags);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_url_set(url, CURLUPART_SCHEME, "https", curl_flags);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_url_get(url, CURLUPART_URL, https_url, curl_flags);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
curl_url_cleanup(url);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <gcrypt.h>
|
||||||
|
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "config/account.h"
|
#include "config/account.h"
|
||||||
@ -40,6 +41,10 @@
|
|||||||
#define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000
|
#define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000
|
||||||
#define OMEMO_ERR_GCRYPT -20000
|
#define OMEMO_ERR_GCRYPT -20000
|
||||||
|
|
||||||
|
#define OMEMO_AESGCM_NONCE_LENGTH AES128_GCM_IV_LENGTH
|
||||||
|
#define OMEMO_AESGCM_KEY_LENGTH 32
|
||||||
|
#define OMEMO_AESGCM_URL_SCHEME "aesgcm"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PROF_OMEMOPOLICY_MANUAL,
|
PROF_OMEMOPOLICY_MANUAL,
|
||||||
PROF_OMEMOPOLICY_AUTOMATIC,
|
PROF_OMEMOPOLICY_AUTOMATIC,
|
||||||
@ -95,3 +100,8 @@ void omemo_start_device_session(const char* const jid, uint32_t device_id, GList
|
|||||||
gboolean omemo_loaded(void);
|
gboolean omemo_loaded(void);
|
||||||
char* omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_receipt, gboolean muc, const char* const replace_id);
|
char* omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_receipt, gboolean muc, const char* const replace_id);
|
||||||
char* omemo_on_message_recv(const char* const from, uint32_t sid, const unsigned char* const iv, size_t iv_len, GList* keys, const unsigned char* const payload, size_t payload_len, gboolean muc, gboolean* trusted);
|
char* omemo_on_message_recv(const char* const from, uint32_t sid, const unsigned char* const iv, size_t iv_len, GList* keys, const unsigned char* const payload, size_t payload_len, gboolean muc, gboolean* trusted);
|
||||||
|
|
||||||
|
char* omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res);
|
||||||
|
gcry_error_t omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment);
|
||||||
|
void omemo_free(void* a);
|
||||||
|
int omemo_parse_aesgcm_url(const char* aesgcm_url, char** https_url, char** fragment);
|
||||||
|
190
src/tools/aesgcm_download.c
Normal file
190
src/tools/aesgcm_download.c
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* aesgcm_download.c
|
||||||
|
* vim: expandtab:ts=4:sts=4:sw=4
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
|
*
|
||||||
|
* 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 <https://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 "config.h"
|
||||||
|
|
||||||
|
#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 <errno.h>
|
||||||
|
|
||||||
|
#include "profanity.h"
|
||||||
|
#include "event/client_events.h"
|
||||||
|
#include "tools/http_common.h"
|
||||||
|
#include "tools/aesgcm_download.h"
|
||||||
|
#include "omemo/omemo.h"
|
||||||
|
#include "config/preferences.h"
|
||||||
|
#include "ui/ui.h"
|
||||||
|
#include "ui/window.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define FALLBACK_MSG ""
|
||||||
|
|
||||||
|
void*
|
||||||
|
aesgcm_file_get(void* userdata)
|
||||||
|
{
|
||||||
|
AESGCMDownload* aesgcm_dl = (AESGCMDownload*)userdata;
|
||||||
|
|
||||||
|
char* https_url = NULL;
|
||||||
|
char* fragment = NULL;
|
||||||
|
|
||||||
|
// Convert the aesgcm:// URL to a https:// URL and extract the encoded key
|
||||||
|
// and tag stored in the URL fragment.
|
||||||
|
if (omemo_parse_aesgcm_url(aesgcm_dl->url, &https_url, &fragment) != 0) {
|
||||||
|
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
|
||||||
|
"Download failed: Cannot parse URL '%s'.",
|
||||||
|
aesgcm_dl->url);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a temporary file used for storing the ciphertext that is to be
|
||||||
|
// retrieved from the https:// URL.
|
||||||
|
gchar* tmpname = NULL;
|
||||||
|
gint tmpfd;
|
||||||
|
if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) {
|
||||||
|
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
|
||||||
|
"Downloading '%s' failed: Unable to create "
|
||||||
|
"temporary ciphertext file for writing "
|
||||||
|
"(%s).",
|
||||||
|
https_url, g_strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the target file for storing the cleartext.
|
||||||
|
FILE* outfh = fopen(aesgcm_dl->filename, "wb");
|
||||||
|
if (outfh == NULL) {
|
||||||
|
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
|
||||||
|
"Downloading '%s' failed: Unable to open "
|
||||||
|
"output file at '%s' for writing (%s).",
|
||||||
|
https_url, aesgcm_dl->filename,
|
||||||
|
g_strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap the HTTPDownload tool and use it for retrieving the ciphertext
|
||||||
|
// and storing it in the temporary file previously opened.
|
||||||
|
HTTPDownload* http_dl = malloc(sizeof(HTTPDownload));
|
||||||
|
http_dl->window = aesgcm_dl->window;
|
||||||
|
http_dl->worker = aesgcm_dl->worker;
|
||||||
|
http_dl->url = strdup(https_url);
|
||||||
|
http_dl->filename = strdup(tmpname);
|
||||||
|
http_dl->cmd_template = NULL;
|
||||||
|
aesgcm_dl->http_dl = http_dl;
|
||||||
|
|
||||||
|
http_file_get(http_dl); // TODO(wstrm): Verify result.
|
||||||
|
|
||||||
|
FILE* tmpfh = fopen(tmpname, "rb");
|
||||||
|
if (tmpfh == NULL) {
|
||||||
|
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
|
||||||
|
"Downloading '%s' failed: Unable to open "
|
||||||
|
"temporary file at '%s' for reading (%s).",
|
||||||
|
aesgcm_dl->url, tmpname,
|
||||||
|
g_strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcry_error_t crypt_res;
|
||||||
|
crypt_res = omemo_decrypt_file(tmpfh, outfh,
|
||||||
|
http_dl->bytes_received, fragment);
|
||||||
|
|
||||||
|
if (fclose(tmpfh) == EOF) {
|
||||||
|
cons_show_error(g_strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
close(tmpfd);
|
||||||
|
remove(tmpname);
|
||||||
|
g_free(tmpname);
|
||||||
|
|
||||||
|
if (crypt_res != GPG_ERR_NO_ERROR) {
|
||||||
|
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
|
||||||
|
"Downloading '%s' failed: Failed to decrypt "
|
||||||
|
"file (%s).",
|
||||||
|
https_url, gcry_strerror(crypt_res));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose(outfh) == EOF) {
|
||||||
|
cons_show_error(g_strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(https_url);
|
||||||
|
free(fragment);
|
||||||
|
|
||||||
|
if (aesgcm_dl->cmd_template != NULL) {
|
||||||
|
gchar** argv = format_call_external_argv(aesgcm_dl->cmd_template,
|
||||||
|
aesgcm_dl->filename,
|
||||||
|
aesgcm_dl->filename);
|
||||||
|
|
||||||
|
// TODO: Log the error.
|
||||||
|
if (!call_external(argv, NULL, NULL)) {
|
||||||
|
http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
|
||||||
|
"Downloading '%s' failed: Unable to call "
|
||||||
|
"command '%s' with file at '%s' (%s).",
|
||||||
|
aesgcm_dl->url,
|
||||||
|
aesgcm_dl->cmd_template,
|
||||||
|
aesgcm_dl->filename,
|
||||||
|
"TODO: Log the error");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(argv);
|
||||||
|
free(aesgcm_dl->cmd_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(aesgcm_dl->filename);
|
||||||
|
free(aesgcm_dl->url);
|
||||||
|
free(aesgcm_dl);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
aesgcm_download_cancel_processes(ProfWin* window)
|
||||||
|
{
|
||||||
|
http_download_cancel_processes(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
aesgcm_download_add_download(AESGCMDownload* aesgcm_dl)
|
||||||
|
{
|
||||||
|
http_download_add_download(aesgcm_dl->http_dl);
|
||||||
|
}
|
66
src/tools/aesgcm_download.h
Normal file
66
src/tools/aesgcm_download.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* aesgcm_download.h
|
||||||
|
* vim: expandtab:ts=4:sts=4:sw=4
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
|
*
|
||||||
|
* 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 <https://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_AESGCM_DOWNLOAD_H
|
||||||
|
#define TOOLS_AESGCM_DOWNLOAD_H
|
||||||
|
|
||||||
|
#ifdef PLATFORM_CYGWIN
|
||||||
|
#define SOCKET int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include "tools/http_common.h"
|
||||||
|
#include "tools/http_download.h"
|
||||||
|
|
||||||
|
#include "ui/win_types.h"
|
||||||
|
|
||||||
|
typedef struct aesgcm_download_t
|
||||||
|
{
|
||||||
|
char* url;
|
||||||
|
char* filename;
|
||||||
|
char* cmd_template;
|
||||||
|
ProfWin* window;
|
||||||
|
pthread_t worker;
|
||||||
|
HTTPDownload* http_dl;
|
||||||
|
} AESGCMDownload;
|
||||||
|
|
||||||
|
void* aesgcm_file_get(void* userdata);
|
||||||
|
|
||||||
|
void aesgcm_download_cancel_processes(ProfWin* window);
|
||||||
|
void aesgcm_download_add_download(AESGCMDownload* download);
|
||||||
|
|
||||||
|
#endif
|
75
src/tools/http_common.c
Normal file
75
src/tools/http_common.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* http_common.c
|
||||||
|
* vim: expandtab:ts=4:sts=4:sw=4
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
|
*
|
||||||
|
* 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 <https://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 <gio/gio.h>
|
||||||
|
|
||||||
|
#include "tools/http_common.h"
|
||||||
|
|
||||||
|
#define FALLBACK_MSG ""
|
||||||
|
|
||||||
|
void
|
||||||
|
http_print_transfer_update(ProfWin* window, char* url, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
GString* msg = g_string_new(FALLBACK_MSG);
|
||||||
|
g_string_vprintf(msg, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
win_update_entry_message(window, url, msg->str);
|
||||||
|
|
||||||
|
g_string_free(msg, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
http_print_transfer(ProfWin* window, char* url, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
GString* msg = g_string_new(FALLBACK_MSG);
|
||||||
|
g_string_vprintf(msg, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
win_print_http_transfer(window, msg->str, url);
|
||||||
|
|
||||||
|
g_string_free(msg, TRUE);
|
||||||
|
}
|
44
src/tools/http_common.h
Normal file
44
src/tools/http_common.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* http_common.h
|
||||||
|
* vim: expandtab:ts=4:sts=4:sw=4
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
|
*
|
||||||
|
* 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 <https://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_COMMON_H
|
||||||
|
#define TOOLS_HTTP_COMMON_H
|
||||||
|
|
||||||
|
#include "ui/window.h"
|
||||||
|
|
||||||
|
void http_print_transfer(ProfWin* window, char* url, const char* fmt, ...);
|
||||||
|
void http_print_transfer_update(ProfWin* window, char* url, const char* fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
236
src/tools/http_download.c
Normal file
236
src/tools/http_download.c
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* http_download.c
|
||||||
|
* vim: expandtab:ts=4:sts=4:sw=4
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
|
*
|
||||||
|
* 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 <https://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 "config.h"
|
||||||
|
|
||||||
|
#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 <errno.h>
|
||||||
|
|
||||||
|
#include "profanity.h"
|
||||||
|
#include "event/client_events.h"
|
||||||
|
#include "tools/http_download.h"
|
||||||
|
#include "config/preferences.h"
|
||||||
|
#include "ui/ui.h"
|
||||||
|
#include "ui/window.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
GSList* download_processes = NULL;
|
||||||
|
|
||||||
|
static int
|
||||||
|
_xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||||
|
{
|
||||||
|
HTTPDownload* download = (HTTPDownload*)userdata;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
|
||||||
|
if (download->cancel) {
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (download->bytes_received == dlnow) {
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
download->bytes_received = dlnow;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int dlperc = 0;
|
||||||
|
if (dltotal != 0) {
|
||||||
|
dlperc = (100 * dlnow) / dltotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_print_transfer_update(download->window, download->url,
|
||||||
|
"Downloading '%s': %d%%", download->url, dlperc);
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
void*
|
||||||
|
http_file_get(void* userdata)
|
||||||
|
{
|
||||||
|
HTTPDownload* download = (HTTPDownload*)userdata;
|
||||||
|
|
||||||
|
char* err = NULL;
|
||||||
|
|
||||||
|
CURL* curl;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
download->cancel = 0;
|
||||||
|
download->bytes_received = 0;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
http_print_transfer(download->window, download->url,
|
||||||
|
"Downloading '%s': 0%%", download->url);
|
||||||
|
|
||||||
|
FILE* outfh = fopen(download->filename, "wb");
|
||||||
|
if (outfh == NULL) {
|
||||||
|
http_print_transfer_update(download->window, download->url,
|
||||||
|
"Downloading '%s' failed: Unable to open "
|
||||||
|
"output file at '%s' for writing (%s).",
|
||||||
|
download->url, download->filename,
|
||||||
|
g_strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, download->url);
|
||||||
|
|
||||||
|
#if LIBCURL_VERSION_NUM >= 0x072000
|
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, _xferinfo);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, download);
|
||||||
|
#else
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, _older_progress);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, download);
|
||||||
|
#endif
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)outfh);
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
|
||||||
|
|
||||||
|
if (cert_path) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CAPATH, cert_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res = curl_easy_perform(curl)) != CURLE_OK) {
|
||||||
|
err = strdup(curl_easy_strerror(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
curl_global_cleanup();
|
||||||
|
|
||||||
|
if (fclose(outfh) == EOF) {
|
||||||
|
err = strdup(g_strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
g_free(cert_path);
|
||||||
|
if (err) {
|
||||||
|
if (download->cancel) {
|
||||||
|
http_print_transfer_update(download->window, download->url,
|
||||||
|
"Downloading '%s' failed: "
|
||||||
|
"Download was canceled",
|
||||||
|
download->url);
|
||||||
|
} else {
|
||||||
|
http_print_transfer_update(download->window, download->url,
|
||||||
|
"Downloading '%s' failed: %s",
|
||||||
|
download->url, err);
|
||||||
|
}
|
||||||
|
free(err);
|
||||||
|
} else {
|
||||||
|
if (!download->cancel) {
|
||||||
|
http_print_transfer_update(download->window, download->url,
|
||||||
|
"Downloading '%s': done",
|
||||||
|
download->url);
|
||||||
|
win_mark_received(download->window, download->url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
download_processes = g_slist_remove(download_processes, download);
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
|
||||||
|
if (download->cmd_template != NULL) {
|
||||||
|
gchar** argv = format_call_external_argv(download->cmd_template,
|
||||||
|
download->url,
|
||||||
|
download->filename);
|
||||||
|
|
||||||
|
// TODO: Log the error.
|
||||||
|
if (!call_external(argv, NULL, NULL)) {
|
||||||
|
http_print_transfer_update(download->window, download->url,
|
||||||
|
"Downloading '%s' failed: Unable to call "
|
||||||
|
"command '%s' with file at '%s' (%s).",
|
||||||
|
download->url,
|
||||||
|
download->cmd_template,
|
||||||
|
download->filename,
|
||||||
|
"TODO: Log the error");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(argv);
|
||||||
|
free(download->cmd_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(download->url);
|
||||||
|
free(download->filename);
|
||||||
|
free(download);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
http_download_cancel_processes(ProfWin* window)
|
||||||
|
{
|
||||||
|
GSList* download_process = download_processes;
|
||||||
|
while (download_process) {
|
||||||
|
HTTPDownload* download = download_process->data;
|
||||||
|
if (download->window == window) {
|
||||||
|
download->cancel = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
download_process = g_slist_next(download_process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
http_download_add_download(HTTPDownload* download)
|
||||||
|
{
|
||||||
|
download_processes = g_slist_append(download_processes, download);
|
||||||
|
}
|
66
src/tools/http_download.h
Normal file
66
src/tools/http_download.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* http_download.h
|
||||||
|
* vim: expandtab:ts=4:sts=4:sw=4
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||||
|
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||||
|
*
|
||||||
|
* 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 <https://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_DOWNLOAD_H
|
||||||
|
#define TOOLS_HTTP_DOWNLOAD_H
|
||||||
|
|
||||||
|
#ifdef PLATFORM_CYGWIN
|
||||||
|
#define SOCKET int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "ui/win_types.h"
|
||||||
|
#include "tools/http_common.h"
|
||||||
|
|
||||||
|
typedef struct http_download_t
|
||||||
|
{
|
||||||
|
char* url;
|
||||||
|
char* filename;
|
||||||
|
char* cmd_template;
|
||||||
|
curl_off_t bytes_received;
|
||||||
|
ProfWin* window;
|
||||||
|
pthread_t worker;
|
||||||
|
int cancel;
|
||||||
|
} HTTPDownload;
|
||||||
|
|
||||||
|
void* http_file_get(void* userdata);
|
||||||
|
|
||||||
|
void http_download_cancel_processes(ProfWin* window);
|
||||||
|
void http_download_add_download(HTTPDownload* download);
|
||||||
|
|
||||||
|
#endif
|
@ -128,12 +128,41 @@ _data_callback(void* ptr, size_t size, size_t nmemb, void* data)
|
|||||||
return realsize;
|
return realsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
format_alt_url(char* original_url, char* new_scheme, char* new_fragment, char** new_url)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
CURLU* h = curl_url();
|
||||||
|
|
||||||
|
if ((ret = curl_url_set(h, CURLUPART_URL, original_url, 0)) != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_scheme != NULL) {
|
||||||
|
if ((ret = curl_url_set(h, CURLUPART_SCHEME, new_scheme, CURLU_NON_SUPPORT_SCHEME)) != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_fragment != NULL) {
|
||||||
|
if ((ret = curl_url_set(h, CURLUPART_FRAGMENT, new_fragment, 0)) != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_url_get(h, CURLUPART_URL, new_url, 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
curl_url_cleanup(h);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
http_file_put(void* userdata)
|
http_file_put(void* userdata)
|
||||||
{
|
{
|
||||||
HTTPUpload* upload = (HTTPUpload*)userdata;
|
HTTPUpload* upload = (HTTPUpload*)userdata;
|
||||||
|
|
||||||
FILE* fd = NULL;
|
FILE* fh = NULL;
|
||||||
|
|
||||||
char* err = NULL;
|
char* err = NULL;
|
||||||
char* content_type_header;
|
char* content_type_header;
|
||||||
@ -149,7 +178,7 @@ http_file_put(void* userdata)
|
|||||||
if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) {
|
if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) {
|
||||||
msg = strdup(FALLBACK_MSG);
|
msg = strdup(FALLBACK_MSG);
|
||||||
}
|
}
|
||||||
win_print_http_upload(upload->window, msg, upload->put_url);
|
win_print_http_transfer(upload->window, msg, upload->put_url);
|
||||||
free(msg);
|
free(msg);
|
||||||
|
|
||||||
char* cert_path = prefs_get_string(PREF_TLS_CERTPATH);
|
char* cert_path = prefs_get_string(PREF_TLS_CERTPATH);
|
||||||
@ -186,18 +215,13 @@ http_file_put(void* userdata)
|
|||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
|
||||||
|
|
||||||
if (!(fd = fopen(upload->filename, "rb"))) {
|
fh = upload->filehandle;
|
||||||
if (asprintf(&err, "failed to open '%s'", upload->filename) == -1) {
|
|
||||||
err = NULL;
|
|
||||||
}
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cert_path) {
|
if (cert_path) {
|
||||||
curl_easy_setopt(curl, CURLOPT_CAPATH, cert_path);
|
curl_easy_setopt(curl, CURLOPT_CAPATH, cert_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_READDATA, fd);
|
curl_easy_setopt(curl, CURLOPT_READDATA, fh);
|
||||||
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(upload->filesize));
|
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(upload->filesize));
|
||||||
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
||||||
|
|
||||||
@ -225,12 +249,11 @@ http_file_put(void* userdata)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
curl_slist_free_all(headers);
|
curl_slist_free_all(headers);
|
||||||
if (fd) {
|
if (fh) {
|
||||||
fclose(fd);
|
fclose(fh);
|
||||||
}
|
}
|
||||||
free(content_type_header);
|
free(content_type_header);
|
||||||
free(output.buffer);
|
free(output.buffer);
|
||||||
@ -262,31 +285,43 @@ end:
|
|||||||
win_mark_received(upload->window, upload->put_url);
|
win_mark_received(upload->window, upload->put_url);
|
||||||
free(msg);
|
free(msg);
|
||||||
|
|
||||||
|
char* url = NULL;
|
||||||
|
if (format_alt_url(upload->get_url, upload->alt_scheme, upload->alt_fragment, &url) != 0) {
|
||||||
|
char* msg;
|
||||||
|
if (asprintf(&msg, "Uploading '%s' failed: Bad URL ('%s')", upload->filename, upload->get_url) == -1) {
|
||||||
|
msg = strdup(FALLBACK_MSG);
|
||||||
|
}
|
||||||
|
cons_show_error(msg);
|
||||||
|
free(msg);
|
||||||
|
} else {
|
||||||
switch (upload->window->type) {
|
switch (upload->window->type) {
|
||||||
case WIN_CHAT:
|
case WIN_CHAT:
|
||||||
{
|
{
|
||||||
ProfChatWin* chatwin = (ProfChatWin*)(upload->window);
|
ProfChatWin* chatwin = (ProfChatWin*)(upload->window);
|
||||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||||
cl_ev_send_msg(chatwin, upload->get_url, upload->get_url);
|
cl_ev_send_msg(chatwin, url, url);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIN_PRIVATE:
|
case WIN_PRIVATE:
|
||||||
{
|
{
|
||||||
ProfPrivateWin* privatewin = (ProfPrivateWin*)(upload->window);
|
ProfPrivateWin* privatewin = (ProfPrivateWin*)(upload->window);
|
||||||
assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
|
assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
|
||||||
cl_ev_send_priv_msg(privatewin, upload->get_url, upload->get_url);
|
cl_ev_send_priv_msg(privatewin, url, url);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIN_MUC:
|
case WIN_MUC:
|
||||||
{
|
{
|
||||||
ProfMucWin* mucwin = (ProfMucWin*)(upload->window);
|
ProfMucWin* mucwin = (ProfMucWin*)(upload->window);
|
||||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
||||||
cl_ev_send_muc_msg(mucwin, upload->get_url, upload->get_url);
|
cl_ev_send_muc_msg(mucwin, url, url);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curl_free(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,24 +332,26 @@ end:
|
|||||||
free(upload->mime_type);
|
free(upload->mime_type);
|
||||||
free(upload->get_url);
|
free(upload->get_url);
|
||||||
free(upload->put_url);
|
free(upload->put_url);
|
||||||
|
free(upload->alt_scheme);
|
||||||
|
free(upload->alt_fragment);
|
||||||
free(upload);
|
free(upload);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char*
|
char*
|
||||||
file_mime_type(const char* const file_name)
|
file_mime_type(const char* const filename)
|
||||||
{
|
{
|
||||||
char* out_mime_type;
|
char* out_mime_type;
|
||||||
char file_header[FILE_HEADER_BYTES];
|
char file_header[FILE_HEADER_BYTES];
|
||||||
FILE* fd;
|
FILE* fh;
|
||||||
if (!(fd = fopen(file_name, "rb"))) {
|
if (!(fh = fopen(filename, "rb"))) {
|
||||||
return strdup(FALLBACK_MIMETYPE);
|
return strdup(FALLBACK_MIMETYPE);
|
||||||
}
|
}
|
||||||
size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fd);
|
size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fh);
|
||||||
fclose(fd);
|
fclose(fh);
|
||||||
|
|
||||||
char* content_type = g_content_type_guess(file_name, (unsigned char*)file_header, file_header_size, NULL);
|
char* content_type = g_content_type_guess(filename, (unsigned char*)file_header, file_header_size, NULL);
|
||||||
if (content_type != NULL) {
|
if (content_type != NULL) {
|
||||||
char* mime_type = g_content_type_get_mime_type(content_type);
|
char* mime_type = g_content_type_get_mime_type(content_type);
|
||||||
out_mime_type = strdup(mime_type);
|
out_mime_type = strdup(mime_type);
|
||||||
@ -327,10 +364,10 @@ file_mime_type(const char* const file_name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
file_size(const char* const filename)
|
file_size(int filedes)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
stat(filename, &st);
|
fstat(filedes, &st);
|
||||||
return st.st_size;
|
return st.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +48,14 @@
|
|||||||
typedef struct http_upload_t
|
typedef struct http_upload_t
|
||||||
{
|
{
|
||||||
char* filename;
|
char* filename;
|
||||||
|
FILE* filehandle;
|
||||||
off_t filesize;
|
off_t filesize;
|
||||||
curl_off_t bytes_sent;
|
curl_off_t bytes_sent;
|
||||||
char* mime_type;
|
char* mime_type;
|
||||||
char* get_url;
|
char* get_url;
|
||||||
char* put_url;
|
char* put_url;
|
||||||
|
char* alt_scheme;
|
||||||
|
char* alt_fragment;
|
||||||
ProfWin* window;
|
ProfWin* window;
|
||||||
pthread_t worker;
|
pthread_t worker;
|
||||||
int cancel;
|
int cancel;
|
||||||
@ -60,8 +63,8 @@ typedef struct http_upload_t
|
|||||||
|
|
||||||
void* http_file_put(void* userdata);
|
void* http_file_put(void* userdata);
|
||||||
|
|
||||||
char* file_mime_type(const char* const file_name);
|
char* file_mime_type(const char* const filename);
|
||||||
off_t file_size(const char* const file_name);
|
off_t file_size(int filedes);
|
||||||
|
|
||||||
void http_upload_cancel_processes(ProfWin* window);
|
void http_upload_cancel_processes(ProfWin* window);
|
||||||
void http_upload_add_upload(HTTPUpload* upload);
|
void http_upload_add_upload(HTTPUpload* upload);
|
||||||
|
@ -2068,17 +2068,20 @@ cons_correction_setting(void)
|
|||||||
void
|
void
|
||||||
cons_executable_setting(void)
|
cons_executable_setting(void)
|
||||||
{
|
{
|
||||||
char* avatar = prefs_get_string(PREF_AVATAR_CMD);
|
gchar* avatar = prefs_get_string(PREF_AVATAR_CMD);
|
||||||
cons_show("Default '/avatar open' command (/executable avatar) : %s", avatar);
|
cons_show("Default '/avatar open' command (/executable avatar) : %s", avatar);
|
||||||
g_free(avatar);
|
g_free(avatar);
|
||||||
|
|
||||||
//TODO: there needs to be a way to get all the "locales"/schemes so we can
|
//TODO: there needs to be a way to get all the "locales"/schemes so we can
|
||||||
//display the defualt openers for all filetypes
|
//display the default openers for all filetypes
|
||||||
gchar** urlopen = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, "");
|
gchar* urlopen = prefs_get_string(PREF_URL_OPEN_CMD);
|
||||||
cons_show("Default '/url open' command (/executable urlopen) : %s", urlopen[1]);
|
cons_show("Default '/url open' command (/executable urlopen) : %s", urlopen);
|
||||||
g_strfreev(urlopen);
|
g_free(urlopen);
|
||||||
|
|
||||||
char* urlsave = prefs_get_string(PREF_URL_SAVE_CMD);
|
gchar* urlsave = prefs_get_string(PREF_URL_SAVE_CMD);
|
||||||
|
if (urlsave == NULL) {
|
||||||
|
urlsave = g_strdup("(built-in)");
|
||||||
|
}
|
||||||
cons_show("Default '/url save' command (/executable urlsave) : %s", urlsave);
|
cons_show("Default '/url save' command (/executable urlsave) : %s", urlsave);
|
||||||
g_free(urlsave);
|
g_free(urlsave);
|
||||||
}
|
}
|
||||||
@ -2192,12 +2195,6 @@ cons_show_omemo_prefs(void)
|
|||||||
cons_show("OMEMO char (/omemo char) : %s", ch);
|
cons_show("OMEMO char (/omemo char) : %s", ch);
|
||||||
free(ch);
|
free(ch);
|
||||||
|
|
||||||
if (prefs_get_boolean(PREF_OMEMO_SENDFILE)) {
|
|
||||||
cons_show("Allow sending unencrypted files in an OMEMO session via /sendfile (/omemo sendfile): ON");
|
|
||||||
} else {
|
|
||||||
cons_show("Allow sending unencrypted files in an OMEMO session via /sendfile (/omemo sendfile): OFF");
|
|
||||||
}
|
|
||||||
|
|
||||||
cons_alert(NULL);
|
cons_alert(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1390,7 +1390,7 @@ win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
win_print_http_upload(ProfWin* window, const char* const message, char* url)
|
win_print_http_transfer(ProfWin* window, const char* const message, char* url)
|
||||||
{
|
{
|
||||||
win_print_outgoing_with_receipt(window, "!", NULL, message, url, NULL);
|
win_print_outgoing_with_receipt(window, "!", NULL, message, url, NULL);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ void win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, c
|
|||||||
void win_print_outgoing_muc_msg(ProfWin* window, char* show_char, const char* const me, const char* const id, const char* const replace_id, const char* const message);
|
void win_print_outgoing_muc_msg(ProfWin* window, char* show_char, const char* const me, const char* const id, const char* const replace_id, const char* const message);
|
||||||
void win_print_history(ProfWin* window, const ProfMessage* const message);
|
void win_print_history(ProfWin* window, const ProfMessage* const message);
|
||||||
|
|
||||||
void win_print_http_upload(ProfWin* window, const char* const message, char* url);
|
void win_print_http_transfer(ProfWin* window, const char* const message, char* url);
|
||||||
|
|
||||||
void win_newline(ProfWin* window);
|
void win_newline(ProfWin* window);
|
||||||
void win_redraw(ProfWin* window);
|
void win_redraw(ProfWin* window);
|
||||||
|
@ -107,3 +107,10 @@ void
|
|||||||
omemo_start_sessions(void)
|
omemo_start_sessions(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
void omemo_free(void* a){};
|
||||||
|
@ -330,6 +330,186 @@ strip_quotes_strips_both(void** state)
|
|||||||
free(result);
|
free(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char* template;
|
||||||
|
char* url;
|
||||||
|
char* filename;
|
||||||
|
char* argv;
|
||||||
|
} format_call_external_argv_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
format_call_external_argv_td(void** state)
|
||||||
|
{
|
||||||
|
|
||||||
|
enum table { num_tests = 4 };
|
||||||
|
|
||||||
|
format_call_external_argv_t tests[num_tests] = {
|
||||||
|
(format_call_external_argv_t){
|
||||||
|
.template = "/bin/echo %u %p",
|
||||||
|
.url = "https://example.org",
|
||||||
|
.filename = "image.jpeg",
|
||||||
|
.argv = "/bin/echo https://example.org image.jpeg",
|
||||||
|
},
|
||||||
|
(format_call_external_argv_t){
|
||||||
|
.template = "/bin/echo %p %u",
|
||||||
|
.url = "https://example.org",
|
||||||
|
.filename = "image.jpeg",
|
||||||
|
.argv = "/bin/echo image.jpeg https://example.org",
|
||||||
|
},
|
||||||
|
(format_call_external_argv_t){
|
||||||
|
.template = "/bin/echo %p",
|
||||||
|
.url = "https://example.org",
|
||||||
|
.filename = "image.jpeg",
|
||||||
|
.argv = "/bin/echo image.jpeg",
|
||||||
|
},
|
||||||
|
(format_call_external_argv_t){
|
||||||
|
.template = "/bin/echo %u",
|
||||||
|
.url = "https://example.org",
|
||||||
|
.filename = "image.jpeg",
|
||||||
|
.argv = "/bin/echo https://example.org",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
gchar** got_argv = NULL;
|
||||||
|
gchar* got_argv_str = NULL;
|
||||||
|
for (int i = 0; i < num_tests; i++) {
|
||||||
|
got_argv = format_call_external_argv(
|
||||||
|
tests[i].template,
|
||||||
|
tests[i].url,
|
||||||
|
tests[i].filename);
|
||||||
|
got_argv_str = g_strjoinv(" ", got_argv);
|
||||||
|
|
||||||
|
assert_string_equal(got_argv_str, tests[i].argv);
|
||||||
|
|
||||||
|
g_strfreev(got_argv);
|
||||||
|
g_free(got_argv_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char* url;
|
||||||
|
char* path;
|
||||||
|
char* target;
|
||||||
|
char* basename;
|
||||||
|
} unique_filename_from_url_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
unique_filename_from_url_td(void** state)
|
||||||
|
{
|
||||||
|
|
||||||
|
enum table { num_tests = 15 };
|
||||||
|
char* pwd = g_get_current_dir();
|
||||||
|
|
||||||
|
unique_filename_from_url_t tests[num_tests] = {
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg",
|
||||||
|
.path = "./.",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg",
|
||||||
|
.path = NULL,
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg#somefragment",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg?query=param",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg?query=param&another=one",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg?query=param&another=one",
|
||||||
|
.path = "/tmp/",
|
||||||
|
.target = "/tmp/",
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg?query=param&another=one",
|
||||||
|
.path = "/tmp/hopefully/this/file/does/not/exist",
|
||||||
|
.target = "/tmp/hopefully/this/file/does/not/",
|
||||||
|
.basename = "exist",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/image.jpeg?query=param&another=one",
|
||||||
|
.path = "/tmp/hopefully/this/file/does/not/exist/",
|
||||||
|
.target = "/tmp/hopefully/this/file/does/not/exist/",
|
||||||
|
.basename = "image.jpeg",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/images/",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "images",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/images/../../file",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "file",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/images/../../file/..",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "index",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/images/..//",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "index",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test/",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "index",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "https://host.test",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "index",
|
||||||
|
},
|
||||||
|
(unique_filename_from_url_t){
|
||||||
|
.url = "aesgcm://host.test",
|
||||||
|
.path = "./",
|
||||||
|
.target = pwd,
|
||||||
|
.basename = "index",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
char* got_filename = NULL;
|
||||||
|
char* exp_filename = NULL;
|
||||||
|
for (int i = 0; i < num_tests; i++) {
|
||||||
|
got_filename = unique_filename_from_url(tests[i].url, tests[i].path);
|
||||||
|
exp_filename = g_build_filename(tests[i].target, tests[i].basename, NULL);
|
||||||
|
|
||||||
|
assert_string_equal(got_filename, exp_filename);
|
||||||
|
|
||||||
|
free(got_filename);
|
||||||
|
free(exp_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(pwd);
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
_lists_equal(GSList* a, GSList* b)
|
_lists_equal(GSList* a, GSList* b)
|
||||||
{
|
{
|
||||||
|
@ -31,3 +31,5 @@ void strip_quotes_strips_last(void** state);
|
|||||||
void strip_quotes_strips_both(void** state);
|
void strip_quotes_strips_both(void** state);
|
||||||
void prof_partial_occurrences_tests(void** state);
|
void prof_partial_occurrences_tests(void** state);
|
||||||
void prof_whole_occurrences_tests(void** state);
|
void prof_whole_occurrences_tests(void** state);
|
||||||
|
void unique_filename_from_url_td(void** state);
|
||||||
|
void format_call_external_argv_td(void** state);
|
||||||
|
27
tests/unittests/tools/stub_aesgcm_download.c
Normal file
27
tests/unittests/tools/stub_aesgcm_download.c
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef TOOLS_AESGCM_DOWNLOAD_H
|
||||||
|
#define TOOLS_AESGCM_DOWNLOAD_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
typedef struct prof_win_t ProfWin;
|
||||||
|
typedef struct http_download_t HTTPDownload;
|
||||||
|
|
||||||
|
typedef struct aesgcm_download_t
|
||||||
|
{
|
||||||
|
char* url;
|
||||||
|
char* filename;
|
||||||
|
ProfWin* window;
|
||||||
|
pthread_t worker;
|
||||||
|
HTTPDownload* http_dl;
|
||||||
|
} AESGCMDownload;
|
||||||
|
|
||||||
|
void*
|
||||||
|
aesgcm_file_get(void* userdata)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
void aesgcm_download_cancel_processes(ProfWin* window){};
|
||||||
|
void aesgcm_download_add_download(AESGCMDownload* download){};
|
||||||
|
|
||||||
|
#endif
|
30
tests/unittests/tools/stub_http_download.c
Normal file
30
tests/unittests/tools/stub_http_download.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef TOOLS_HTTP_DOWNLOAD_H
|
||||||
|
#define TOOLS_HTTP_DOWNLOAD_H
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
typedef struct prof_win_t ProfWin;
|
||||||
|
|
||||||
|
typedef struct http_download_t
|
||||||
|
{
|
||||||
|
char* url;
|
||||||
|
char* filename;
|
||||||
|
char* directory;
|
||||||
|
FILE* filehandle;
|
||||||
|
curl_off_t bytes_received;
|
||||||
|
ProfWin* window;
|
||||||
|
pthread_t worker;
|
||||||
|
int cancel;
|
||||||
|
} HTTPDownload;
|
||||||
|
|
||||||
|
void*
|
||||||
|
http_file_get(void* userdata)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_download_cancel_processes(){};
|
||||||
|
void http_download_add_download(){};
|
||||||
|
|
||||||
|
#endif
|
@ -20,8 +20,6 @@ typedef struct http_upload_t
|
|||||||
int cancel;
|
int cancel;
|
||||||
} HTTPUpload;
|
} HTTPUpload;
|
||||||
|
|
||||||
//GSList *upload_processes;
|
|
||||||
|
|
||||||
void*
|
void*
|
||||||
http_file_put(void* userdata)
|
http_file_put(void* userdata)
|
||||||
{
|
{
|
||||||
@ -33,6 +31,7 @@ file_mime_type(const char* const file_name)
|
|||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
file_size(const char* const file_name)
|
file_size(const char* const file_name)
|
||||||
{
|
{
|
||||||
|
@ -471,6 +471,10 @@ mucwin_unset_message_char(ProfMucWin* mucwin)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void win_update_entry_message(ProfWin* window, const char* const id, const char* const message){};
|
||||||
|
void win_mark_received(ProfWin* window, const char* const id){};
|
||||||
|
void win_print_http_transfer(ProfWin* window, const char* const message, char* url){};
|
||||||
|
|
||||||
void
|
void
|
||||||
ui_show_roster(void)
|
ui_show_roster(void)
|
||||||
{
|
{
|
||||||
|
@ -90,6 +90,8 @@ main(int argc, char* argv[])
|
|||||||
unit_test(strip_quotes_strips_first),
|
unit_test(strip_quotes_strips_first),
|
||||||
unit_test(strip_quotes_strips_last),
|
unit_test(strip_quotes_strips_last),
|
||||||
unit_test(strip_quotes_strips_both),
|
unit_test(strip_quotes_strips_both),
|
||||||
|
unit_test(format_call_external_argv_td),
|
||||||
|
unit_test(unique_filename_from_url_td),
|
||||||
|
|
||||||
unit_test(clear_empty),
|
unit_test(clear_empty),
|
||||||
unit_test(reset_after_create),
|
unit_test(reset_after_create),
|
||||||
|
Loading…
Reference in New Issue
Block a user