From 3370418d71de255c832da97113543e554ec0e86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Thu, 11 Jun 2020 22:50:36 +0200 Subject: [PATCH 01/31] Initial /sendfile OMEMO encryption --- src/command/cmd_funcs.c | 133 ++++++++++++++++++++++++++--------- src/omemo/crypto.c | 99 ++++++++++++++++++++++++++ src/omemo/crypto.h | 18 +++-- src/tools/http_upload.c | 20 +++--- src/tools/http_upload.h | 10 +-- src/xmpp/iq.c | 4 +- tests/unittests/test_omemo.c | 10 +++ tests/unittests/test_omemo.h | 2 + 8 files changed, 242 insertions(+), 54 deletions(-) create mode 100644 tests/unittests/test_omemo.c create mode 100644 tests/unittests/test_omemo.h diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 0837f630..c37cc51b 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -73,6 +73,7 @@ #include "plugins/plugins.h" #include "ui/ui.h" #include "ui/window_list.h" +#include "omemo/crypto.h" #include "xmpp/xmpp.h" #include "xmpp/connection.h" #include "xmpp/contact.h" @@ -4809,7 +4810,9 @@ gboolean cmd_sendfile(ProfWin* window, const char* const command, gchar** args) { jabber_conn_status_t conn_status = connection_get_status(); - char* filename = args[0]; + char *filename = args[0]; + char *filepath = NULL; + unsigned char *key = NULL; // expand ~ to $HOME if (filename[0] == '~' && filename[1] == '/') { @@ -4820,45 +4823,103 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) filename = strdup(filename); } + filepath = strdup(filename); + if (conn_status != JABBER_CONNECTED) { cons_show("You are not currently connected."); - free(filename); - return TRUE; + goto out; } 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; + goto out; } switch (window->type) { - case WIN_MUC: - { - ProfMucWin* mucwin = (ProfMucWin*)window; - assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + case WIN_MUC: + case WIN_CHAT: + { + ProfChatWin *chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_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; + if (chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) { + int tmpfd; + GError *err = NULL; + char *tmppath = NULL; + + tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmppath, &err); + if (err != NULL) { + cons_show_error("Unable to create temporary file for encrypted transfer."); + win_println(window, THEME_ERROR, "-", "Unable to create temporary file for encrypted transfer."); + goto out; + } + + struct stat tmpst; + if (fstat(tmpfd, &tmpst)) { + cons_show_error("Cannot determine file size."); + win_println(window, THEME_ERROR, "-", "Cannot determine file size."); + goto out; + } + + FILE *tmpfile = fdopen(tmpfd, "wb"); + if (tmpfile == NULL) { + cons_show_error("Unable to open temporary file."); + win_println(window, THEME_ERROR, "-", "Unable to open temporary file."); + goto out; + } + + FILE *infile = fopen(filepath, "rb"); + if (infile == NULL) { + cons_show_error("Unable to open file."); + win_println(window, THEME_ERROR, "-", "Unable to open file."); + close(tmpfd); + goto out; + } + + int crypt_res = GPG_ERR_NO_ERROR; + + // TODO(wstrm): Move these to omemo/crypto.c + unsigned char nonce[AES256_GCM_NONCE_LENGTH]; + key = gcry_malloc_secure(AES256_GCM_KEY_LENGTH); + if (key == NULL) { + cons_show_error("Cannot allocate secure memory for encryption."); + win_println(window, THEME_ERROR, "-", "Cannot allocate secure memory for encryption."); + goto out; + } + + key = gcry_random_bytes_secure(AES256_GCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM); + gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH); + + crypt_res = aes256gcm_encrypt_file(infile, tmpfile, tmpst.st_size, key, nonce); + + if (crypt_res != 0) { + cons_show_error("Failed to encrypt file."); + win_println(window, THEME_ERROR, "-", "Failed to encrypt file."); + goto out; + } + + free(filepath); + filepath = tmppath; + + break; + } + + 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 /omemo sendfile, /otr sendfile, /pgp sendfile."); + win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet."); + goto out; + } + break; } - 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; + case WIN_PRIVATE: + { + // We don't support encryption in private MUC windows. + break; + } + default: + cons_show_error("Unsupported window for file transmission."); + goto out; } break; } @@ -4875,14 +4936,12 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) if (access(filename, R_OK) != 0) { cons_show_error("Uploading '%s' failed: File not found!", filename); - free(filename); - return TRUE; + goto out; } if (!is_regular_file(filename)) { cons_show_error("Uploading '%s' failed: Not a file!", filename); - free(filename); - return TRUE; + goto out; } HTTPUpload* upload = malloc(sizeof(HTTPUpload)); @@ -4894,6 +4953,14 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) iq_http_upload_request(upload); +out: + if (key != NULL) + gcry_free(key); + if (filename != NULL) + free(filename); + if (filepath != NULL) + free(filepath); + return TRUE; } diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index 380551ad..6d6ba519 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -41,6 +41,9 @@ #include "omemo/omemo.h" #include "omemo/crypto.h" +#define AES256_GCM_TAG_LENGTH 16 +#define AES256_GCM_BUFFER_SIZE 1024 + int omemo_crypto_init(void) { @@ -373,3 +376,99 @@ out: gcry_cipher_close(hd); return res; } + +int 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, AES256_GCM_KEY_LENGTH); + if (res != GPG_ERR_NO_ERROR) { + goto out; + } + + res = gcry_cipher_setiv(hd, nonce, AES256_GCM_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) { + 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; +} + +int aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, + unsigned char key[], unsigned char nonce[]) { + return aes256gcm_crypt_file(in, out, file_size, key, nonce, true); +} + +int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, + unsigned char key[], unsigned char nonce[]) { + return aes256gcm_crypt_file(in, out, file_size, key, nonce, false); +} diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index 4fb6283e..916486b7 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -32,12 +32,16 @@ * source files in the program, then also delete it here. * */ +#include #include #define AES128_GCM_KEY_LENGTH 16 #define AES128_GCM_IV_LENGTH 12 #define AES128_GCM_TAG_LENGTH 16 +#define AES256_GCM_KEY_LENGTH 32 +#define AES256_GCM_NONCE_LENGTH 12 + int omemo_crypto_init(void); /** * Callback for a secure random number generator. @@ -176,7 +180,13 @@ int aes128gcm_encrypt(unsigned char* ciphertext, size_t* ciphertext_len, const unsigned char* const plaintext, size_t plaintext_len, const unsigned char* const iv, const unsigned char* const key); -int aes128gcm_decrypt(unsigned char* plaintext, - size_t* plaintext_len, const unsigned char* const ciphertext, - size_t ciphertext_len, const unsigned char* const iv, size_t iv_len, - const unsigned char* const key, const unsigned char* const tag); +int aes128gcm_decrypt(unsigned char *plaintext, + size_t *plaintext_len, const unsigned char *const ciphertext, + size_t ciphertext_len, const unsigned char *const iv, size_t iv_len, + const unsigned char *const key, const unsigned char *const tag); + +int aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, + unsigned char key[], unsigned char nonce[]); + +int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, + unsigned char key[], unsigned char nonce[]); diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 312fad46..68de57ed 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -146,7 +146,7 @@ http_file_put(void* userdata) pthread_mutex_lock(&lock); char* msg; - if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) { + if (asprintf(&msg, "Uploading '%s': 0%%", upload->filepath) == -1) { msg = strdup(FALLBACK_MSG); } win_print_http_upload(upload->window, msg, upload->put_url); @@ -186,8 +186,8 @@ http_file_put(void* userdata) curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity"); - if (!(fd = fopen(upload->filename, "rb"))) { - if (asprintf(&err, "failed to open '%s'", upload->filename) == -1) { + if (!(fd = fopen(upload->filepath, "rb"))) { + if (asprintf(&err, "failed to open '%s'", upload->filepath) == -1) { err = NULL; } goto end; @@ -294,6 +294,7 @@ end: pthread_mutex_unlock(&lock); free(upload->filename); + free(upload->filepath); free(upload->mime_type); free(upload->get_url); free(upload->put_url); @@ -303,18 +304,18 @@ end: } char* -file_mime_type(const char* const file_name) +file_mime_type(const char* const filepath) { char* out_mime_type; char file_header[FILE_HEADER_BYTES]; - FILE* fd; - if (!(fd = fopen(file_name, "rb"))) { + FILE *fd; + if (!(fd = fopen(filepath, "rb"))) { return strdup(FALLBACK_MIMETYPE); } size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fd); fclose(fd); - char* content_type = g_content_type_guess(file_name, (unsigned char*)file_header, file_header_size, NULL); + char *content_type = g_content_type_guess(filepath, (unsigned char*)file_header, file_header_size, NULL); if (content_type != NULL) { char* mime_type = g_content_type_get_mime_type(content_type); out_mime_type = strdup(mime_type); @@ -326,11 +327,10 @@ file_mime_type(const char* const file_name) return out_mime_type; } -off_t -file_size(const char* const filename) +off_t file_size(const char* const filepath) { struct stat st; - stat(filename, &st); + stat(filepath, &st); return st.st_size; } diff --git a/src/tools/http_upload.h b/src/tools/http_upload.h index 3838a5e8..cbab96ed 100644 --- a/src/tools/http_upload.h +++ b/src/tools/http_upload.h @@ -45,9 +45,9 @@ #include "ui/win_types.h" -typedef struct http_upload_t -{ - char* filename; +typedef struct http_upload_t { + char *filename; + char *filepath; off_t filesize; curl_off_t bytes_sent; char* mime_type; @@ -60,8 +60,8 @@ typedef struct http_upload_t void* http_file_put(void* userdata); -char* file_mime_type(const char* const file_name); -off_t file_size(const char* const file_name); +char* file_mime_type(const char* const filepath); +off_t file_size(const char* const filepath); void http_upload_cancel_processes(ProfWin* window); void http_upload_add_upload(HTTPUpload* upload); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 07acdf14..1c7d16e7 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -2401,9 +2401,9 @@ _http_upload_response_id_handler(xmpp_stanza_t* const stanza, void* const userda if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { char* error_message = stanza_get_error_message(stanza); if (from) { - cons_show_error("Uploading '%s' failed for %s: %s", upload->filename, from, error_message); + cons_show_error("Uploading '%s' failed for %s: %s", upload->filepath, from, error_message); } else { - cons_show_error("Uploading '%s' failed: %s", upload->filename, error_message); + cons_show_error("Uploading '%s' failed: %s", upload->filepath, error_message); } free(error_message); return 0; diff --git a/tests/unittests/test_omemo.c b/tests/unittests/test_omemo.c new file mode 100644 index 00000000..4c239e6c --- /dev/null +++ b/tests/unittests/test_omemo.c @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include + +#include "omemo/crypto.h" + +void test_omemo_aesgcm256_encrypt_file(void **state) {} +void test_omemo_aesgcm256_encrypt_file(void **state) {} diff --git a/tests/unittests/test_omemo.h b/tests/unittests/test_omemo.h new file mode 100644 index 00000000..b945121c --- /dev/null +++ b/tests/unittests/test_omemo.h @@ -0,0 +1,2 @@ +void test_omemo_aesgcm256_encrypt_file(void **state); +void test_omemo_aesgcm256_decrypt_file(void **state); From 39c32906131b3bb9383ed61be15ba0dd9d720898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sat, 27 Jun 2020 17:19:35 +0200 Subject: [PATCH 02/31] Refactor to use file stream --- src/command/cmd_funcs.c | 84 +++++++++++++++++++---------------------- src/tools/http_upload.c | 35 +++++++---------- src/tools/http_upload.h | 6 +-- src/xmpp/iq.c | 4 +- 4 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index c37cc51b..23f0b98d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4811,7 +4811,6 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) { jabber_conn_status_t conn_status = connection_get_status(); char *filename = args[0]; - char *filepath = NULL; unsigned char *key = NULL; // expand ~ to $HOME @@ -4823,7 +4822,15 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) filename = strdup(filename); } - filepath = strdup(filename); + if (access(filename, R_OK) != 0) { + cons_show_error("Uploading '%s' failed: File not found!", filename); + goto out; + } + + if (!is_regular_file(filename)) { + cons_show_error("Uploading '%s' failed: Not a file!", filename); + goto out; + } if (conn_status != JABBER_CONNECTED) { cons_show("You are not currently connected."); @@ -4835,6 +4842,14 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) 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: @@ -4844,37 +4859,14 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) if (chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) { int tmpfd; - GError *err = NULL; - char *tmppath = NULL; - - tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmppath, &err); - if (err != NULL) { + if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", NULL, NULL)) == -1) { cons_show_error("Unable to create temporary file for encrypted transfer."); win_println(window, THEME_ERROR, "-", "Unable to create temporary file for encrypted transfer."); + fclose(fh); goto out; } - struct stat tmpst; - if (fstat(tmpfd, &tmpst)) { - cons_show_error("Cannot determine file size."); - win_println(window, THEME_ERROR, "-", "Cannot determine file size."); - goto out; - } - - FILE *tmpfile = fdopen(tmpfd, "wb"); - if (tmpfile == NULL) { - cons_show_error("Unable to open temporary file."); - win_println(window, THEME_ERROR, "-", "Unable to open temporary file."); - goto out; - } - - FILE *infile = fopen(filepath, "rb"); - if (infile == NULL) { - cons_show_error("Unable to open file."); - win_println(window, THEME_ERROR, "-", "Unable to open file."); - close(tmpfd); - goto out; - } + FILE *tmpfh = fdopen(tmpfd, "wb"); int crypt_res = GPG_ERR_NO_ERROR; @@ -4884,22 +4876,33 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) if (key == NULL) { cons_show_error("Cannot allocate secure memory for encryption."); win_println(window, THEME_ERROR, "-", "Cannot allocate secure memory for encryption."); + fclose(fh); + fclose(tmpfh); goto out; } key = gcry_random_bytes_secure(AES256_GCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM); gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH); - crypt_res = aes256gcm_encrypt_file(infile, tmpfile, tmpst.st_size, key, nonce); + crypt_res = aes256gcm_encrypt_file(fh, tmpfh, file_size(fd), key, nonce); if (crypt_res != 0) { cons_show_error("Failed to encrypt file."); win_println(window, THEME_ERROR, "-", "Failed to encrypt file."); + fclose(fh); + fclose(tmpfh); goto out; } - free(filepath); - filepath = tmppath; + // Force flush as the upload will read from the same stream. + fflush(tmpfh); + rewind(tmpfh); + + fclose(fh); + + // Switch original stream with temporary encrypted stream. + fd = tmpfd; + fh = tmpfh; break; } @@ -4934,21 +4937,12 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) return TRUE; } - if (access(filename, R_OK) != 0) { - cons_show_error("Uploading '%s' failed: File not found!", filename); - goto out; - } - - if (!is_regular_file(filename)) { - cons_show_error("Uploading '%s' failed: Not a file!", filename); - goto out; - } - - HTTPUpload* upload = malloc(sizeof(HTTPUpload)); + HTTPUpload *upload = malloc(sizeof(HTTPUpload)); upload->window = window; - upload->filename = filename; - upload->filesize = file_size(filename); + upload->filename = strdup(filename); + upload->filehandle = fh; + upload->filesize = file_size(fd); upload->mime_type = file_mime_type(filename); iq_http_upload_request(upload); @@ -4958,8 +4952,6 @@ out: gcry_free(key); if (filename != NULL) free(filename); - if (filepath != NULL) - free(filepath); return TRUE; } diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 68de57ed..5f725851 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -133,7 +133,7 @@ http_file_put(void* userdata) { HTTPUpload* upload = (HTTPUpload*)userdata; - FILE* fd = NULL; + FILE *fh = NULL; char* err = NULL; char* content_type_header; @@ -146,7 +146,7 @@ http_file_put(void* userdata) pthread_mutex_lock(&lock); char* msg; - if (asprintf(&msg, "Uploading '%s': 0%%", upload->filepath) == -1) { + if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) { msg = strdup(FALLBACK_MSG); } win_print_http_upload(upload->window, msg, upload->put_url); @@ -186,18 +186,13 @@ http_file_put(void* userdata) curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity"); - if (!(fd = fopen(upload->filepath, "rb"))) { - if (asprintf(&err, "failed to open '%s'", upload->filepath) == -1) { - err = NULL; - } - goto end; - } + fh = upload->filehandle; if (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_UPLOAD, 1L); @@ -225,12 +220,11 @@ http_file_put(void* userdata) #endif } -end: curl_easy_cleanup(curl); curl_global_cleanup(); curl_slist_free_all(headers); - if (fd) { - fclose(fd); + if (fh) { + fclose(fh); } free(content_type_header); free(output.buffer); @@ -294,7 +288,6 @@ end: pthread_mutex_unlock(&lock); free(upload->filename); - free(upload->filepath); free(upload->mime_type); free(upload->get_url); free(upload->put_url); @@ -304,18 +297,18 @@ end: } char* -file_mime_type(const char* const filepath) +file_mime_type(const char* const filename) { char* out_mime_type; char file_header[FILE_HEADER_BYTES]; - FILE *fd; - if (!(fd = fopen(filepath, "rb"))) { + FILE *fh; + if (!(fh = fopen(filename, "rb"))) { return strdup(FALLBACK_MIMETYPE); } - size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fd); - fclose(fd); + size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fh); + fclose(fh); - char *content_type = g_content_type_guess(filepath, (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) { char* mime_type = g_content_type_get_mime_type(content_type); out_mime_type = strdup(mime_type); @@ -327,10 +320,10 @@ file_mime_type(const char* const filepath) return out_mime_type; } -off_t file_size(const char* const filepath) +off_t file_size(int filedes) { struct stat st; - stat(filepath, &st); + fstat(filedes, &st); return st.st_size; } diff --git a/src/tools/http_upload.h b/src/tools/http_upload.h index cbab96ed..5b3b4754 100644 --- a/src/tools/http_upload.h +++ b/src/tools/http_upload.h @@ -47,7 +47,7 @@ typedef struct http_upload_t { char *filename; - char *filepath; + FILE *filehandle; off_t filesize; curl_off_t bytes_sent; char* mime_type; @@ -60,8 +60,8 @@ typedef struct http_upload_t { void* http_file_put(void* userdata); -char* file_mime_type(const char* const filepath); -off_t file_size(const char* const filepath); +char* file_mime_type(const char* const filename); +off_t file_size(int filedes); void http_upload_cancel_processes(ProfWin* window); void http_upload_add_upload(HTTPUpload* upload); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 1c7d16e7..07acdf14 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -2401,9 +2401,9 @@ _http_upload_response_id_handler(xmpp_stanza_t* const stanza, void* const userda if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { char* error_message = stanza_get_error_message(stanza); if (from) { - cons_show_error("Uploading '%s' failed for %s: %s", upload->filepath, from, error_message); + cons_show_error("Uploading '%s' failed for %s: %s", upload->filename, from, error_message); } else { - cons_show_error("Uploading '%s' failed: %s", upload->filepath, error_message); + cons_show_error("Uploading '%s' failed: %s", upload->filename, error_message); } free(error_message); return 0; From e9d58757825e542fe4ec15ce350c0df4192ec29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sat, 27 Jun 2020 20:23:50 +0200 Subject: [PATCH 03/31] Reformat HTTP get URL to AESGCM scheme --- src/command/cmd_funcs.c | 32 ++++++++++++++- src/tools/http_upload.c | 89 +++++++++++++++++++++++++++++------------ src/tools/http_upload.h | 10 +++-- 3 files changed, 100 insertions(+), 31 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 23f0b98d..d16cd938 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -95,6 +95,12 @@ #ifdef HAVE_OMEMO #include "omemo/omemo.h" #include "xmpp/omemo.h" + +#define AESGCM_URL_SCHEME "aesgcm" +#define AESGCM_URL_NONCE_LEN 24 +#define AESGCM_URL_KEY_LEN 64 +#define AESGCM_URL_FRAGMENT_LEN \ + (size_t)(AESGCM_URL_NONCE_LEN + AESGCM_URL_KEY_LEN) #endif #ifdef HAVE_GTK @@ -4806,6 +4812,21 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args) return TRUE; } +char *create_aesgcm_fragment(unsigned char *key, int key_size, + unsigned char *nonce, int nonce_size) { + char fragment[(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 strdup(fragment); +} + gboolean cmd_sendfile(ProfWin* window, const char* const command, gchar** args) { @@ -4849,6 +4870,8 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } FILE *fh = fdopen(fd, "rb"); + char *alt_scheme = NULL; + char *alt_fragment = NULL; switch (window->type) { case WIN_MUC: @@ -4898,12 +4921,17 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) fflush(tmpfh); rewind(tmpfh); - fclose(fh); + fclose(fh); // Also closes descriptor. // Switch original stream with temporary encrypted stream. fd = tmpfd; fh = tmpfh; + alt_scheme = AESGCM_URL_SCHEME; + alt_fragment = create_aesgcm_fragment( + key, AES256_GCM_KEY_LENGTH, + nonce, AES256_GCM_NONCE_LENGTH); + break; } @@ -4944,6 +4972,8 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) upload->filehandle = fh; upload->filesize = file_size(fd); upload->mime_type = file_mime_type(filename); + upload->alt_scheme = alt_scheme; + upload->alt_fragment = alt_fragment; iq_http_upload_request(upload); diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 5f725851..69587c08 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -128,8 +128,35 @@ _data_callback(void* ptr, size_t size, size_t nmemb, void* data) return realsize; } -void* -http_file_put(void* userdata) +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 * +http_file_put(void *userdata) { HTTPUpload* upload = (HTTPUpload*)userdata; @@ -256,30 +283,40 @@ http_file_put(void* userdata) win_mark_received(upload->window, upload->put_url); free(msg); - switch (upload->window->type) { - case WIN_CHAT: - { - ProfChatWin* chatwin = (ProfChatWin*)(upload->window); - assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); - cl_ev_send_msg(chatwin, upload->get_url, upload->get_url); - break; - } - case WIN_PRIVATE: - { - ProfPrivateWin* privatewin = (ProfPrivateWin*)(upload->window); - assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK); - cl_ev_send_priv_msg(privatewin, upload->get_url, upload->get_url); - break; - } - case WIN_MUC: - { - ProfMucWin* mucwin = (ProfMucWin*)(upload->window); - assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); - cl_ev_send_muc_msg(mucwin, upload->get_url, upload->get_url); - break; - } - default: - break; + char *url = NULL; + if (format_alt_url(upload->get_url, upload->alt_scheme, upload->alt_fragment, &url) != 0) { + char *msg; + asprintf(&msg, "Uploading '%s' failed: Bad URL ('%s')", upload->filename, upload->get_url); + cons_show_error(msg); + free(msg); + } else { + switch (upload->window->type) { + case WIN_CHAT: + { + ProfChatWin *chatwin = (ProfChatWin*)(upload->window); + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + cl_ev_send_msg(chatwin, url, url); + break; + } + case WIN_PRIVATE: + { + ProfPrivateWin *privatewin = (ProfPrivateWin*)(upload->window); + assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK); + cl_ev_send_priv_msg(privatewin, url, url); + break; + } + case WIN_MUC: + { + ProfMucWin *mucwin = (ProfMucWin*)(upload->window); + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); + cl_ev_send_muc_msg(mucwin, url, url); + break; + } + default: + break; + } + + curl_free(url); } } } diff --git a/src/tools/http_upload.h b/src/tools/http_upload.h index 5b3b4754..9e801973 100644 --- a/src/tools/http_upload.h +++ b/src/tools/http_upload.h @@ -50,10 +50,12 @@ typedef struct http_upload_t { FILE *filehandle; off_t filesize; curl_off_t bytes_sent; - char* mime_type; - char* get_url; - char* put_url; - ProfWin* window; + char *mime_type; + char *get_url; + char *put_url; + char *alt_scheme; + char *alt_fragment; + ProfWin *window; pthread_t worker; int cancel; } HTTPUpload; From d5b1dc0eb6ee9c4caee6c73b9cf26133c875a1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 12:17:21 +0200 Subject: [PATCH 04/31] Move setup for AESGCM to omemo/crypto --- src/command/cmd_funcs.c | 71 ++++++++++------------------------------- src/omemo/crypto.c | 50 +++++++++++++++++++++++++---- src/omemo/crypto.h | 9 +++--- src/tools/http_upload.c | 2 ++ 4 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index d16cd938..74a21a09 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4812,27 +4812,13 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args) return TRUE; } -char *create_aesgcm_fragment(unsigned char *key, int key_size, - unsigned char *nonce, int nonce_size) { - char fragment[(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 strdup(fragment); -} - gboolean cmd_sendfile(ProfWin* window, const char* const command, gchar** args) { jabber_conn_status_t conn_status = connection_get_status(); char *filename = args[0]; - unsigned char *key = NULL; + char *alt_scheme = NULL; + char *alt_fragment = NULL; // expand ~ to $HOME if (filename[0] == '~' && filename[1] == '/') { @@ -4870,8 +4856,6 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } FILE *fh = fdopen(fd, "rb"); - char *alt_scheme = NULL; - char *alt_fragment = NULL; switch (window->type) { case WIN_MUC: @@ -4881,37 +4865,25 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); if (chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) { + + // Create temporary file for writing ciphertext. int tmpfd; if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", NULL, NULL)) == -1) { - cons_show_error("Unable to create temporary file for encrypted transfer."); - win_println(window, THEME_ERROR, "-", "Unable to create temporary file for encrypted transfer."); + char *msg = "Unable to create temporary file for encrypted transfer."; + cons_show_error(msg); + win_println(window, THEME_ERROR, "-", msg); fclose(fh); goto out; } - FILE *tmpfh = fdopen(tmpfd, "wb"); int crypt_res = GPG_ERR_NO_ERROR; - - // TODO(wstrm): Move these to omemo/crypto.c - unsigned char nonce[AES256_GCM_NONCE_LENGTH]; - key = gcry_malloc_secure(AES256_GCM_KEY_LENGTH); - if (key == NULL) { - cons_show_error("Cannot allocate secure memory for encryption."); - win_println(window, THEME_ERROR, "-", "Cannot allocate secure memory for encryption."); - fclose(fh); - fclose(tmpfh); - goto out; - } - - key = gcry_random_bytes_secure(AES256_GCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM); - gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH); - - crypt_res = aes256gcm_encrypt_file(fh, tmpfh, file_size(fd), key, nonce); - + alt_scheme = AESGCM_URL_SCHEME; + alt_fragment = aes256gcm_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); if (crypt_res != 0) { - cons_show_error("Failed to encrypt file."); - win_println(window, THEME_ERROR, "-", "Failed to encrypt file."); + char *msg = "Failed to encrypt file."; + cons_show_error(msg); + win_println(window, THEME_ERROR, "-", msg); fclose(fh); fclose(tmpfh); goto out; @@ -4927,11 +4899,6 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) fd = tmpfd; fh = tmpfh; - alt_scheme = AESGCM_URL_SCHEME; - alt_fragment = create_aesgcm_fragment( - key, AES256_GCM_KEY_LENGTH, - nonce, AES256_GCM_NONCE_LENGTH); - break; } @@ -4943,11 +4910,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } break; } - case WIN_PRIVATE: - { - // We don't support encryption in private MUC windows. - break; - } + case WIN_PRIVATE: // We don't support encryption in private MUC windows. default: cons_show_error("Unsupported window for file transmission."); goto out; @@ -4972,14 +4935,14 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) upload->filehandle = fh; upload->filesize = file_size(fd); upload->mime_type = file_mime_type(filename); - upload->alt_scheme = alt_scheme; - upload->alt_fragment = alt_fragment; + upload->alt_scheme = strdup(alt_scheme); + upload->alt_fragment = strdup(alt_fragment); iq_http_upload_request(upload); out: - if (key != NULL) - gcry_free(key); + if (alt_fragment != NULL) + aes256gcm_fragment_free(alt_fragment); if (filename != NULL) free(filename); diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index 6d6ba519..7dd3be10 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -463,12 +463,50 @@ out: return res; } -int aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, - unsigned char key[], unsigned char nonce[]) { - return aes256gcm_crypt_file(in, out, file_size, key, nonce, true); +char *aes256gcm_create_secure_fragment(unsigned char *key, unsigned char *nonce) { + int key_size = AES256_GCM_KEY_LENGTH; + int nonce_size = AES256_GCM_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; } -int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, - unsigned char key[], unsigned char nonce[]) { - return aes256gcm_crypt_file(in, out, file_size, key, nonce, false); +void aes256gcm_fragment_free(char *fragment) { + gcry_free(fragment); } + +char *aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res) { + unsigned char *key = gcry_random_bytes_secure( + AES256_GCM_KEY_LENGTH, + GCRY_VERY_STRONG_RANDOM); + + // Create nonce/IV with random bytes. + unsigned char nonce[AES256_GCM_NONCE_LENGTH]; + gcry_create_nonce(nonce, AES256_GCM_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; +} + +//int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, +// unsigned char key[], unsigned char nonce[]) { +// return aes256gcm_crypt_file(in, out, file_size, key, nonce, false); +//} diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index 916486b7..34cbb82c 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -185,8 +185,9 @@ int aes128gcm_decrypt(unsigned char *plaintext, size_t ciphertext_len, const unsigned char *const iv, size_t iv_len, const unsigned char *const key, const unsigned char *const tag); -int aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, - unsigned char key[], unsigned char nonce[]); +char *aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res); -int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, - unsigned char key[], unsigned char nonce[]); +//int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, +// unsigned char key[], unsigned char nonce[]); + +void aes256gcm_fragment_free(char *fragment); diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 69587c08..d9742e30 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -328,6 +328,8 @@ http_file_put(void *userdata) free(upload->mime_type); free(upload->get_url); free(upload->put_url); + free(upload->alt_scheme); + free(upload->alt_fragment); free(upload); return NULL; From fc6136ddf005553960257ae2b4a625116cf3513a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 12:20:52 +0200 Subject: [PATCH 05/31] Remove unused #define's and move URL scheme define to omemo/crypto.h --- src/command/cmd_funcs.c | 8 +------- src/omemo/crypto.h | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 74a21a09..0f74ff7c 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -95,12 +95,6 @@ #ifdef HAVE_OMEMO #include "omemo/omemo.h" #include "xmpp/omemo.h" - -#define AESGCM_URL_SCHEME "aesgcm" -#define AESGCM_URL_NONCE_LEN 24 -#define AESGCM_URL_KEY_LEN 64 -#define AESGCM_URL_FRAGMENT_LEN \ - (size_t)(AESGCM_URL_NONCE_LEN + AESGCM_URL_KEY_LEN) #endif #ifdef HAVE_GTK @@ -4878,7 +4872,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) FILE *tmpfh = fdopen(tmpfd, "wb"); int crypt_res = GPG_ERR_NO_ERROR; - alt_scheme = AESGCM_URL_SCHEME; + alt_scheme = AES256_GCM_URL_SCHEME; alt_fragment = aes256gcm_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); if (crypt_res != 0) { char *msg = "Failed to encrypt file."; diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index 34cbb82c..f24fa163 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -39,6 +39,7 @@ #define AES128_GCM_IV_LENGTH 12 #define AES128_GCM_TAG_LENGTH 16 +#define AES256_GCM_URL_SCHEME "aesgcm" #define AES256_GCM_KEY_LENGTH 32 #define AES256_GCM_NONCE_LENGTH 12 From e98644f631b516e38dd4142103356ceb31628aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 14:36:16 +0200 Subject: [PATCH 06/31] Add guards for OMEMO --- src/command/cmd_funcs.c | 22 ++++++++++++++++++---- src/tools/http_upload.c | 4 +++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 0f74ff7c..f000ae1e 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4858,6 +4858,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) ProfChatWin *chatwin = (ProfChatWin*)window; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); +#ifdef HAVE_OMEMO if (chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) { // Create temporary file for writing ciphertext. @@ -4871,7 +4872,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } FILE *tmpfh = fdopen(tmpfd, "wb"); - int crypt_res = GPG_ERR_NO_ERROR; + int crypt_res; alt_scheme = AES256_GCM_URL_SCHEME; alt_fragment = aes256gcm_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); if (crypt_res != 0) { @@ -4895,10 +4896,11 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) break; } +#endif 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 /omemo sendfile, /otr sendfile, /pgp 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; } @@ -4929,14 +4931,26 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) upload->filehandle = fh; upload->filesize = file_size(fd); upload->mime_type = file_mime_type(filename); - upload->alt_scheme = strdup(alt_scheme); - upload->alt_fragment = strdup(alt_fragment); + + 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); out: +#ifdef HAVE_OMEMO if (alt_fragment != NULL) aes256gcm_fragment_free(alt_fragment); +#endif if (filename != NULL) free(filename); diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index d9742e30..8d369f8a 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -286,7 +286,9 @@ http_file_put(void *userdata) char *url = NULL; if (format_alt_url(upload->get_url, upload->alt_scheme, upload->alt_fragment, &url) != 0) { char *msg; - asprintf(&msg, "Uploading '%s' failed: Bad URL ('%s')", upload->filename, upload->get_url); + 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 { From f4ab1ca9e75bbc635781c795089bf669ee08942d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 15:16:03 +0200 Subject: [PATCH 07/31] Move file encryption function to public header --- src/command/cmd_funcs.c | 7 +++--- src/omemo/crypto.c | 31 ----------------------- src/omemo/crypto.h | 11 ++++---- src/omemo/omemo.c | 32 ++++++++++++++++++++++++ src/omemo/omemo.h | 9 +++++-- tests/unittests/omemo/stub_omemo.c | 40 +++++++++--------------------- tests/unittests/test_omemo.c | 10 -------- tests/unittests/test_omemo.h | 2 -- 8 files changed, 59 insertions(+), 83 deletions(-) delete mode 100644 tests/unittests/test_omemo.c delete mode 100644 tests/unittests/test_omemo.h diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index f000ae1e..25d21264 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -73,7 +73,6 @@ #include "plugins/plugins.h" #include "ui/ui.h" #include "ui/window_list.h" -#include "omemo/crypto.h" #include "xmpp/xmpp.h" #include "xmpp/connection.h" #include "xmpp/contact.h" @@ -4873,8 +4872,8 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) FILE *tmpfh = fdopen(tmpfd, "wb"); int crypt_res; - alt_scheme = AES256_GCM_URL_SCHEME; - alt_fragment = aes256gcm_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); + alt_scheme = OMEMO_AESGCM_URL_SCHEME; + alt_fragment = omemo_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); if (crypt_res != 0) { char *msg = "Failed to encrypt file."; cons_show_error(msg); @@ -4949,7 +4948,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) out: #ifdef HAVE_OMEMO if (alt_fragment != NULL) - aes256gcm_fragment_free(alt_fragment); + omemo_free(alt_fragment); #endif if (filename != NULL) free(filename); diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index 7dd3be10..3be64636 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -479,34 +479,3 @@ char *aes256gcm_create_secure_fragment(unsigned char *key, unsigned char *nonce) return fragment; } - -void aes256gcm_fragment_free(char *fragment) { - gcry_free(fragment); -} - -char *aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res) { - unsigned char *key = gcry_random_bytes_secure( - AES256_GCM_KEY_LENGTH, - GCRY_VERY_STRONG_RANDOM); - - // Create nonce/IV with random bytes. - unsigned char nonce[AES256_GCM_NONCE_LENGTH]; - gcry_create_nonce(nonce, AES256_GCM_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; -} - -//int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, -// unsigned char key[], unsigned char nonce[]) { -// return aes256gcm_crypt_file(in, out, file_size, key, nonce, false); -//} diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index f24fa163..e8d91ecc 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -33,13 +33,13 @@ * */ #include +#include #include #define AES128_GCM_KEY_LENGTH 16 #define AES128_GCM_IV_LENGTH 12 #define AES128_GCM_TAG_LENGTH 16 -#define AES256_GCM_URL_SCHEME "aesgcm" #define AES256_GCM_KEY_LENGTH 32 #define AES256_GCM_NONCE_LENGTH 12 @@ -186,9 +186,8 @@ int aes128gcm_decrypt(unsigned char *plaintext, size_t ciphertext_len, const unsigned char *const iv, size_t iv_len, const unsigned char *const key, const unsigned char *const tag); -char *aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res); +int aes256gcm_crypt_file(FILE *in, FILE *out, off_t file_size, + unsigned char key[], unsigned char nonce[], bool encrypt); -//int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size, -// unsigned char key[], unsigned char nonce[]); - -void aes256gcm_fragment_free(char *fragment); +char *aes256gcm_create_secure_fragment(unsigned char *key, + unsigned char *nonce); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index c6c34ac1..c6f92e46 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1653,3 +1653,35 @@ _generate_signed_pre_key(void) signal_protocol_signed_pre_key_store_key(omemo_ctx.store, 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( + AES256_GCM_KEY_LENGTH, + GCRY_VERY_STRONG_RANDOM); + + // Create nonce/IV with random bytes. + unsigned char nonce[AES256_GCM_NONCE_LENGTH]; + gcry_create_nonce(nonce, AES256_GCM_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; +} + +//int omemo_decrypt_file(FILE *in, FILE *out, off_t file_size, +// unsigned char key[], unsigned char nonce[]) { +// return aes256gcm_crypt_file(in, out, file_size, key, nonce, false); +//} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index ecfc42d9..0bddd9cd 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,6 +40,8 @@ #define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000 #define OMEMO_ERR_GCRYPT -20000 +#define OMEMO_AESGCM_URL_SCHEME "aesgcm" + typedef enum { PROF_OMEMOPOLICY_MANUAL, PROF_OMEMOPOLICY_AUTOMATIC, @@ -93,5 +95,8 @@ void omemo_start_muc_sessions(const char* const roomjid); void omemo_start_device_session(const char* const jid, uint32_t device_id, GList* prekeys, uint32_t signed_prekey_id, const unsigned char* const signed_prekey, size_t signed_prekey_len, const unsigned char* const signature, size_t signature_len, const unsigned char* const identity_key, size_t identity_key_len); 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_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_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_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res); +void omemo_free(void *a); diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c index 448d5a0d..7cca34d5 100644 --- a/tests/unittests/omemo/stub_omemo.c +++ b/tests/unittests/omemo/stub_omemo.c @@ -79,31 +79,15 @@ omemo_own_fingerprint(gboolean formatted) return NULL; } -void -omemo_start_muc_sessions(const char* const roomjid) -{ -} -void -omemo_start_session(const char* const barejid) -{ -} -void -omemo_trust(const char* const jid, const char* const fingerprint_formatted) -{ -} -void -omemo_untrust(const char* const jid, const char* const fingerprint_formatted) -{ -} -void -omemo_devicelist_publish(GList* device_list) -{ -} -void -omemo_publish_crypto_materials(void) -{ -} -void -omemo_start_sessions(void) -{ -} +void omemo_start_muc_sessions(const char *const roomjid) {} +void omemo_start_session(const char *const barejid) {} +void omemo_trust(const char *const jid, const char *const fingerprint_formatted) {} +void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) {} +void omemo_devicelist_publish(GList *device_list) {} +void omemo_publish_crypto_materials(void) {} +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) {}; diff --git a/tests/unittests/test_omemo.c b/tests/unittests/test_omemo.c deleted file mode 100644 index 4c239e6c..00000000 --- a/tests/unittests/test_omemo.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include -#include -#include - -#include "omemo/crypto.h" - -void test_omemo_aesgcm256_encrypt_file(void **state) {} -void test_omemo_aesgcm256_encrypt_file(void **state) {} diff --git a/tests/unittests/test_omemo.h b/tests/unittests/test_omemo.h deleted file mode 100644 index b945121c..00000000 --- a/tests/unittests/test_omemo.h +++ /dev/null @@ -1,2 +0,0 @@ -void test_omemo_aesgcm256_encrypt_file(void **state); -void test_omemo_aesgcm256_decrypt_file(void **state); From 9d58472c8cb4dd9fffc5620cae681c064e32f7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 15:26:13 +0200 Subject: [PATCH 08/31] Remove /omemo sendfile --- src/command/cmd_ac.c | 13 - src/command/cmd_defs.c | 95 +++-- src/command/cmd_funcs.c | 17 +- src/command/cmd_funcs.h | 236 ++++++------ src/config/preferences.c | 779 +++++++++++++++++++-------------------- src/config/preferences.h | 1 - src/ui/console.c | 8 +- 7 files changed, 556 insertions(+), 593 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 3aeedc60..f7414bad 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -193,7 +193,6 @@ static Autocomplete otr_sendfile_ac; static Autocomplete omemo_ac; static Autocomplete omemo_log_ac; static Autocomplete omemo_policy_ac; -static Autocomplete omemo_sendfile_ac; #endif static Autocomplete connect_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, "policy"); autocomplete_add(omemo_ac, "char"); - autocomplete_add(omemo_ac, "sendfile"); omemo_log_ac = autocomplete_new(); 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, "automatic"); autocomplete_add(omemo_policy_ac, "always"); - - omemo_sendfile_ac = autocomplete_new(); - autocomplete_add(omemo_sendfile_ac, "on"); - autocomplete_add(omemo_sendfile_ac, "off"); #endif connect_property_ac = autocomplete_new(); @@ -1292,7 +1286,6 @@ cmd_ac_reset(ProfWin* window) autocomplete_reset(omemo_ac); autocomplete_reset(omemo_log_ac); autocomplete_reset(omemo_policy_ac); - autocomplete_reset(omemo_sendfile_ac); #endif autocomplete_reset(connect_property_ac); autocomplete_reset(tls_property_ac); @@ -1450,7 +1443,6 @@ cmd_ac_uninit(void) autocomplete_free(omemo_ac); autocomplete_free(omemo_log_ac); autocomplete_free(omemo_policy_ac); - autocomplete_free(omemo_sendfile_ac); #endif autocomplete_free(connect_property_ac); autocomplete_free(tls_property_ac); @@ -2510,11 +2502,6 @@ _omemo_autocomplete(ProfWin* window, const char* const input, gboolean previous) 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(); if (conn_status == JABBER_CONNECTED) { diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 53c42bae..d291856c 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2274,55 +2274,52 @@ static struct cmd_t command_defs[] = { }, { "/omemo", - parse_args, 1, 3, NULL, - CMD_SUBFUNCS( - { "gen", cmd_omemo_gen }, - { "log", cmd_omemo_log }, - { "start", cmd_omemo_start }, - { "end", cmd_omemo_end }, - { "trust", cmd_omemo_trust }, - { "untrust", cmd_omemo_untrust }, - { "fingerprint", cmd_omemo_fingerprint }, - { "char", cmd_omemo_char }, - { "policy", cmd_omemo_policy }, - { "clear_device_list", cmd_omemo_clear_device_list }, - { "sendfile", cmd_omemo_sendfile }) - CMD_NOMAINFUNC - CMD_TAGS( - CMD_TAG_CHAT, - CMD_TAG_UI) - CMD_SYN( - "/omemo gen", - "/omemo log on|off|redact", - "/omemo start []", - "/omemo trust [] ", - "/omemo end", - "/omemo fingerprint []", - "/omemo char ", - "/omemo policy manual|automatic|always", - "/omemo sendfile on|off", - "/omemo clear_device_list") - CMD_DESC( - "OMEMO commands to manage keys, and perform encryption during chat sessions.") - CMD_ARGS( - { "gen", "Generate OMEMO crytographic materials for current account." }, - { "start []", "Start an OMEMO session with contact, or current recipient if omitted." }, - { "end", "End the current OMEMO session." }, - { "log on|off", "Enable or disable plaintext logging of OMEMO encrypted messages." }, - { "log redact", "Log OMEMO encrypted messages, but replace the contents with [redacted]. This is the default." }, - { "fingerprint []", "Show contact fingerprints, or current recipient if omitted." }, - { "char ", "Set the character to be displayed next to OMEMO encrypted messages." }, - { "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 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." }) - CMD_EXAMPLES( - "/omemo gen", - "/omemo start odin@valhalla.edda", - "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", - "/omemo untrust loki@valhalla.edda c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", - "/omemo char *") + parse_args, 1, 3, NULL, + CMD_SUBFUNCS( + { "gen", cmd_omemo_gen }, + { "log", cmd_omemo_log }, + { "start", cmd_omemo_start }, + { "end", cmd_omemo_end }, + { "trust", cmd_omemo_trust }, + { "untrust", cmd_omemo_untrust }, + { "fingerprint", cmd_omemo_fingerprint }, + { "char", cmd_omemo_char }, + { "policy", cmd_omemo_policy }, + { "clear_device_list", cmd_omemo_clear_device_list }) + CMD_NOMAINFUNC + CMD_TAGS( + CMD_TAG_CHAT, + CMD_TAG_UI) + CMD_SYN( + "/omemo gen", + "/omemo log on|off|redact", + "/omemo start []", + "/omemo trust [] ", + "/omemo end", + "/omemo fingerprint []", + "/omemo char ", + "/omemo policy manual|automatic|always", + "/omemo clear_device_list") + CMD_DESC( + "OMEMO commands to manage keys, and perform encryption during chat sessions.") + CMD_ARGS( + { "gen", "Generate OMEMO crytographic materials for current account." }, + { "start []", "Start an OMEMO session with contact, or current recipient if omitted." }, + { "end", "End the current OMEMO session." }, + { "log on|off", "Enable or disable plaintext logging of OMEMO encrypted messages." }, + { "log redact", "Log OMEMO encrypted messages, but replace the contents with [redacted]. This is the default." }, + { "fingerprint []", "Show contact fingerprints, or current recipient if omitted." }, + { "char ", "Set the character to be displayed next to OMEMO encrypted messages." }, + { "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 always", "Set the global OMEMO policy to always, an error will be displayed if an OMEMO session cannot be initiated upon starting a conversation." }, + { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}) + CMD_EXAMPLES( + "/omemo gen", + "/omemo start odin@valhalla.edda", + "/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", + "/omemo untrust loki@valhalla.edda c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a", + "/omemo char *") }, { "/save", diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 25d21264..113192cc 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4858,7 +4858,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); #ifdef HAVE_OMEMO - if (chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) { + if (chatwin->is_omemo) { // Create temporary file for writing ciphertext. int tmpfd; @@ -8832,20 +8832,7 @@ cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args) } 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 -cmd_save(ProfWin* window, const char* const command, gchar** args) +cmd_save(ProfWin *window, const char *const command, gchar **args) { log_info("Saving preferences to configuration file"); cons_show("Saving preferences."); diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 92c81364..f6fac7ca 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -111,133 +111,133 @@ gboolean cmd_pgp(ProfWin* window, const char* const command, gchar** args); #ifdef HAVE_LIBGPGME gboolean cmd_ox(ProfWin* window, const char* const command, gchar** args); #endif // HAVE_LIBGPGME -gboolean cmd_outtype(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_prefs(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_priority(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_quit(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_reconnect(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_room(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_rooms(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_bookmark(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_bookmark_ignore(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_roster(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_software(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_splash(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_states(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_status_get(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_status_set(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_sub(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_theme(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_wintitle(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_vercheck(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_who(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_win(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_alias(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_xmlconsole(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_ping(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_form(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_occupants(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_kick(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_ban(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_subject(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_affiliation(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_role(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_privileges(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_presence(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_wrap(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_time(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_resource(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_inpblock(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_titlebar(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_titlebar_show_hide(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_mainwin(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_statusbar(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_inputwin(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_script(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_export(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_charset(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_console(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_command_list(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_command_exec(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_outtype(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_prefs(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_priority(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_quit(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_reconnect(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_room(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_rooms(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_bookmark(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_bookmark_ignore(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_roster(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_software(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_splash(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_states(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_status_get(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_status_set(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_sub(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_theme(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_tiny(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_wintitle(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_vercheck(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_who(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_win(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_alias(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_xmlconsole(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_ping(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_form(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_occupants(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_kick(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_ban(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_subject(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_affiliation(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_role(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_privileges(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_presence(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_wrap(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_time(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_resource(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_inpblock(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_titlebar(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_titlebar_show_hide(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_mainwin(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_statusbar(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_inputwin(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_script(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_export(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_charset(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_console(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_command_list(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_command_exec(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_sourcepath(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_install(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_update(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_uninstall(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_load(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_unload(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_reload(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins_python_version(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_sourcepath(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_install(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_update(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_uninstall(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_load(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_unload(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_reload(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins_python_version(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_blocked(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_blocked(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_list(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_show(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_add(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_remove(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_enable(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_disable(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_rename(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_default(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_set(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account_clear(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_list(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_show(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_add(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_remove(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_enable(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_disable(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_rename(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_default(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_set(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account_clear(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_tls_certpath(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_tls_trust(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_tls_trusted(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_tls_revoke(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_tls_cert(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_tls_certpath(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_tls_trust(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_tls_trusted(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_tls_revoke(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_tls_cert(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_char(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_log(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_libver(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_policy(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_gen(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_myfp(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_theirfp(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_start(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_end(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_trust(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_untrust(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_secret(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_question(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_answer(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_sendfile(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_char(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_log(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_libver(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_policy(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_gen(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_myfp(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_theirfp(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_start(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_end(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_trust(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_untrust(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_secret(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_question(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_answer(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_sendfile(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_wins(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_wins_unread(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_wins_prune(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_wins_swap(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_wins(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_wins_unread(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_wins_prune(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_form_field(ProfWin* window, char* tag, gchar** args); +gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); -gboolean cmd_omemo_gen(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_omemo_char(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_omemo_log(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_omemo_start(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_omemo_end(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_omemo_fingerprint(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_omemo_trust(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_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_omemo_gen(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_char(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_log(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_trust(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_clear_device_list(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_save(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_reload(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_paste(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_color(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_avatar(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_os(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_correction(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_correct(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_slashguard(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_save(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_executable(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_paste(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_color(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_avatar(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_os(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_correction(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_correct(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_slashguard(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_save(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_executable(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/config/preferences.c b/src/config/preferences.c index e4aebdb5..e1ff4a06 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1790,145 +1790,145 @@ _save_prefs(void) static const char* _get_group(preference_t pref) { - switch (pref) { - case PREF_CLEAR_PERSIST_HISTORY: - case PREF_SPLASH: - case PREF_BEEP: - case PREF_THEME: - case PREF_VERCHECK: - case PREF_WINTITLE_SHOW: - case PREF_WINTITLE_GOODBYE: - case PREF_FLASH: - case PREF_INTYPE: - case PREF_HISTORY: - case PREF_OCCUPANTS: - case PREF_OCCUPANTS_JID: - case PREF_OCCUPANTS_WRAP: - case PREF_STATUSES: - case PREF_STATUSES_CONSOLE: - case PREF_STATUSES_CHAT: - case PREF_STATUSES_MUC: - case PREF_MUC_PRIVILEGES: - case PREF_PRESENCE: - case PREF_WRAP: - case PREF_TIME_CONSOLE: - case PREF_TIME_CHAT: - case PREF_TIME_MUC: - case PREF_TIME_CONFIG: - case PREF_TIME_PRIVATE: - case PREF_TIME_XMLCONSOLE: - case PREF_TIME_STATUSBAR: - case PREF_TIME_LASTACTIVITY: - case PREF_ROSTER: - case PREF_ROSTER_OFFLINE: - case PREF_ROSTER_RESOURCE: - case PREF_ROSTER_PRESENCE: - case PREF_ROSTER_STATUS: - case PREF_ROSTER_EMPTY: - case PREF_ROSTER_BY: - case PREF_ROSTER_ORDER: - case PREF_ROSTER_UNREAD: - case PREF_ROSTER_COUNT: - case PREF_ROSTER_COUNT_ZERO: - case PREF_ROSTER_PRIORITY: - case PREF_ROSTER_WRAP: - case PREF_ROSTER_RESOURCE_JOIN: - case PREF_ROSTER_CONTACTS: - case PREF_ROSTER_UNSUBSCRIBED: - case PREF_ROSTER_ROOMS: - case PREF_ROSTER_ROOMS_POS: - case PREF_ROSTER_ROOMS_BY: - case PREF_ROSTER_ROOMS_ORDER: - case PREF_ROSTER_ROOMS_UNREAD: - case PREF_ROSTER_ROOMS_SERVER: - case PREF_ROSTER_ROOMS_USE_AS_NAME: - case PREF_ROSTER_PRIVATE: - case PREF_RESOURCE_TITLE: - case PREF_RESOURCE_MESSAGE: - case PREF_ENC_WARN: - case PREF_INPBLOCK_DYNAMIC: - case PREF_TLS_SHOW: - case PREF_CONSOLE_MUC: - case PREF_CONSOLE_PRIVATE: - case PREF_CONSOLE_CHAT: - case PREF_COLOR_NICK: - case PREF_COLOR_NICK_OWN: - case PREF_ROSTER_COLOR_NICK: - case PREF_OCCUPANTS_COLOR_NICK: - case PREF_STATUSBAR_SHOW_NAME: - case PREF_STATUSBAR_SHOW_NUMBER: - case PREF_STATUSBAR_SHOW_READ: - case PREF_STATUSBAR_SELF: - case PREF_STATUSBAR_CHAT: - case PREF_STATUSBAR_ROOM: - case PREF_TITLEBAR_MUC_TITLE_JID: - case PREF_TITLEBAR_MUC_TITLE_NAME: - case PREF_SLASH_GUARD: - return PREF_GROUP_UI; - case PREF_STATES: - case PREF_OUTTYPE: - return PREF_GROUP_CHATSTATES; - case PREF_NOTIFY_TYPING: - case PREF_NOTIFY_TYPING_CURRENT: - case PREF_NOTIFY_CHAT: - case PREF_NOTIFY_CHAT_CURRENT: - case PREF_NOTIFY_CHAT_TEXT: - case PREF_NOTIFY_ROOM: - case PREF_NOTIFY_ROOM_MENTION: - case PREF_NOTIFY_ROOM_TRIGGER: - case PREF_NOTIFY_ROOM_CURRENT: - case PREF_NOTIFY_ROOM_TEXT: - case PREF_NOTIFY_INVITE: - case PREF_NOTIFY_SUB: - case PREF_NOTIFY_MENTION_CASE_SENSITIVE: - case PREF_NOTIFY_MENTION_WHOLE_WORD: - case PREF_TRAY: - case PREF_TRAY_READ: - case PREF_ADV_NOTIFY_DISCO_OR_VERSION: - return PREF_GROUP_NOTIFICATIONS; - case PREF_CHLOG: - case PREF_GRLOG: - case PREF_LOG_ROTATE: - case PREF_LOG_SHARED: - return PREF_GROUP_LOGGING; - case PREF_AVATAR_CMD: - case PREF_URL_OPEN_CMD: - case PREF_URL_SAVE_CMD: - return PREF_GROUP_EXECUTABLES; - case PREF_AUTOAWAY_CHECK: - case PREF_AUTOAWAY_MODE: - case PREF_AUTOAWAY_MESSAGE: - case PREF_AUTOXA_MESSAGE: - case PREF_LASTACTIVITY: - return PREF_GROUP_PRESENCE; - case PREF_CONNECT_ACCOUNT: - case PREF_DEFAULT_ACCOUNT: - case PREF_CARBONS: - case PREF_RECEIPTS_SEND: - case PREF_RECEIPTS_REQUEST: - case PREF_REVEAL_OS: - case PREF_TLS_CERTPATH: - case PREF_CORRECTION_ALLOW: - case PREF_MAM: - return PREF_GROUP_CONNECTION; - case PREF_OTR_LOG: - case PREF_OTR_POLICY: - case PREF_OTR_SENDFILE: - return PREF_GROUP_OTR; - case PREF_PGP_LOG: - case PREF_PGP_SENDFILE: - return PREF_GROUP_PGP; - case PREF_BOOKMARK_INVITE: - case PREF_ROOM_LIST_CACHE: - return PREF_GROUP_MUC; - case PREF_PLUGINS_SOURCEPATH: - return PREF_GROUP_PLUGINS; - case PREF_OMEMO_LOG: - case PREF_OMEMO_POLICY: - case PREF_OMEMO_SENDFILE: - return PREF_GROUP_OMEMO; - default: - return NULL; + switch (pref) + { + case PREF_CLEAR_PERSIST_HISTORY: + case PREF_SPLASH: + case PREF_BEEP: + case PREF_THEME: + case PREF_VERCHECK: + case PREF_WINTITLE_SHOW: + case PREF_WINTITLE_GOODBYE: + case PREF_FLASH: + case PREF_INTYPE: + case PREF_HISTORY: + case PREF_OCCUPANTS: + case PREF_OCCUPANTS_JID: + case PREF_OCCUPANTS_WRAP: + case PREF_STATUSES: + case PREF_STATUSES_CONSOLE: + case PREF_STATUSES_CHAT: + case PREF_STATUSES_MUC: + case PREF_MUC_PRIVILEGES: + case PREF_PRESENCE: + case PREF_WRAP: + case PREF_TIME_CONSOLE: + case PREF_TIME_CHAT: + case PREF_TIME_MUC: + case PREF_TIME_CONFIG: + case PREF_TIME_PRIVATE: + case PREF_TIME_XMLCONSOLE: + case PREF_TIME_STATUSBAR: + case PREF_TIME_LASTACTIVITY: + case PREF_ROSTER: + case PREF_ROSTER_OFFLINE: + case PREF_ROSTER_RESOURCE: + case PREF_ROSTER_PRESENCE: + case PREF_ROSTER_STATUS: + case PREF_ROSTER_EMPTY: + case PREF_ROSTER_BY: + case PREF_ROSTER_ORDER: + case PREF_ROSTER_UNREAD: + case PREF_ROSTER_COUNT: + case PREF_ROSTER_COUNT_ZERO: + case PREF_ROSTER_PRIORITY: + case PREF_ROSTER_WRAP: + case PREF_ROSTER_RESOURCE_JOIN: + case PREF_ROSTER_CONTACTS: + case PREF_ROSTER_UNSUBSCRIBED: + case PREF_ROSTER_ROOMS: + case PREF_ROSTER_ROOMS_POS: + case PREF_ROSTER_ROOMS_BY: + case PREF_ROSTER_ROOMS_ORDER: + case PREF_ROSTER_ROOMS_UNREAD: + case PREF_ROSTER_ROOMS_SERVER: + case PREF_ROSTER_ROOMS_USE_AS_NAME: + case PREF_ROSTER_PRIVATE: + case PREF_RESOURCE_TITLE: + case PREF_RESOURCE_MESSAGE: + case PREF_ENC_WARN: + case PREF_INPBLOCK_DYNAMIC: + case PREF_TLS_SHOW: + case PREF_CONSOLE_MUC: + case PREF_CONSOLE_PRIVATE: + case PREF_CONSOLE_CHAT: + case PREF_COLOR_NICK: + case PREF_COLOR_NICK_OWN: + case PREF_ROSTER_COLOR_NICK: + case PREF_OCCUPANTS_COLOR_NICK: + case PREF_STATUSBAR_SHOW_NAME: + case PREF_STATUSBAR_SHOW_NUMBER: + case PREF_STATUSBAR_SHOW_READ: + case PREF_STATUSBAR_SELF: + case PREF_STATUSBAR_CHAT: + case PREF_STATUSBAR_ROOM: + case PREF_TITLEBAR_MUC_TITLE_JID: + case PREF_TITLEBAR_MUC_TITLE_NAME: + case PREF_SLASH_GUARD: + return PREF_GROUP_UI; + case PREF_STATES: + case PREF_OUTTYPE: + return PREF_GROUP_CHATSTATES; + case PREF_NOTIFY_TYPING: + case PREF_NOTIFY_TYPING_CURRENT: + case PREF_NOTIFY_CHAT: + case PREF_NOTIFY_CHAT_CURRENT: + case PREF_NOTIFY_CHAT_TEXT: + case PREF_NOTIFY_ROOM: + case PREF_NOTIFY_ROOM_MENTION: + case PREF_NOTIFY_ROOM_TRIGGER: + case PREF_NOTIFY_ROOM_CURRENT: + case PREF_NOTIFY_ROOM_TEXT: + case PREF_NOTIFY_INVITE: + case PREF_NOTIFY_SUB: + case PREF_NOTIFY_MENTION_CASE_SENSITIVE: + case PREF_NOTIFY_MENTION_WHOLE_WORD: + case PREF_TRAY: + case PREF_TRAY_READ: + case PREF_ADV_NOTIFY_DISCO_OR_VERSION: + return PREF_GROUP_NOTIFICATIONS; + case PREF_CHLOG: + case PREF_GRLOG: + case PREF_LOG_ROTATE: + case PREF_LOG_SHARED: + return PREF_GROUP_LOGGING; + case PREF_AVATAR_CMD: + case PREF_URL_OPEN_CMD: + case PREF_URL_SAVE_CMD: + return PREF_GROUP_EXECUTABLES; + case PREF_AUTOAWAY_CHECK: + case PREF_AUTOAWAY_MODE: + case PREF_AUTOAWAY_MESSAGE: + case PREF_AUTOXA_MESSAGE: + case PREF_LASTACTIVITY: + return PREF_GROUP_PRESENCE; + case PREF_CONNECT_ACCOUNT: + case PREF_DEFAULT_ACCOUNT: + case PREF_CARBONS: + case PREF_RECEIPTS_SEND: + case PREF_RECEIPTS_REQUEST: + case PREF_REVEAL_OS: + case PREF_TLS_CERTPATH: + case PREF_CORRECTION_ALLOW: + case PREF_MAM: + return PREF_GROUP_CONNECTION; + case PREF_OTR_LOG: + case PREF_OTR_POLICY: + case PREF_OTR_SENDFILE: + return PREF_GROUP_OTR; + case PREF_PGP_LOG: + case PREF_PGP_SENDFILE: + return PREF_GROUP_PGP; + case PREF_BOOKMARK_INVITE: + case PREF_ROOM_LIST_CACHE: + return PREF_GROUP_MUC; + case PREF_PLUGINS_SOURCEPATH: + return PREF_GROUP_PLUGINS; + case PREF_OMEMO_LOG: + case PREF_OMEMO_POLICY: + return PREF_GROUP_OMEMO; + default: + return NULL; } } @@ -1937,257 +1937,256 @@ _get_group(preference_t pref) static const char* _get_key(preference_t pref) { - switch (pref) { - case PREF_CLEAR_PERSIST_HISTORY: - return "clear.persist_history"; - case PREF_SPLASH: - return "splash"; - case PREF_BEEP: - return "beep"; - case PREF_THEME: - return "theme"; - case PREF_VERCHECK: - return "vercheck"; - case PREF_WINTITLE_SHOW: - return "wintitle.show"; - case PREF_WINTITLE_GOODBYE: - return "wintitle.goodbye"; - case PREF_FLASH: - return "flash"; - case PREF_TRAY: - return "tray"; - case PREF_TRAY_READ: - return "tray.read"; - case PREF_ADV_NOTIFY_DISCO_OR_VERSION: - return "adv.notify.discoversion"; - case PREF_INTYPE: - return "intype"; - case PREF_HISTORY: - return "history"; - case PREF_CARBONS: - return "carbons"; - case PREF_RECEIPTS_SEND: - return "receipts.send"; - case PREF_RECEIPTS_REQUEST: - return "receipts.request"; - case PREF_REVEAL_OS: - return "reveal.os"; - case PREF_OCCUPANTS: - return "occupants"; - case PREF_OCCUPANTS_JID: - return "occupants.jid"; - case PREF_OCCUPANTS_WRAP: - return "occupants.wrap"; - case PREF_MUC_PRIVILEGES: - return "privileges"; - case PREF_STATUSES: - return "statuses"; - case PREF_STATUSES_CONSOLE: - return "statuses.console"; - case PREF_STATUSES_CHAT: - return "statuses.chat"; - case PREF_STATUSES_MUC: - return "statuses.muc"; - case PREF_STATES: - return "enabled"; - case PREF_OUTTYPE: - return "outtype"; - case PREF_NOTIFY_TYPING: - return "typing"; - case PREF_NOTIFY_TYPING_CURRENT: - return "typing.current"; - case PREF_NOTIFY_CHAT: - return "message"; - case PREF_NOTIFY_CHAT_CURRENT: - return "message.current"; - case PREF_NOTIFY_CHAT_TEXT: - return "message.text"; - case PREF_NOTIFY_ROOM: - return "room"; - case PREF_NOTIFY_ROOM_TRIGGER: - return "room.trigger"; - case PREF_NOTIFY_ROOM_MENTION: - return "room.mention"; - case PREF_NOTIFY_ROOM_CURRENT: - return "room.current"; - case PREF_NOTIFY_ROOM_TEXT: - return "room.text"; - case PREF_NOTIFY_INVITE: - return "invite"; - case PREF_NOTIFY_SUB: - return "sub"; - case PREF_NOTIFY_MENTION_CASE_SENSITIVE: - return "room.mention.casesensitive"; - case PREF_NOTIFY_MENTION_WHOLE_WORD: - return "room.mention.wholeword"; - case PREF_CHLOG: - return "chlog"; - case PREF_GRLOG: - return "grlog"; - case PREF_AUTOAWAY_CHECK: - return "autoaway.check"; - case PREF_AUTOAWAY_MODE: - return "autoaway.mode"; - case PREF_AUTOAWAY_MESSAGE: - return "autoaway.awaymessage"; - case PREF_AUTOXA_MESSAGE: - return "autoaway.xamessage"; - case PREF_CONNECT_ACCOUNT: - return "account"; - case PREF_DEFAULT_ACCOUNT: - return "defaccount"; - case PREF_OTR_LOG: - return "log"; - case PREF_OTR_POLICY: - return "policy"; - case PREF_OTR_SENDFILE: - return "sendfile"; - case PREF_LOG_ROTATE: - return "rotate"; - case PREF_LOG_SHARED: - return "shared"; - case PREF_PRESENCE: - return "presence"; - case PREF_WRAP: - return "wrap"; - case PREF_TIME_CONSOLE: - return "time.console"; - case PREF_TIME_CHAT: - return "time.chat"; - case PREF_TIME_MUC: - return "time.muc"; - case PREF_TIME_CONFIG: - return "time.config"; - case PREF_TIME_PRIVATE: - return "time.private"; - case PREF_TIME_XMLCONSOLE: - return "time.xmlconsole"; - case PREF_TIME_STATUSBAR: - return "time.statusbar"; - case PREF_TIME_LASTACTIVITY: - return "time.lastactivity"; - case PREF_ROSTER: - return "roster"; - case PREF_ROSTER_OFFLINE: - return "roster.offline"; - case PREF_ROSTER_RESOURCE: - return "roster.resource"; - case PREF_ROSTER_PRESENCE: - return "roster.presence"; - case PREF_ROSTER_STATUS: - return "roster.status"; - case PREF_ROSTER_EMPTY: - return "roster.empty"; - case PREF_ROSTER_BY: - return "roster.by"; - case PREF_ROSTER_ORDER: - return "roster.order"; - case PREF_ROSTER_UNREAD: - return "roster.unread"; - case PREF_ROSTER_COUNT: - return "roster.count"; - case PREF_ROSTER_COUNT_ZERO: - return "roster.count.zero"; - case PREF_ROSTER_PRIORITY: - return "roster.priority"; - case PREF_ROSTER_WRAP: - return "roster.wrap"; - case PREF_ROSTER_RESOURCE_JOIN: - return "roster.resource.join"; - case PREF_ROSTER_CONTACTS: - return "roster.contacts"; - case PREF_ROSTER_UNSUBSCRIBED: - return "roster.unsubscribed"; - case PREF_ROSTER_ROOMS: - return "roster.rooms"; - case PREF_ROSTER_ROOMS_POS: - return "roster.rooms.pos"; - case PREF_ROSTER_ROOMS_BY: - return "roster.rooms.by"; - case PREF_ROSTER_ROOMS_ORDER: - return "roster.rooms.order"; - case PREF_ROSTER_ROOMS_UNREAD: - return "roster.rooms.unread"; - case PREF_ROSTER_ROOMS_SERVER: - return "roster.rooms.server"; - case PREF_ROSTER_ROOMS_USE_AS_NAME: - return "roster.rooms.use.name"; - case PREF_ROSTER_PRIVATE: - return "roster.private"; - case PREF_RESOURCE_TITLE: - return "resource.title"; - case PREF_RESOURCE_MESSAGE: - return "resource.message"; - case PREF_INPBLOCK_DYNAMIC: - return "inpblock.dynamic"; - case PREF_ENC_WARN: - return "enc.warn"; - case PREF_TITLEBAR_MUC_TITLE_JID: - return "titlebar.muc.title.jid"; - case PREF_TITLEBAR_MUC_TITLE_NAME: - return "titlebar.muc.title.name"; - case PREF_PGP_LOG: - return "log"; - case PREF_PGP_SENDFILE: - return "sendfile"; - case PREF_TLS_CERTPATH: - return "tls.certpath"; - case PREF_TLS_SHOW: - return "tls.show"; - case PREF_LASTACTIVITY: - return "lastactivity"; - case PREF_CONSOLE_MUC: - return "console.muc"; - case PREF_CONSOLE_PRIVATE: - return "console.private"; - case PREF_CONSOLE_CHAT: - return "console.chat"; - case PREF_COLOR_NICK: - return "color.nick"; - case PREF_COLOR_NICK_OWN: - return "color.nick.own"; - case PREF_ROSTER_COLOR_NICK: - return "color.roster.nick"; - case PREF_OCCUPANTS_COLOR_NICK: - return "color.occupants.nick"; - case PREF_BOOKMARK_INVITE: - return "bookmark.invite"; - case PREF_PLUGINS_SOURCEPATH: - return "sourcepath"; - case PREF_ROOM_LIST_CACHE: - return "rooms.cache"; - case PREF_STATUSBAR_SHOW_NAME: - return "statusbar.show.name"; - case PREF_STATUSBAR_SHOW_NUMBER: - return "statusbar.show.number"; - case PREF_STATUSBAR_SHOW_READ: - return "statusbar.show.read"; - case PREF_STATUSBAR_SELF: - return "statusbar.self"; - case PREF_STATUSBAR_CHAT: - return "statusbar.chat"; - case PREF_STATUSBAR_ROOM: - return "statusbar.room"; - case PREF_OMEMO_LOG: - return "log"; - case PREF_OMEMO_POLICY: - return "policy"; - case PREF_OMEMO_SENDFILE: - return "sendfile"; - case PREF_CORRECTION_ALLOW: - return "correction.allow"; - case PREF_AVATAR_CMD: - return "avatar.cmd"; - case PREF_SLASH_GUARD: - return "slashguard"; - case PREF_MAM: - return "mam"; - case PREF_URL_OPEN_CMD: - return "url.open.cmd"; - case PREF_URL_SAVE_CMD: - return "url.save.cmd"; - default: - return NULL; + switch (pref) + { + case PREF_CLEAR_PERSIST_HISTORY: + return "clear.persist_history"; + case PREF_SPLASH: + return "splash"; + case PREF_BEEP: + return "beep"; + case PREF_THEME: + return "theme"; + case PREF_VERCHECK: + return "vercheck"; + case PREF_WINTITLE_SHOW: + return "wintitle.show"; + case PREF_WINTITLE_GOODBYE: + return "wintitle.goodbye"; + case PREF_FLASH: + return "flash"; + case PREF_TRAY: + return "tray"; + case PREF_TRAY_READ: + return "tray.read"; + case PREF_ADV_NOTIFY_DISCO_OR_VERSION: + return "adv.notify.discoversion"; + case PREF_INTYPE: + return "intype"; + case PREF_HISTORY: + return "history"; + case PREF_CARBONS: + return "carbons"; + case PREF_RECEIPTS_SEND: + return "receipts.send"; + case PREF_RECEIPTS_REQUEST: + return "receipts.request"; + case PREF_REVEAL_OS: + return "reveal.os"; + case PREF_OCCUPANTS: + return "occupants"; + case PREF_OCCUPANTS_JID: + return "occupants.jid"; + case PREF_OCCUPANTS_WRAP: + return "occupants.wrap"; + case PREF_MUC_PRIVILEGES: + return "privileges"; + case PREF_STATUSES: + return "statuses"; + case PREF_STATUSES_CONSOLE: + return "statuses.console"; + case PREF_STATUSES_CHAT: + return "statuses.chat"; + case PREF_STATUSES_MUC: + return "statuses.muc"; + case PREF_STATES: + return "enabled"; + case PREF_OUTTYPE: + return "outtype"; + case PREF_NOTIFY_TYPING: + return "typing"; + case PREF_NOTIFY_TYPING_CURRENT: + return "typing.current"; + case PREF_NOTIFY_CHAT: + return "message"; + case PREF_NOTIFY_CHAT_CURRENT: + return "message.current"; + case PREF_NOTIFY_CHAT_TEXT: + return "message.text"; + case PREF_NOTIFY_ROOM: + return "room"; + case PREF_NOTIFY_ROOM_TRIGGER: + return "room.trigger"; + case PREF_NOTIFY_ROOM_MENTION: + return "room.mention"; + case PREF_NOTIFY_ROOM_CURRENT: + return "room.current"; + case PREF_NOTIFY_ROOM_TEXT: + return "room.text"; + case PREF_NOTIFY_INVITE: + return "invite"; + case PREF_NOTIFY_SUB: + return "sub"; + case PREF_NOTIFY_MENTION_CASE_SENSITIVE: + return "room.mention.casesensitive"; + case PREF_NOTIFY_MENTION_WHOLE_WORD: + return "room.mention.wholeword"; + case PREF_CHLOG: + return "chlog"; + case PREF_GRLOG: + return "grlog"; + case PREF_AUTOAWAY_CHECK: + return "autoaway.check"; + case PREF_AUTOAWAY_MODE: + return "autoaway.mode"; + case PREF_AUTOAWAY_MESSAGE: + return "autoaway.awaymessage"; + case PREF_AUTOXA_MESSAGE: + return "autoaway.xamessage"; + case PREF_CONNECT_ACCOUNT: + return "account"; + case PREF_DEFAULT_ACCOUNT: + return "defaccount"; + case PREF_OTR_LOG: + return "log"; + case PREF_OTR_POLICY: + return "policy"; + case PREF_OTR_SENDFILE: + return "sendfile"; + case PREF_LOG_ROTATE: + return "rotate"; + case PREF_LOG_SHARED: + return "shared"; + case PREF_PRESENCE: + return "presence"; + case PREF_WRAP: + return "wrap"; + case PREF_TIME_CONSOLE: + return "time.console"; + case PREF_TIME_CHAT: + return "time.chat"; + case PREF_TIME_MUC: + return "time.muc"; + case PREF_TIME_CONFIG: + return "time.config"; + case PREF_TIME_PRIVATE: + return "time.private"; + case PREF_TIME_XMLCONSOLE: + return "time.xmlconsole"; + case PREF_TIME_STATUSBAR: + return "time.statusbar"; + case PREF_TIME_LASTACTIVITY: + return "time.lastactivity"; + case PREF_ROSTER: + return "roster"; + case PREF_ROSTER_OFFLINE: + return "roster.offline"; + case PREF_ROSTER_RESOURCE: + return "roster.resource"; + case PREF_ROSTER_PRESENCE: + return "roster.presence"; + case PREF_ROSTER_STATUS: + return "roster.status"; + case PREF_ROSTER_EMPTY: + return "roster.empty"; + case PREF_ROSTER_BY: + return "roster.by"; + case PREF_ROSTER_ORDER: + return "roster.order"; + case PREF_ROSTER_UNREAD: + return "roster.unread"; + case PREF_ROSTER_COUNT: + return "roster.count"; + case PREF_ROSTER_COUNT_ZERO: + return "roster.count.zero"; + case PREF_ROSTER_PRIORITY: + return "roster.priority"; + case PREF_ROSTER_WRAP: + return "roster.wrap"; + case PREF_ROSTER_RESOURCE_JOIN: + return "roster.resource.join"; + case PREF_ROSTER_CONTACTS: + return "roster.contacts"; + case PREF_ROSTER_UNSUBSCRIBED: + return "roster.unsubscribed"; + case PREF_ROSTER_ROOMS: + return "roster.rooms"; + case PREF_ROSTER_ROOMS_POS: + return "roster.rooms.pos"; + case PREF_ROSTER_ROOMS_BY: + return "roster.rooms.by"; + case PREF_ROSTER_ROOMS_ORDER: + return "roster.rooms.order"; + case PREF_ROSTER_ROOMS_UNREAD: + return "roster.rooms.unread"; + case PREF_ROSTER_ROOMS_SERVER: + return "roster.rooms.server"; + case PREF_ROSTER_ROOMS_USE_AS_NAME: + return "roster.rooms.use.name"; + case PREF_ROSTER_PRIVATE: + return "roster.private"; + case PREF_RESOURCE_TITLE: + return "resource.title"; + case PREF_RESOURCE_MESSAGE: + return "resource.message"; + case PREF_INPBLOCK_DYNAMIC: + return "inpblock.dynamic"; + case PREF_ENC_WARN: + return "enc.warn"; + case PREF_TITLEBAR_MUC_TITLE_JID: + return "titlebar.muc.title.jid"; + case PREF_TITLEBAR_MUC_TITLE_NAME: + return "titlebar.muc.title.name"; + case PREF_PGP_LOG: + return "log"; + case PREF_PGP_SENDFILE: + return "sendfile"; + case PREF_TLS_CERTPATH: + return "tls.certpath"; + case PREF_TLS_SHOW: + return "tls.show"; + case PREF_LASTACTIVITY: + return "lastactivity"; + case PREF_CONSOLE_MUC: + return "console.muc"; + case PREF_CONSOLE_PRIVATE: + return "console.private"; + case PREF_CONSOLE_CHAT: + return "console.chat"; + case PREF_COLOR_NICK: + return "color.nick"; + case PREF_COLOR_NICK_OWN: + return "color.nick.own"; + case PREF_ROSTER_COLOR_NICK: + return "color.roster.nick"; + case PREF_OCCUPANTS_COLOR_NICK: + return "color.occupants.nick"; + case PREF_BOOKMARK_INVITE: + return "bookmark.invite"; + case PREF_PLUGINS_SOURCEPATH: + return "sourcepath"; + case PREF_ROOM_LIST_CACHE: + return "rooms.cache"; + case PREF_STATUSBAR_SHOW_NAME: + return "statusbar.show.name"; + case PREF_STATUSBAR_SHOW_NUMBER: + return "statusbar.show.number"; + case PREF_STATUSBAR_SHOW_READ: + return "statusbar.show.read"; + case PREF_STATUSBAR_SELF: + return "statusbar.self"; + case PREF_STATUSBAR_CHAT: + return "statusbar.chat"; + case PREF_STATUSBAR_ROOM: + return "statusbar.room"; + case PREF_OMEMO_LOG: + return "log"; + case PREF_OMEMO_POLICY: + return "policy"; + case PREF_CORRECTION_ALLOW: + return "correction.allow"; + case PREF_AVATAR_CMD: + return "avatar.cmd"; + case PREF_SLASH_GUARD: + return "slashguard"; + case PREF_MAM: + return "mam"; + case PREF_URL_OPEN_CMD: + return "url.open.cmd"; + case PREF_URL_SAVE_CMD: + return "url.save.cmd"; + default: + return NULL; } } diff --git a/src/config/preferences.h b/src/config/preferences.h index a9261853..141d8fce 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -165,7 +165,6 @@ typedef enum { PREF_STATUSBAR_ROOM, PREF_OMEMO_LOG, PREF_OMEMO_POLICY, - PREF_OMEMO_SENDFILE, PREF_OCCUPANTS_WRAP, PREF_CORRECTION_ALLOW, PREF_AVATAR_CMD, diff --git a/src/ui/console.c b/src/ui/console.c index cb2bb888..03a5b21f 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2192,13 +2192,7 @@ cons_show_omemo_prefs(void) cons_show("OMEMO char (/omemo char) : %s", 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(); } void From 04bfa23ead26801728e0e7f2dfb5100556cefe66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 15:45:19 +0200 Subject: [PATCH 09/31] Remove temporary ciphertext file when finished --- src/command/cmd_funcs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 113192cc..6dcb591f 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4862,7 +4862,8 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) // Create temporary file for writing ciphertext. int tmpfd; - if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", NULL, NULL)) == -1) { + char *tmpname = NULL; + if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) { char *msg = "Unable to create temporary file for encrypted transfer."; cons_show_error(msg); win_println(window, THEME_ERROR, "-", msg); @@ -4871,6 +4872,11 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } FILE *tmpfh = fdopen(tmpfd, "wb"); + // The temporary ciphertext file should be removed upon closure + // later. + remove(tmpname); + free(tmpname); + int crypt_res; alt_scheme = OMEMO_AESGCM_URL_SCHEME; alt_fragment = omemo_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); @@ -4889,7 +4895,7 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) fclose(fh); // Also closes descriptor. - // Switch original stream with temporary encrypted stream. + // Switch original stream with temporary ciphertext stream. fd = tmpfd; fh = tmpfh; From 362c6973de1182554d33936403794d9aa5efe093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 28 Jun 2020 15:46:31 +0200 Subject: [PATCH 10/31] Wording --- src/command/cmd_funcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 6dcb591f..55bef3d1 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4872,8 +4872,8 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) } FILE *tmpfh = fdopen(tmpfd, "wb"); - // The temporary ciphertext file should be removed upon closure - // later. + // The temporary ciphertext file should be removed after it has + // been closed. remove(tmpname); free(tmpname); From 9499df65859abd1404d3f9e96b8af859d3315bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Thu, 2 Jul 2020 22:04:21 +0200 Subject: [PATCH 11/31] Add http_download tool --- src/command/cmd_funcs.c | 7 +- src/tools/http_download.c | 223 ++++++++++++++++++++++++++++++++++++++ src/tools/http_download.h | 63 +++++++++++ src/tools/http_upload.c | 2 +- src/ui/window.c | 2 +- src/ui/window.h | 2 +- 6 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 src/tools/http_download.c create mode 100644 src/tools/http_download.h diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 55bef3d1..7d921ea3 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9114,8 +9114,8 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) 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")) { + gchar *scheme = g_uri_parse_scheme(args[1]); + if( 0 == g_strcmp0(scheme, OMEMO_AESGCM_URL_SCHEME)) { require_save = true; } g_free(scheme); @@ -9221,7 +9221,8 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) if (0 == g_strcmp0(scheme, "http") || 0 == g_strcmp0(scheme, "https") - || 0 == g_strcmp0(scheme, "aesgcm")) { + || 0 == g_strcmp0(scheme, OMEMO_AESGCM_URL_SCHEME) + ) { scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); } diff --git a/src/tools/http_download.c b/src/tools/http_download.c new file mode 100644 index 00000000..80916385 --- /dev/null +++ b/src/tools/http_download.c @@ -0,0 +1,223 @@ +/* + * http_download.c + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2020 William Wennerström + * + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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" + +#define FALLBACK_MSG "" + +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; + } + + char *msg; + if (asprintf(&msg, "Downloading '%s': %d%%", download->url, dlperc) == -1) { + msg = strdup(FALLBACK_MSG); + } + win_update_entry_message(download->window, download->url, msg); + free(msg); + + pthread_mutex_unlock(&lock); + + return 0; +} + +#if LIBCURL_VERSION_NUM < 0x072000 +static int +_older_progress(void *p, double dltotal, double dlnow, double ultotal, double ulnow) +{ + return _xferinfo(p, (curl_off_t)dltotal, (curl_off_t)dlnow, (curl_off_t)ultotal, (curl_off_t)ulnow); +} +#endif + +void * +http_file_get(void *userdata) +{ + HTTPDownload *download = (HTTPDownload *)userdata; + + FILE *fh = NULL; + + char *err = NULL; + + CURL *curl; + CURLcode res; + + download->cancel = 0; + download->bytes_received = 0; + + pthread_mutex_lock(&lock); + char* msg; + if (asprintf(&msg, "Downloading '%s': 0%%", download->url) == -1) { + msg = strdup(FALLBACK_MSG); + } + win_print_http_transfer(download->window, msg, download->url); + free(msg); + + char *cert_path = prefs_get_string(PREF_TLS_CERTPATH); + pthread_mutex_unlock(&lock); + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + + curl_easy_setopt(curl, CURLOPT_URL, 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); + + fh = download->filehandle; + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&fh); + 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 (fh) { + fclose(fh); + } + + pthread_mutex_lock(&lock); + g_free(cert_path); + + if (err) { + char *msg; + if (download->cancel) { + if (asprintf(&msg, "Downloading '%s' failed: Download was canceled", download->url) == -1) { + msg = strdup(FALLBACK_MSG); + } + } else { + if (asprintf(&msg, "Downloading '%s' failed: %s", download->url, err) == -1) { + msg = strdup(FALLBACK_MSG); + } + win_update_entry_message(download->window, download->url, msg); + } + cons_show_error(msg); + free(msg); + free(err); + } else { + if (!download->cancel) { + if (asprintf(&msg, "Downloading '%s': 100%%", download->url) == -1) { + msg = strdup(FALLBACK_MSG); + } + win_update_entry_message(download->window, download->url, msg); + win_mark_received(download->window, download->put_url); + free(msg); + } + } + + download_processes = g_slist_remove(download_processes, download); + pthread_mutex_unlock(&lock); + + free(download->url); + 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); +} diff --git a/src/tools/http_download.h b/src/tools/http_download.h new file mode 100644 index 00000000..7348b77c --- /dev/null +++ b/src/tools/http_download.h @@ -0,0 +1,63 @@ +/* + * http_download.h + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2020 William Wennerström + * + * 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 . + * + * 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 +#include + +#include "ui/win_types.h" + +typedef struct http_download_t { + char *url; + FILE *filehandle; + 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 diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 8d369f8a..2fb32062 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -176,7 +176,7 @@ http_file_put(void *userdata) if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) { 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); char* cert_path = prefs_get_string(PREF_TLS_CERTPATH); diff --git a/src/ui/window.c b/src/ui/window.c index 687af3b2..c03b3aab 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -1390,7 +1390,7 @@ win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* con } 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); } diff --git a/src/ui/window.h b/src/ui/window.h index c731d19b..86d1dbfd 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -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_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_redraw(ProfWin* window); From eebf54c8596abd5c28b405d8860c173207bbec64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 5 Jul 2020 17:21:20 +0200 Subject: [PATCH 12/31] Infer filename from content-disposition or URL The Content-Disposition inferring is probably a bad idea security wise, so I am going to remove it. --- Makefile.am | 6 + src/command/cmd_funcs.c | 259 +++++++++++---------- src/tools/http_download.c | 125 +++++++++- src/tools/http_download.h | 5 + tests/unittests/test_http_download.c | 119 ++++++++++ tests/unittests/test_http_download.h | 2 + tests/unittests/tools/stub_http_download.c | 28 +++ tests/unittests/ui/stub_ui.c | 101 +++++++- tests/unittests/unittests.c | 4 + 9 files changed, 512 insertions(+), 137 deletions(-) create mode 100644 tests/unittests/test_http_download.c create mode 100644 tests/unittests/test_http_download.h create mode 100644 tests/unittests/tools/stub_http_download.c diff --git a/Makefile.am b/Makefile.am index 1228e1ad..a2cf5598 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,6 +43,8 @@ core_sources = \ src/tools/parser.h \ src/tools/http_upload.c \ src/tools/http_upload.h \ + src/tools/http_download.c \ + src/tools/http_download.h \ src/tools/bookmark_ignore.c \ src/tools/bookmark_ignore.h \ src/tools/autocomplete.c src/tools/autocomplete.h \ @@ -89,6 +91,8 @@ unittest_sources = \ src/tools/clipboard.c src/tools/clipboard.h \ src/tools/bookmark_ignore.c \ src/tools/bookmark_ignore.h \ + src/tools/http_download.c \ + src/tools/http_download.h \ src/config/accounts.h \ src/config/account.c src/config/account.h \ src/config/files.c src/config/files.h \ @@ -119,6 +123,7 @@ unittest_sources = \ tests/unittests/database/stub_database.c \ tests/unittests/config/stub_accounts.c \ tests/unittests/tools/stub_http_upload.c \ + tests/unittests/tools/stub_http_download.c \ tests/unittests/helpers.c tests/unittests/helpers.h \ tests/unittests/test_form.c tests/unittests/test_form.h \ tests/unittests/test_common.c tests/unittests/test_common.h \ @@ -145,6 +150,7 @@ unittest_sources = \ tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \ tests/unittests/test_callbacks.c tests/unittests/test_callbacks.h \ tests/unittests/test_plugins_disco.c tests/unittests/test_plugins_disco.h \ + tests/unittests/test_http_download.c tests/unittests/test_http_download.h \ tests/unittests/unittests.c functionaltest_sources = \ diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 7d921ea3..5e8b5cab 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4,6 +4,7 @@ * * Copyright (C) 2012 - 2019 James Booth * Copyright (C) 2019 Michael Vetter + * Copyright (C) 2020 William Wennerström * * This file is part of Profanity. * @@ -67,6 +68,7 @@ #include "config/scripts.h" #include "event/client_events.h" #include "tools/http_upload.h" +#include "tools/http_download.h" #include "tools/autocomplete.h" #include "tools/parser.h" #include "tools/bookmark_ignore.h" @@ -4805,6 +4807,43 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args) return TRUE; } + +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; +} + gboolean cmd_sendfile(ProfWin* window, const char* const command, gchar** args) { @@ -4855,59 +4894,29 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) case WIN_CHAT: { ProfChatWin *chatwin = (ProfChatWin*)window; - assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); #ifdef HAVE_OMEMO if (chatwin->is_omemo) { - - // Create temporary file for writing ciphertext. - int tmpfd; - char *tmpname = NULL; - if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) { - char *msg = "Unable to create temporary file for encrypted transfer."; - cons_show_error(msg); - win_println(window, THEME_ERROR, "-", msg); - fclose(fh); - goto out; - } - 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 *err = NULL; alt_scheme = OMEMO_AESGCM_URL_SCHEME; - alt_fragment = omemo_encrypt_file(fh, tmpfh, file_size(fd), &crypt_res); - if (crypt_res != 0) { - char *msg = "Failed to encrypt file."; - cons_show_error(msg); - win_println(window, THEME_ERROR, "-", msg); - fclose(fh); - fclose(tmpfh); + alt_fragment = _add_omemo_stream(&fd, &fh, &err); + if (err != NULL) { + cons_show_error(err); + win_println(window, THEME_ERROR, "-", err); goto out; } - - // 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; - break; } #endif - 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; + 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; } @@ -9154,11 +9163,75 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) return TRUE; } + +void _url_save_fallback_method(ProfWin *window, const char *url, const char *directory, const char *filename) { + HTTPDownload *download = malloc(sizeof(HTTPDownload)); + download->window = window; + download->url = strdup(url); + + if (filename) { + download->filename = strdup(filename); + } else { + download->filename = NULL; + } + + if (directory) { + download->directory = strdup(directory); + } else { + download->directory = NULL; + } + + pthread_create(&(download->worker), NULL, &http_file_get, download); + http_download_add_download(download); +} + +void _url_save_external_method(const char *scheme_cmd, const char *url, const char *directory, char *filename) { + if (!filename) { + filename = http_filename_from_url(url); + } + + // Explicitly use "." as directory if no directory has been passed. + char *fp = NULL; + if (directory == NULL) { + fp = g_build_filename(".", filename, NULL); + } else { + fp = g_build_filename(directory, filename, NULL); + } + + if (!g_file_test(directory, G_FILE_TEST_EXISTS) || + !g_file_test(directory, G_FILE_TEST_IS_DIR)) { + cons_show_error("Directory '%s' does not exist or is not a directory.", directory); + return; + } + + gchar **argv = g_strsplit(scheme_cmd, " ", 0); + + 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(url); + } else if (0 == g_strcmp0(argv[num_args], "%p")) { + g_free(argv[num_args]); + argv[num_args] = fp; + } + 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 to '%s'.", url, fp); + } +} + 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) { - cons_show("url save not supported in this window"); + if (window->type != WIN_CHAT && + window->type != WIN_MUC && + window->type != WIN_PRIVATE) { + cons_show_error("`/url save` is not supported in this window."); return TRUE; } @@ -9167,91 +9240,37 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) return TRUE; } - gchar* uri = args[1]; - gchar* target_path = g_strdup(args[2]); + gchar *url = args[1]; + gchar *path = g_strdup(args[2]); - GFile* file = g_file_new_for_uri(uri); - - 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); + gchar *scheme = g_uri_parse_scheme(url); if (scheme == NULL) { - cons_show("URL '%s' is not valid.", uri); - g_free(target_path); - g_free(target_dir); - g_free(base_name); + cons_show("URL '%s' is not valid.", url); + g_free(url); return TRUE; } - gchar* scheme_cmd = NULL; - - if (0 == g_strcmp0(scheme, "http") - || 0 == g_strcmp0(scheme, "https") - || 0 == g_strcmp0(scheme, OMEMO_AESGCM_URL_SCHEME) - ) { - scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); + gchar *directory = NULL; + gchar *filename = NULL; + if (path != NULL) { + directory = g_path_get_dirname(path); + filename = g_path_get_basename(path); } - g_free(scheme); - - 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; + gchar *scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); + if (scheme_cmd == NULL) { + if (g_strcmp0(scheme, "http") == 0 + || g_strcmp0(scheme, "https") == 0 + || g_strcmp0(scheme, OMEMO_AESGCM_URL_SCHEME) == 0) { + _url_save_fallback_method(window, url, directory, filename); + } else { + cons_show_error("No download method defined for the scheme '%s'.", scheme); } - 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); + _url_save_external_method(scheme_cmd, url, directory, filename); } - g_free(target_dir); - g_free(base_name); - g_strfreev(argv); + g_free(scheme_cmd); return TRUE; } diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 80916385..5a0f6f18 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -104,13 +104,119 @@ _older_progress(void *p, double dltotal, double dlnow, double ultotal, double ul } #endif +char *http_filename_from_header(char *header) { + const char *header_tag_cd = "Content-Disposition:"; + const int header_tag_cd_len = strlen(header_tag_cd); + + if (!header) { + return NULL; // Bad header. + } + + if (strncasecmp(header, header_tag_cd, header_tag_cd_len) == 0) { + header += header_tag_cd_len; // Move to header content. + } else { + return NULL; // Not a CD header. + } + + const char *filename_key = "filename="; + const size_t filename_key_len = strlen(filename_key); + + char *value = strcasestr(header, filename_key); + if (!value) { + return NULL; // No filename key found. + } + + value += filename_key_len; // Move to key value. + + char fn[4096]; + char *pf = fn; + while(*value != '\0' && *value != ';') { + *pf++ = *value++; + } + *pf = '\0'; + + if (!strlen(fn)) { + return NULL; // Empty tag. + } + + return strdup(fn); +} + +char *http_filename_from_url(const char *url) { + const char *default_name = "index.html"; + + GFile *file = g_file_new_for_uri(url); + char *filename = g_file_get_basename(file); + g_object_unref(file); + + if (g_strcmp0(filename, ".") == 0 + || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { + g_free(filename); + return strdup(default_name); + } + + return filename; +} + +static size_t _header_callback(char *data, size_t size, size_t nitems, void *userdata) { + char *header = (char*)data; + + HTTPDownload *download = (HTTPDownload *)userdata; + size *= nitems; + + if (download->filename != NULL) { + return size; // No-op. + } + + download->filename = http_filename_from_header(header); + + return size; +} + +FILE *_get_filehandle(const char *directory, const char *filename) { + gchar *fp; + FILE *fh; + + // Explicitly use "." as directory if no directory has been passed. + if (directory == NULL) { + fp = g_build_filename(".", filename, NULL); + } else { + fp = g_build_filename(directory, filename, NULL); + } + + fh = fopen(fp, "wb"); + g_free(fp); + return fh; +} + +static size_t _write_callback(void *buffer, size_t size, size_t nmemb, void *userdata) { + HTTPDownload *download = (HTTPDownload *)userdata; + size *= nmemb; + + if (download->filename == NULL) { + download->filename = http_filename_from_url(download->url); + } + + if (download->filename == NULL || download->directory == NULL) { + return 0; // Missing file name or directory, write no data. + } + + if (download->filehandle == NULL ) { + FILE *fh = _get_filehandle(download->directory, download->filename); + if (!fh) { + return 0; // Unable to open file handle. + } + download->filehandle = fh; + } + + return fwrite(buffer, size, nmemb, userdata); +} + void * http_file_get(void *userdata) { HTTPDownload *download = (HTTPDownload *)userdata; - FILE *fh = NULL; - char *err = NULL; CURL *curl; @@ -144,9 +250,12 @@ http_file_get(void *userdata) #endif curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - fh = download->filehandle; + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, _header_callback); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)download); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)download); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&fh); curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity"); if (cert_path) { @@ -160,8 +269,8 @@ http_file_get(void *userdata) curl_easy_cleanup(curl); curl_global_cleanup(); - if (fh) { - fclose(fh); + if (download->filehandle) { + fclose(download->filehandle); } pthread_mutex_lock(&lock); @@ -188,7 +297,7 @@ http_file_get(void *userdata) msg = strdup(FALLBACK_MSG); } win_update_entry_message(download->window, download->url, msg); - win_mark_received(download->window, download->put_url); + win_mark_received(download->window, download->url); free(msg); } } @@ -197,6 +306,8 @@ http_file_get(void *userdata) pthread_mutex_unlock(&lock); free(download->url); + free(download->filename); + free(download->directory); free(download); return NULL; diff --git a/src/tools/http_download.h b/src/tools/http_download.h index 7348b77c..b0377d93 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -48,6 +48,8 @@ typedef struct http_download_t { char *url; + char *filename; + char *directory; FILE *filehandle; curl_off_t bytes_received; ProfWin *window; @@ -60,4 +62,7 @@ void* http_file_get(void *userdata); void http_download_cancel_processes(ProfWin *window); void http_download_add_download(HTTPDownload *download); +char *http_filename_from_url(const char *url); +char *http_filename_from_header(char *header); + #endif diff --git a/tests/unittests/test_http_download.c b/tests/unittests/test_http_download.c new file mode 100644 index 00000000..181fc78d --- /dev/null +++ b/tests/unittests/test_http_download.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "tools/http_download.h" + +typedef struct { + char *url; + char *filename; +} url_test_t; + +typedef struct { + char *header; + char *filename; +} header_test_t; + +void http_filename_from_url_td(void **state) { + int num_tests = 5; + url_test_t tests[] = { + (url_test_t){ + .url = "https://host.test/image.jpeg", + .filename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/images/", + .filename = "images", + }, + (url_test_t){ + .url = "https://host.test/", + .filename = "index.html", + }, + (url_test_t){ + .url = "https://host.test", + .filename = "index.html", + }, + (url_test_t){ + .url = "aesgcm://host.test", + .filename = "index.html", + }, + }; + + char *filename; + for(int i = 0; i < num_tests; i++) { + filename = http_filename_from_url(tests[i].url); + assert_string_equal(filename, tests[i].filename); + } +} + +void http_filename_from_header_td(void **state) { + int num_tests = 11; + header_test_t tests[] = { + (header_test_t){ + .header = "Content-Disposition: filename=image.jpeg", + .filename = "image.jpeg", + }, + (header_test_t){ + .header = "Content-Disposition:filename=image.jpeg", + .filename = "image.jpeg", + }, + (header_test_t){ + .header = "CoNteNt-DiSpoSItioN: filename=image.jpeg", + .filename = "image.jpeg", + }, + (header_test_t){ + .header = "Content-Disposition: attachment; filename=image.jpeg", + .filename = "image.jpeg", + }, + (header_test_t){ + .header = "Content-Disposition: filename=", + .filename = NULL, + }, + (header_test_t){ + .header = "Content-Disposition: filename=;", + .filename = NULL, + }, + (header_test_t){ + .header = "Content-Disposition: inline", + .filename = NULL, + }, + (header_test_t){ + .header = "Content-Disposition:", + .filename = NULL, + }, + (header_test_t){ + .header = "Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT ", + .filename = NULL, + }, + (header_test_t){ + .header = "", + .filename = NULL, + }, + (header_test_t){ + .header = NULL, + .filename = NULL, + }, + }; + + char *got_filename; + char *exp_filename; + char *header; + for(int i = 0; i < num_tests; i++) { + header = tests[i].header; + exp_filename = tests[i].filename; + + got_filename = http_filename_from_header(header); + + if (exp_filename == NULL) { + assert_null(got_filename); + } else { + assert_string_equal(got_filename, exp_filename); + } + } +} diff --git a/tests/unittests/test_http_download.h b/tests/unittests/test_http_download.h new file mode 100644 index 00000000..c8c333ee --- /dev/null +++ b/tests/unittests/test_http_download.h @@ -0,0 +1,2 @@ +void http_filename_from_url_td(void **state); +void http_filename_from_header_td(void **state); diff --git a/tests/unittests/tools/stub_http_download.c b/tests/unittests/tools/stub_http_download.c new file mode 100644 index 00000000..a07146b4 --- /dev/null +++ b/tests/unittests/tools/stub_http_download.c @@ -0,0 +1,28 @@ +#ifndef TOOLS_HTTP_DOWNLOAD_H +#define TOOLS_HTTP_DOWNLOAD_H + +#include +#include + +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); + +void http_download_cancel_processes(ProfWin *window); +void http_download_add_download(HTTPDownload *download); + +char *http_filename_from_url(const char *url); + +#endif diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 192f39ee..f284d04b 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -236,16 +236,97 @@ ui_contact_online(char* barejid, Resource* resource, GDateTime* last_activity) check_expected(last_activity); } -void -ui_contact_typing(const char* const barejid, const char* const resource) -{ -} -void -chatwin_incoming_msg(ProfChatWin* chatwin, ProfMessage* message, gboolean win_created) -{ -} -void -chatwin_receipt_received(ProfChatWin* chatwin, const char* const id) +void ui_contact_typing(const char * const barejid, const char * const resource) {} +void chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_created) {} +void chatwin_receipt_received(ProfChatWin *chatwin, const char * const id) {} + +void privwin_incoming_msg(ProfPrivateWin *privatewin, ProfMessage *message) {} + +void ui_disconnected(void) {} +void chatwin_recipient_gone(ProfChatWin *chatwin) {} + +void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, gboolean request_receipt, const char *const replace_id) {} +void chatwin_outgoing_carbon(ProfChatWin *chatwin, ProfMessage *message) {} +void privwin_outgoing_msg(ProfPrivateWin *privwin, const char * const message) {} + +void privwin_occupant_offline(ProfPrivateWin *privwin) {} +void privwin_occupant_kicked(ProfPrivateWin *privwin, const char *const actor, const char *const reason) {} +void privwin_occupant_banned(ProfPrivateWin *privwin, const char *const actor, const char *const reason) {} +void privwin_occupant_online(ProfPrivateWin *privwin) {} +void privwin_message_occupant_offline(ProfPrivateWin *privwin) {} + +void privwin_message_left_room(ProfPrivateWin *privwin) {} + +void ui_room_join(const char * const roomjid, gboolean focus) {} +void ui_switch_to_room(const char * const roomjid) {} + +void mucwin_role_change(ProfMucWin *mucwin, const char * const role, const char * const actor, + const char * const reason) {} +void mucwin_affiliation_change(ProfMucWin *mucwin, const char * const affiliation, const char * const actor, + const char * const reason) {} +void mucwin_role_and_affiliation_change(ProfMucWin *mucwin, const char * const role, + const char * const affiliation, const char * const actor, const char * const reason) {} +void mucwin_occupant_role_change(ProfMucWin *mucwin, const char * const nick, const char * const role, + const char * const actor, const char * const reason) {} +void mucwin_occupant_affiliation_change(ProfMucWin *mucwin, const char * const nick, const char * const affiliation, + const char * const actor, const char * const reason) {} +void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char * const nick, const char * const role, + const char * const affiliation, const char * const actor, const char * const reason) {} +void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {} +void mucwin_history(ProfMucWin *mucwin, const ProfMessage *const message) {} +void mucwin_incoming_msg(ProfMucWin *mucwin, const ProfMessage *const message, GSList *mentions, GList *triggers, gboolean filter_reflection) {} +void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode, const char *const replace_id) {} +void mucwin_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {} +void mucwin_requires_config(ProfMucWin *mucwin) {} +void ui_room_destroy(const char * const roomjid) {} +void mucwin_info(ProfMucWin *mucwin) {} +void mucwin_show_role_list(ProfMucWin *mucwin, muc_role_t role) {} +void mucwin_show_affiliation_list(ProfMucWin *mucwin, muc_affiliation_t affiliation) {} +void mucwin_room_info_error(ProfMucWin *mucwin, const char * const error) {} +void mucwin_room_disco_info(ProfMucWin *mucwin, GSList *identities, GSList *features) {} +void ui_room_destroyed(const char * const roomjid, const char * const reason, const char * const new_jid, + const char * const password) {} +void ui_room_kicked(const char * const roomjid, const char * const actor, const char * const reason) {} +void mucwin_occupant_kicked(ProfMucWin *mucwin, const char * const nick, const char * const actor, + const char * const reason) {} +void ui_room_banned(const char * const roomjid, const char * const actor, const char * const reason) {} +void mucwin_occupant_banned(ProfMucWin *mucwin, const char * const nick, const char * const actor, + const char * const reason) {} +void ui_leave_room(const char * const roomjid) {} +void mucwin_broadcast(ProfMucWin *mucwin, const char * const message) {} +void mucwin_occupant_offline(ProfMucWin *mucwin, const char * const nick) {} +void mucwin_occupant_online(ProfMucWin *mucwin, const char * const nick, const char * const roles, + const char * const affiliation, const char * const show, const char * const status) {} +void mucwin_occupant_nick_change(ProfMucWin *mucwin, const char * const old_nick, const char * const nick) {} +void mucwin_nick_change(ProfMucWin *mucwin, const char * const nick) {} +void mucwin_occupant_presence(ProfMucWin *mucwin, const char * const nick, const char * const show, + const char * const status) {} +void mucwin_update_occupants(ProfMucWin *mucwin) {} +void mucwin_show_occupants(ProfMucWin *mucwin) {} +void mucwin_hide_occupants(ProfMucWin *mucwin) {} +void mucwin_set_enctext(ProfMucWin *mucwin, const char *const enctext) {} +void mucwin_unset_enctext(ProfMucWin *mucwin) {} +void mucwin_set_message_char(ProfMucWin *mucwin, const char *const ch) {} +void 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 ui_show_roster(void) {} +void ui_hide_roster(void) {} +void ui_roster_add(const char * const barejid, const char * const name) {} +void ui_roster_remove(const char * const barejid) {} +void ui_contact_already_in_group(const char * const contact, const char * const group) {} +void ui_contact_not_in_group(const char * const contact, const char * const group) {} +void ui_group_added(const char * const contact, const char * const group) {} +void ui_group_removed(const char * const contact, const char * const group) {} +void chatwin_contact_online(ProfChatWin *chatwin, Resource *resource, GDateTime *last_activity) {} +void chatwin_contact_offline(ProfChatWin *chatwin, char *resource, char *status) {} + +void ui_contact_offline(char *barejid, char *resource, char *status) {} + +void ui_handle_recipient_error(const char * const recipient, const char * const err_msg) { } diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index 874f6194..9e9833bc 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -38,6 +38,7 @@ #include "test_form.h" #include "test_callbacks.h" #include "test_plugins_disco.h" +#include "test_http_download.h" int main(int argc, char* argv[]) @@ -626,6 +627,9 @@ main(int argc, char* argv[]) unit_test(does_not_add_duplicate_feature), unit_test(removes_plugin_features), unit_test(does_not_remove_feature_when_more_than_one_reference), + + unit_test(http_filename_from_url_td), + unit_test(http_filename_from_header_td), }; return run_tests(all_tests); From a0cf0844abeeffe83a13e438eff3c309bc9887e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 5 Jul 2020 18:28:23 +0200 Subject: [PATCH 13/31] Remove unsafe Conent-Disposition inferring --- src/command/cmd_funcs.c | 99 ++++++++++++-------- src/tools/http_download.c | 133 ++++----------------------- src/tools/http_download.h | 5 +- tests/unittests/test_http_download.c | 117 +++++++---------------- tests/unittests/test_http_download.h | 3 +- tests/unittests/unittests.c | 3 +- 6 files changed, 117 insertions(+), 243 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 5e8b5cab..fb39f104 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9164,46 +9164,23 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) } -void _url_save_fallback_method(ProfWin *window, const char *url, const char *directory, const char *filename) { +void _url_save_fallback_method(ProfWin *window, const char *url, const char *filename) { + FILE *fh = fopen(filename, "wb"); + if (!fh) { + cons_show_error("Cannot open file '%s' for writing.", filename); + return; + } + HTTPDownload *download = malloc(sizeof(HTTPDownload)); download->window = window; download->url = strdup(url); - - if (filename) { - download->filename = strdup(filename); - } else { - download->filename = NULL; - } - - if (directory) { - download->directory = strdup(directory); - } else { - download->directory = NULL; - } + download->filehandle = fh; pthread_create(&(download->worker), NULL, &http_file_get, download); http_download_add_download(download); } -void _url_save_external_method(const char *scheme_cmd, const char *url, const char *directory, char *filename) { - if (!filename) { - filename = http_filename_from_url(url); - } - - // Explicitly use "." as directory if no directory has been passed. - char *fp = NULL; - if (directory == NULL) { - fp = g_build_filename(".", filename, NULL); - } else { - fp = g_build_filename(directory, filename, NULL); - } - - if (!g_file_test(directory, G_FILE_TEST_EXISTS) || - !g_file_test(directory, G_FILE_TEST_IS_DIR)) { - cons_show_error("Directory '%s' does not exist or is not a directory.", directory); - return; - } - +void _url_save_external_method(const char *scheme_cmd, const char *url, const char *filename) { gchar **argv = g_strsplit(scheme_cmd, " ", 0); guint num_args = 0; @@ -9213,7 +9190,7 @@ void _url_save_external_method(const char *scheme_cmd, const char *url, const ch argv[num_args] = g_strdup(url); } else if (0 == g_strcmp0(argv[num_args], "%p")) { g_free(argv[num_args]); - argv[num_args] = fp; + argv[num_args] = strdup(filename); } num_args++; } @@ -9221,10 +9198,29 @@ void _url_save_external_method(const char *scheme_cmd, const char *url, const ch 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 to '%s'.", url, fp); + cons_show("URL '%s' has been saved to '%s'.", url, filename); } } +char *_make_unique_filename(const char *filename) { + char *unique = 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; + } + } + + return unique; +} + gboolean cmd_url_save(ProfWin *window, const char *const command, gchar **args) { @@ -9251,23 +9247,50 @@ cmd_url_save(ProfWin *window, const char *const command, gchar **args) } gchar *directory = NULL; - gchar *filename = NULL; + gchar *basename = NULL; if (path != NULL) { directory = g_path_get_dirname(path); - filename = g_path_get_basename(path); + basename = g_path_get_basename(path); } + if (directory == NULL) { + // Explicitly use "./" as directory if no directory has been passed. + directory = "./"; + } + + if (!g_file_test(directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + cons_show_error("Directory '%s' does not exist or is not a directory.", directory); + return TRUE; + } + + if (!basename) { + basename = http_basename_from_url(url); + } + + char *filename = NULL; + filename = g_build_filename(directory, basename, NULL); + + char *unique_filename = _make_unique_filename(filename); + if (!unique_filename) { + cons_show_error("Failed to generate an unique filename from '%s'.", filename); + free(filename); + return TRUE; + } + + free(filename); + filename = unique_filename; + gchar *scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); if (scheme_cmd == NULL) { if (g_strcmp0(scheme, "http") == 0 || g_strcmp0(scheme, "https") == 0 || g_strcmp0(scheme, OMEMO_AESGCM_URL_SCHEME) == 0) { - _url_save_fallback_method(window, url, directory, filename); + _url_save_fallback_method(window, url, filename); } else { cons_show_error("No download method defined for the scheme '%s'.", scheme); } } else { - _url_save_external_method(scheme_cmd, url, directory, filename); + _url_save_external_method(scheme_cmd, url, filename); } g_free(scheme_cmd); diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 5a0f6f18..3d4003f9 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -104,114 +104,6 @@ _older_progress(void *p, double dltotal, double dlnow, double ultotal, double ul } #endif -char *http_filename_from_header(char *header) { - const char *header_tag_cd = "Content-Disposition:"; - const int header_tag_cd_len = strlen(header_tag_cd); - - if (!header) { - return NULL; // Bad header. - } - - if (strncasecmp(header, header_tag_cd, header_tag_cd_len) == 0) { - header += header_tag_cd_len; // Move to header content. - } else { - return NULL; // Not a CD header. - } - - const char *filename_key = "filename="; - const size_t filename_key_len = strlen(filename_key); - - char *value = strcasestr(header, filename_key); - if (!value) { - return NULL; // No filename key found. - } - - value += filename_key_len; // Move to key value. - - char fn[4096]; - char *pf = fn; - while(*value != '\0' && *value != ';') { - *pf++ = *value++; - } - *pf = '\0'; - - if (!strlen(fn)) { - return NULL; // Empty tag. - } - - return strdup(fn); -} - -char *http_filename_from_url(const char *url) { - const char *default_name = "index.html"; - - GFile *file = g_file_new_for_uri(url); - char *filename = g_file_get_basename(file); - g_object_unref(file); - - if (g_strcmp0(filename, ".") == 0 - || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { - g_free(filename); - return strdup(default_name); - } - - return filename; -} - -static size_t _header_callback(char *data, size_t size, size_t nitems, void *userdata) { - char *header = (char*)data; - - HTTPDownload *download = (HTTPDownload *)userdata; - size *= nitems; - - if (download->filename != NULL) { - return size; // No-op. - } - - download->filename = http_filename_from_header(header); - - return size; -} - -FILE *_get_filehandle(const char *directory, const char *filename) { - gchar *fp; - FILE *fh; - - // Explicitly use "." as directory if no directory has been passed. - if (directory == NULL) { - fp = g_build_filename(".", filename, NULL); - } else { - fp = g_build_filename(directory, filename, NULL); - } - - fh = fopen(fp, "wb"); - g_free(fp); - return fh; -} - -static size_t _write_callback(void *buffer, size_t size, size_t nmemb, void *userdata) { - HTTPDownload *download = (HTTPDownload *)userdata; - size *= nmemb; - - if (download->filename == NULL) { - download->filename = http_filename_from_url(download->url); - } - - if (download->filename == NULL || download->directory == NULL) { - return 0; // Missing file name or directory, write no data. - } - - if (download->filehandle == NULL ) { - FILE *fh = _get_filehandle(download->directory, download->filename); - if (!fh) { - return 0; // Unable to open file handle. - } - download->filehandle = fh; - } - - return fwrite(buffer, size, nmemb, userdata); -} - void * http_file_get(void *userdata) { @@ -250,11 +142,7 @@ http_file_get(void *userdata) #endif curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, _header_callback); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)download); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)download); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)download->filehandle); curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity"); @@ -306,8 +194,6 @@ http_file_get(void *userdata) pthread_mutex_unlock(&lock); free(download->url); - free(download->filename); - free(download->directory); free(download); return NULL; @@ -332,3 +218,20 @@ http_download_add_download(HTTPDownload *download) { download_processes = g_slist_append(download_processes, download); } + +char *http_basename_from_url(const char *url) { + const char *default_name = "index.html"; + + GFile *file = g_file_new_for_uri(url); + char *filename = g_file_get_basename(file); + g_object_unref(file); + + if (g_strcmp0(filename, ".") == 0 + || g_strcmp0(filename, "..") == 0 + || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { + g_free(filename); + return strdup(default_name); + } + + return filename; +} diff --git a/src/tools/http_download.h b/src/tools/http_download.h index b0377d93..868b99f2 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -48,8 +48,6 @@ typedef struct http_download_t { char *url; - char *filename; - char *directory; FILE *filehandle; curl_off_t bytes_received; ProfWin *window; @@ -62,7 +60,6 @@ void* http_file_get(void *userdata); void http_download_cancel_processes(ProfWin *window); void http_download_add_download(HTTPDownload *download); -char *http_filename_from_url(const char *url); -char *http_filename_from_header(char *header); +char *http_basename_from_url(const char *url); #endif diff --git a/tests/unittests/test_http_download.c b/tests/unittests/test_http_download.c index 181fc78d..c0516a66 100644 --- a/tests/unittests/test_http_download.c +++ b/tests/unittests/test_http_download.c @@ -12,108 +12,61 @@ typedef struct { char *url; - char *filename; + char *basename; } url_test_t; -typedef struct { - char *header; - char *filename; -} header_test_t; - -void http_filename_from_url_td(void **state) { - int num_tests = 5; +void http_basename_from_url_td(void **state) { + int num_tests = 11; url_test_t tests[] = { (url_test_t){ .url = "https://host.test/image.jpeg", - .filename = "image.jpeg", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/image.jpeg#somefragment", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/image.jpeg?query=param", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/image.jpeg?query=param&another=one", + .basename = "image.jpeg", }, (url_test_t){ .url = "https://host.test/images/", - .filename = "images", + .basename = "images", + }, + (url_test_t){ + .url = "https://host.test/images/../../file", + .basename = "file", + }, + (url_test_t){ + .url = "https://host.test/images/../../file/..", + .basename = "index.html", + }, + (url_test_t){ + .url = "https://host.test/images/..//", + .basename = "index.html", }, (url_test_t){ .url = "https://host.test/", - .filename = "index.html", + .basename = "index.html", }, (url_test_t){ .url = "https://host.test", - .filename = "index.html", + .basename = "index.html", }, (url_test_t){ .url = "aesgcm://host.test", - .filename = "index.html", + .basename = "index.html", }, }; - char *filename; + char *basename; for(int i = 0; i < num_tests; i++) { - filename = http_filename_from_url(tests[i].url); - assert_string_equal(filename, tests[i].filename); - } -} - -void http_filename_from_header_td(void **state) { - int num_tests = 11; - header_test_t tests[] = { - (header_test_t){ - .header = "Content-Disposition: filename=image.jpeg", - .filename = "image.jpeg", - }, - (header_test_t){ - .header = "Content-Disposition:filename=image.jpeg", - .filename = "image.jpeg", - }, - (header_test_t){ - .header = "CoNteNt-DiSpoSItioN: filename=image.jpeg", - .filename = "image.jpeg", - }, - (header_test_t){ - .header = "Content-Disposition: attachment; filename=image.jpeg", - .filename = "image.jpeg", - }, - (header_test_t){ - .header = "Content-Disposition: filename=", - .filename = NULL, - }, - (header_test_t){ - .header = "Content-Disposition: filename=;", - .filename = NULL, - }, - (header_test_t){ - .header = "Content-Disposition: inline", - .filename = NULL, - }, - (header_test_t){ - .header = "Content-Disposition:", - .filename = NULL, - }, - (header_test_t){ - .header = "Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT ", - .filename = NULL, - }, - (header_test_t){ - .header = "", - .filename = NULL, - }, - (header_test_t){ - .header = NULL, - .filename = NULL, - }, - }; - - char *got_filename; - char *exp_filename; - char *header; - for(int i = 0; i < num_tests; i++) { - header = tests[i].header; - exp_filename = tests[i].filename; - - got_filename = http_filename_from_header(header); - - if (exp_filename == NULL) { - assert_null(got_filename); - } else { - assert_string_equal(got_filename, exp_filename); - } + basename = http_basename_from_url(tests[i].url); + assert_string_equal(basename, tests[i].basename); } } diff --git a/tests/unittests/test_http_download.h b/tests/unittests/test_http_download.h index c8c333ee..ce8913eb 100644 --- a/tests/unittests/test_http_download.h +++ b/tests/unittests/test_http_download.h @@ -1,2 +1 @@ -void http_filename_from_url_td(void **state); -void http_filename_from_header_td(void **state); +void http_basename_from_url_td(void **state); diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index 9e9833bc..06c1b307 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -628,8 +628,7 @@ main(int argc, char* argv[]) unit_test(removes_plugin_features), unit_test(does_not_remove_feature_when_more_than_one_reference), - unit_test(http_filename_from_url_td), - unit_test(http_filename_from_header_td), + unit_test(http_basename_from_url_td), }; return run_tests(all_tests); From 4711fc62a3d691d35400bc7316f9026c64227d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 20 Jul 2020 13:01:05 +0200 Subject: [PATCH 14/31] Run make format on rebase --- src/command/cmd_funcs.c | 138 ++-- src/command/cmd_funcs.h | 236 +++---- src/config/preferences.c | 776 ++++++++++----------- src/omemo/crypto.c | 20 +- src/omemo/crypto.h | 18 +- src/omemo/omemo.c | 13 +- src/omemo/omemo.h | 8 +- src/tools/http_download.c | 54 +- src/tools/http_download.h | 17 +- src/tools/http_upload.c | 31 +- src/tools/http_upload.h | 19 +- src/ui/console.c | 2 +- src/ui/window.c | 2 +- src/ui/window.h | 2 +- tests/unittests/omemo/stub_omemo.c | 41 +- tests/unittests/test_http_download.c | 103 +-- tests/unittests/test_http_download.h | 2 +- tests/unittests/tools/stub_http_download.c | 22 +- tests/unittests/ui/stub_ui.c | 105 +-- 19 files changed, 782 insertions(+), 827 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index fb39f104..cfacd370 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4807,16 +4807,18 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args) return TRUE; } - -char *_add_omemo_stream(int *fd, FILE **fh, char **err) { +#ifdef HAVE_OMEMO +char* +_add_omemo_stream(int* fd, FILE** fh, char** err) +{ // Create temporary file for writing ciphertext. int tmpfd; - char *tmpname = NULL; + 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"); + FILE* tmpfh = fdopen(tmpfd, "wb"); // The temporary ciphertext file should be removed after it has // been closed. @@ -4824,7 +4826,7 @@ char *_add_omemo_stream(int *fd, FILE **fh, char **err) { free(tmpname); int crypt_res; - char *fragment; + char* fragment; fragment = omemo_encrypt_file(*fh, tmpfh, file_size(*fd), &crypt_res); if (crypt_res != 0) { fclose(tmpfh); @@ -4843,14 +4845,15 @@ char *_add_omemo_stream(int *fd, FILE **fh, char **err) { return fragment; } +#endif gboolean cmd_sendfile(ProfWin* window, const char* const command, gchar** args) { jabber_conn_status_t conn_status = connection_get_status(); - char *filename = args[0]; - char *alt_scheme = NULL; - char *alt_fragment = NULL; + char* filename = args[0]; + char* alt_scheme = NULL; + char* alt_fragment = NULL; // expand ~ to $HOME if (filename[0] == '~' && filename[1] == '/') { @@ -4887,58 +4890,46 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args) goto out; } - FILE *fh = fdopen(fd, "rb"); + FILE* fh = fdopen(fd, "rb"); switch (window->type) { - case WIN_MUC: - case WIN_CHAT: - { - ProfChatWin *chatwin = (ProfChatWin*)window; + 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; - } + 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; } - case WIN_PRIVATE: // We don't support encryption in private MUC windows. - default: - cons_show_error("Unsupported window for file transmission."); - goto out; +#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 - break; - } + case WIN_PRIVATE: // We don't support encryption in private MUC windows. default: cons_show_error("Unsupported window for file transmission."); - free(filename); - return TRUE; + goto out; } - HTTPUpload *upload = malloc(sizeof(HTTPUpload)); + HTTPUpload* upload = malloc(sizeof(HTTPUpload)); upload->window = window; upload->filename = strdup(filename); @@ -8847,7 +8838,7 @@ cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args) } gboolean -cmd_save(ProfWin *window, const char *const command, gchar **args) +cmd_save(ProfWin* window, const char* const command, gchar** args) { log_info("Saving preferences to configuration file"); cons_show("Saving preferences."); @@ -9123,8 +9114,8 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) 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, OMEMO_AESGCM_URL_SCHEME)) { + gchar* scheme = g_uri_parse_scheme(args[1]); + if (0 == g_strcmp0(scheme, "aesgcm")) { require_save = true; } g_free(scheme); @@ -9163,15 +9154,16 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) return TRUE; } - -void _url_save_fallback_method(ProfWin *window, const char *url, const char *filename) { - FILE *fh = fopen(filename, "wb"); +void +_url_save_fallback_method(ProfWin* window, const char* url, const char* filename) +{ + FILE* fh = fopen(filename, "wb"); if (!fh) { cons_show_error("Cannot open file '%s' for writing.", filename); return; } - HTTPDownload *download = malloc(sizeof(HTTPDownload)); + HTTPDownload* download = malloc(sizeof(HTTPDownload)); download->window = window; download->url = strdup(url); download->filehandle = fh; @@ -9180,8 +9172,10 @@ void _url_save_fallback_method(ProfWin *window, const char *url, const char *fil http_download_add_download(download); } -void _url_save_external_method(const char *scheme_cmd, const char *url, const char *filename) { - gchar **argv = g_strsplit(scheme_cmd, " ", 0); +void +_url_save_external_method(const char* scheme_cmd, const char* url, const char* filename) +{ + gchar** argv = g_strsplit(scheme_cmd, " ", 0); guint num_args = 0; while (argv[num_args]) { @@ -9202,11 +9196,13 @@ void _url_save_external_method(const char *scheme_cmd, const char *url, const ch } } -char *_make_unique_filename(const char *filename) { - char *unique = strdup(filename); +char* +_make_unique_filename(const char* filename) +{ + char* unique = strdup(filename); unsigned int i = 0; - while(g_file_test(unique, G_FILE_TEST_EXISTS)) { + while (g_file_test(unique, G_FILE_TEST_EXISTS)) { free(unique); if (i > 1000) { // Give up after 1000 attempts. @@ -9222,11 +9218,9 @@ char *_make_unique_filename(const char *filename) { } 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_error("`/url save` is not supported in this window."); return TRUE; } @@ -9236,18 +9230,18 @@ cmd_url_save(ProfWin *window, const char *const command, gchar **args) return TRUE; } - gchar *url = args[1]; - gchar *path = g_strdup(args[2]); + gchar* url = args[1]; + gchar* path = g_strdup(args[2]); - gchar *scheme = g_uri_parse_scheme(url); + gchar* scheme = g_uri_parse_scheme(url); if (scheme == NULL) { cons_show("URL '%s' is not valid.", url); g_free(url); return TRUE; } - gchar *directory = NULL; - gchar *basename = NULL; + gchar* directory = NULL; + gchar* basename = NULL; if (path != NULL) { directory = g_path_get_dirname(path); basename = g_path_get_basename(path); @@ -9267,10 +9261,10 @@ cmd_url_save(ProfWin *window, const char *const command, gchar **args) basename = http_basename_from_url(url); } - char *filename = NULL; + char* filename = NULL; filename = g_build_filename(directory, basename, NULL); - char *unique_filename = _make_unique_filename(filename); + char* unique_filename = _make_unique_filename(filename); if (!unique_filename) { cons_show_error("Failed to generate an unique filename from '%s'.", filename); free(filename); @@ -9280,11 +9274,11 @@ cmd_url_save(ProfWin *window, const char *const command, gchar **args) free(filename); filename = unique_filename; - gchar *scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); + gchar* scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); if (scheme_cmd == NULL) { if (g_strcmp0(scheme, "http") == 0 - || g_strcmp0(scheme, "https") == 0 - || g_strcmp0(scheme, OMEMO_AESGCM_URL_SCHEME) == 0) { + || g_strcmp0(scheme, "https") == 0 + || g_strcmp0(scheme, "aesgcm") == 0) { _url_save_fallback_method(window, url, filename); } else { cons_show_error("No download method defined for the scheme '%s'.", scheme); diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index f6fac7ca..d5311bba 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -111,133 +111,133 @@ gboolean cmd_pgp(ProfWin* window, const char* const command, gchar** args); #ifdef HAVE_LIBGPGME gboolean cmd_ox(ProfWin* window, const char* const command, gchar** args); #endif // HAVE_LIBGPGME -gboolean cmd_outtype(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_prefs(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_priority(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_quit(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_reconnect(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_room(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_rooms(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_bookmark(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_bookmark_ignore(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_roster(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_software(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_splash(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_states(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_status_get(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_status_set(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_sub(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_theme(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_tiny(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_wintitle(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_vercheck(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_who(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_win(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_alias(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_xmlconsole(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_ping(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_form(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_occupants(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_kick(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_ban(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_subject(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_affiliation(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_role(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_privileges(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_presence(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_wrap(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_time(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_resource(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_inpblock(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_titlebar(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_titlebar_show_hide(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_mainwin(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_statusbar(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_inputwin(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_script(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_export(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_charset(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_console(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_command_list(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_command_exec(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_outtype(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_prefs(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_priority(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_quit(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_reconnect(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_room(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_rooms(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_bookmark(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_bookmark_ignore(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_roster(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_software(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_splash(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_states(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_status_get(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_status_set(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_sub(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_theme(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_tiny(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_wintitle(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_vercheck(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_who(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_win(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_alias(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_xmlconsole(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_ping(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_form(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_occupants(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_kick(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_ban(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_subject(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_affiliation(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_role(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_privileges(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_presence(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_wrap(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_time(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_resource(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_inpblock(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_titlebar(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_titlebar_show_hide(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_mainwin(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_statusbar(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_inputwin(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_script(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_export(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_charset(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_console(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_command_list(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_command_exec(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_plugins(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_sourcepath(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_install(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_update(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_uninstall(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_load(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_unload(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_reload(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_plugins_python_version(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_plugins(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_sourcepath(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_install(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_update(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_uninstall(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_load(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_unload(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_reload(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_plugins_python_version(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_blocked(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_blocked(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_account(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_list(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_show(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_add(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_remove(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_enable(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_disable(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_rename(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_default(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_set(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_account_clear(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_account(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_list(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_show(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_add(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_remove(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_enable(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_disable(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_rename(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_default(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_set(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_account_clear(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_tls_certpath(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_tls_trust(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_tls_trusted(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_tls_revoke(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_tls_cert(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_tls_certpath(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_tls_trust(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_tls_trusted(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_tls_revoke(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_tls_cert(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_otr_char(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_log(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_libver(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_policy(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_gen(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_myfp(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_theirfp(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_start(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_end(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_trust(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_untrust(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_secret(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_question(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_answer(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_otr_sendfile(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_otr_char(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_log(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_libver(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_policy(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_gen(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_myfp(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_theirfp(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_start(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_end(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_trust(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_untrust(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_secret(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_question(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_answer(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_otr_sendfile(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_wins(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_wins_unread(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_wins_prune(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_wins(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_wins_unread(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_wins_prune(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_wins_swap(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); +gboolean cmd_form_field(ProfWin* window, char* tag, gchar** args); -gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_omemo_char(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_omemo_log(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_omemo_trust(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_clear_device_list(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_omemo_gen(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_char(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_log(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_start(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_end(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_fingerprint(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_trust(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_clear_device_list(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_save(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_reload(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_paste(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_color(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_avatar(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_os(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_correction(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_correct(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_slashguard(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_save(ProfWin *window, const char *const command, gchar **args); -gboolean cmd_executable(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_paste(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_color(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_avatar(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_os(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_correction(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_correct(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_slashguard(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_save(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_executable(ProfWin* window, const char* const command, gchar** args); #endif diff --git a/src/config/preferences.c b/src/config/preferences.c index e1ff4a06..0511212e 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1790,145 +1790,144 @@ _save_prefs(void) static const char* _get_group(preference_t pref) { - switch (pref) - { - case PREF_CLEAR_PERSIST_HISTORY: - case PREF_SPLASH: - case PREF_BEEP: - case PREF_THEME: - case PREF_VERCHECK: - case PREF_WINTITLE_SHOW: - case PREF_WINTITLE_GOODBYE: - case PREF_FLASH: - case PREF_INTYPE: - case PREF_HISTORY: - case PREF_OCCUPANTS: - case PREF_OCCUPANTS_JID: - case PREF_OCCUPANTS_WRAP: - case PREF_STATUSES: - case PREF_STATUSES_CONSOLE: - case PREF_STATUSES_CHAT: - case PREF_STATUSES_MUC: - case PREF_MUC_PRIVILEGES: - case PREF_PRESENCE: - case PREF_WRAP: - case PREF_TIME_CONSOLE: - case PREF_TIME_CHAT: - case PREF_TIME_MUC: - case PREF_TIME_CONFIG: - case PREF_TIME_PRIVATE: - case PREF_TIME_XMLCONSOLE: - case PREF_TIME_STATUSBAR: - case PREF_TIME_LASTACTIVITY: - case PREF_ROSTER: - case PREF_ROSTER_OFFLINE: - case PREF_ROSTER_RESOURCE: - case PREF_ROSTER_PRESENCE: - case PREF_ROSTER_STATUS: - case PREF_ROSTER_EMPTY: - case PREF_ROSTER_BY: - case PREF_ROSTER_ORDER: - case PREF_ROSTER_UNREAD: - case PREF_ROSTER_COUNT: - case PREF_ROSTER_COUNT_ZERO: - case PREF_ROSTER_PRIORITY: - case PREF_ROSTER_WRAP: - case PREF_ROSTER_RESOURCE_JOIN: - case PREF_ROSTER_CONTACTS: - case PREF_ROSTER_UNSUBSCRIBED: - case PREF_ROSTER_ROOMS: - case PREF_ROSTER_ROOMS_POS: - case PREF_ROSTER_ROOMS_BY: - case PREF_ROSTER_ROOMS_ORDER: - case PREF_ROSTER_ROOMS_UNREAD: - case PREF_ROSTER_ROOMS_SERVER: - case PREF_ROSTER_ROOMS_USE_AS_NAME: - case PREF_ROSTER_PRIVATE: - case PREF_RESOURCE_TITLE: - case PREF_RESOURCE_MESSAGE: - case PREF_ENC_WARN: - case PREF_INPBLOCK_DYNAMIC: - case PREF_TLS_SHOW: - case PREF_CONSOLE_MUC: - case PREF_CONSOLE_PRIVATE: - case PREF_CONSOLE_CHAT: - case PREF_COLOR_NICK: - case PREF_COLOR_NICK_OWN: - case PREF_ROSTER_COLOR_NICK: - case PREF_OCCUPANTS_COLOR_NICK: - case PREF_STATUSBAR_SHOW_NAME: - case PREF_STATUSBAR_SHOW_NUMBER: - case PREF_STATUSBAR_SHOW_READ: - case PREF_STATUSBAR_SELF: - case PREF_STATUSBAR_CHAT: - case PREF_STATUSBAR_ROOM: - case PREF_TITLEBAR_MUC_TITLE_JID: - case PREF_TITLEBAR_MUC_TITLE_NAME: - case PREF_SLASH_GUARD: - return PREF_GROUP_UI; - case PREF_STATES: - case PREF_OUTTYPE: - return PREF_GROUP_CHATSTATES; - case PREF_NOTIFY_TYPING: - case PREF_NOTIFY_TYPING_CURRENT: - case PREF_NOTIFY_CHAT: - case PREF_NOTIFY_CHAT_CURRENT: - case PREF_NOTIFY_CHAT_TEXT: - case PREF_NOTIFY_ROOM: - case PREF_NOTIFY_ROOM_MENTION: - case PREF_NOTIFY_ROOM_TRIGGER: - case PREF_NOTIFY_ROOM_CURRENT: - case PREF_NOTIFY_ROOM_TEXT: - case PREF_NOTIFY_INVITE: - case PREF_NOTIFY_SUB: - case PREF_NOTIFY_MENTION_CASE_SENSITIVE: - case PREF_NOTIFY_MENTION_WHOLE_WORD: - case PREF_TRAY: - case PREF_TRAY_READ: - case PREF_ADV_NOTIFY_DISCO_OR_VERSION: - return PREF_GROUP_NOTIFICATIONS; - case PREF_CHLOG: - case PREF_GRLOG: - case PREF_LOG_ROTATE: - case PREF_LOG_SHARED: - return PREF_GROUP_LOGGING; - case PREF_AVATAR_CMD: - case PREF_URL_OPEN_CMD: - case PREF_URL_SAVE_CMD: - return PREF_GROUP_EXECUTABLES; - case PREF_AUTOAWAY_CHECK: - case PREF_AUTOAWAY_MODE: - case PREF_AUTOAWAY_MESSAGE: - case PREF_AUTOXA_MESSAGE: - case PREF_LASTACTIVITY: - return PREF_GROUP_PRESENCE; - case PREF_CONNECT_ACCOUNT: - case PREF_DEFAULT_ACCOUNT: - case PREF_CARBONS: - case PREF_RECEIPTS_SEND: - case PREF_RECEIPTS_REQUEST: - case PREF_REVEAL_OS: - case PREF_TLS_CERTPATH: - case PREF_CORRECTION_ALLOW: - case PREF_MAM: - return PREF_GROUP_CONNECTION; - case PREF_OTR_LOG: - case PREF_OTR_POLICY: - case PREF_OTR_SENDFILE: - return PREF_GROUP_OTR; - case PREF_PGP_LOG: - case PREF_PGP_SENDFILE: - return PREF_GROUP_PGP; - case PREF_BOOKMARK_INVITE: - case PREF_ROOM_LIST_CACHE: - return PREF_GROUP_MUC; - case PREF_PLUGINS_SOURCEPATH: - return PREF_GROUP_PLUGINS; - case PREF_OMEMO_LOG: - case PREF_OMEMO_POLICY: - return PREF_GROUP_OMEMO; - default: - return NULL; + switch (pref) { + case PREF_CLEAR_PERSIST_HISTORY: + case PREF_SPLASH: + case PREF_BEEP: + case PREF_THEME: + case PREF_VERCHECK: + case PREF_WINTITLE_SHOW: + case PREF_WINTITLE_GOODBYE: + case PREF_FLASH: + case PREF_INTYPE: + case PREF_HISTORY: + case PREF_OCCUPANTS: + case PREF_OCCUPANTS_JID: + case PREF_OCCUPANTS_WRAP: + case PREF_STATUSES: + case PREF_STATUSES_CONSOLE: + case PREF_STATUSES_CHAT: + case PREF_STATUSES_MUC: + case PREF_MUC_PRIVILEGES: + case PREF_PRESENCE: + case PREF_WRAP: + case PREF_TIME_CONSOLE: + case PREF_TIME_CHAT: + case PREF_TIME_MUC: + case PREF_TIME_CONFIG: + case PREF_TIME_PRIVATE: + case PREF_TIME_XMLCONSOLE: + case PREF_TIME_STATUSBAR: + case PREF_TIME_LASTACTIVITY: + case PREF_ROSTER: + case PREF_ROSTER_OFFLINE: + case PREF_ROSTER_RESOURCE: + case PREF_ROSTER_PRESENCE: + case PREF_ROSTER_STATUS: + case PREF_ROSTER_EMPTY: + case PREF_ROSTER_BY: + case PREF_ROSTER_ORDER: + case PREF_ROSTER_UNREAD: + case PREF_ROSTER_COUNT: + case PREF_ROSTER_COUNT_ZERO: + case PREF_ROSTER_PRIORITY: + case PREF_ROSTER_WRAP: + case PREF_ROSTER_RESOURCE_JOIN: + case PREF_ROSTER_CONTACTS: + case PREF_ROSTER_UNSUBSCRIBED: + case PREF_ROSTER_ROOMS: + case PREF_ROSTER_ROOMS_POS: + case PREF_ROSTER_ROOMS_BY: + case PREF_ROSTER_ROOMS_ORDER: + case PREF_ROSTER_ROOMS_UNREAD: + case PREF_ROSTER_ROOMS_SERVER: + case PREF_ROSTER_ROOMS_USE_AS_NAME: + case PREF_ROSTER_PRIVATE: + case PREF_RESOURCE_TITLE: + case PREF_RESOURCE_MESSAGE: + case PREF_ENC_WARN: + case PREF_INPBLOCK_DYNAMIC: + case PREF_TLS_SHOW: + case PREF_CONSOLE_MUC: + case PREF_CONSOLE_PRIVATE: + case PREF_CONSOLE_CHAT: + case PREF_COLOR_NICK: + case PREF_COLOR_NICK_OWN: + case PREF_ROSTER_COLOR_NICK: + case PREF_OCCUPANTS_COLOR_NICK: + case PREF_STATUSBAR_SHOW_NAME: + case PREF_STATUSBAR_SHOW_NUMBER: + case PREF_STATUSBAR_SHOW_READ: + case PREF_STATUSBAR_SELF: + case PREF_STATUSBAR_CHAT: + case PREF_STATUSBAR_ROOM: + case PREF_TITLEBAR_MUC_TITLE_JID: + case PREF_TITLEBAR_MUC_TITLE_NAME: + case PREF_SLASH_GUARD: + return PREF_GROUP_UI; + case PREF_STATES: + case PREF_OUTTYPE: + return PREF_GROUP_CHATSTATES; + case PREF_NOTIFY_TYPING: + case PREF_NOTIFY_TYPING_CURRENT: + case PREF_NOTIFY_CHAT: + case PREF_NOTIFY_CHAT_CURRENT: + case PREF_NOTIFY_CHAT_TEXT: + case PREF_NOTIFY_ROOM: + case PREF_NOTIFY_ROOM_MENTION: + case PREF_NOTIFY_ROOM_TRIGGER: + case PREF_NOTIFY_ROOM_CURRENT: + case PREF_NOTIFY_ROOM_TEXT: + case PREF_NOTIFY_INVITE: + case PREF_NOTIFY_SUB: + case PREF_NOTIFY_MENTION_CASE_SENSITIVE: + case PREF_NOTIFY_MENTION_WHOLE_WORD: + case PREF_TRAY: + case PREF_TRAY_READ: + case PREF_ADV_NOTIFY_DISCO_OR_VERSION: + return PREF_GROUP_NOTIFICATIONS; + case PREF_CHLOG: + case PREF_GRLOG: + case PREF_LOG_ROTATE: + case PREF_LOG_SHARED: + return PREF_GROUP_LOGGING; + case PREF_AVATAR_CMD: + case PREF_URL_OPEN_CMD: + case PREF_URL_SAVE_CMD: + return PREF_GROUP_EXECUTABLES; + case PREF_AUTOAWAY_CHECK: + case PREF_AUTOAWAY_MODE: + case PREF_AUTOAWAY_MESSAGE: + case PREF_AUTOXA_MESSAGE: + case PREF_LASTACTIVITY: + return PREF_GROUP_PRESENCE; + case PREF_CONNECT_ACCOUNT: + case PREF_DEFAULT_ACCOUNT: + case PREF_CARBONS: + case PREF_RECEIPTS_SEND: + case PREF_RECEIPTS_REQUEST: + case PREF_REVEAL_OS: + case PREF_TLS_CERTPATH: + case PREF_CORRECTION_ALLOW: + case PREF_MAM: + return PREF_GROUP_CONNECTION; + case PREF_OTR_LOG: + case PREF_OTR_POLICY: + case PREF_OTR_SENDFILE: + return PREF_GROUP_OTR; + case PREF_PGP_LOG: + case PREF_PGP_SENDFILE: + return PREF_GROUP_PGP; + case PREF_BOOKMARK_INVITE: + case PREF_ROOM_LIST_CACHE: + return PREF_GROUP_MUC; + case PREF_PLUGINS_SOURCEPATH: + return PREF_GROUP_PLUGINS; + case PREF_OMEMO_LOG: + case PREF_OMEMO_POLICY: + return PREF_GROUP_OMEMO; + default: + return NULL; } } @@ -1937,256 +1936,255 @@ _get_group(preference_t pref) static const char* _get_key(preference_t pref) { - switch (pref) - { - case PREF_CLEAR_PERSIST_HISTORY: - return "clear.persist_history"; - case PREF_SPLASH: - return "splash"; - case PREF_BEEP: - return "beep"; - case PREF_THEME: - return "theme"; - case PREF_VERCHECK: - return "vercheck"; - case PREF_WINTITLE_SHOW: - return "wintitle.show"; - case PREF_WINTITLE_GOODBYE: - return "wintitle.goodbye"; - case PREF_FLASH: - return "flash"; - case PREF_TRAY: - return "tray"; - case PREF_TRAY_READ: - return "tray.read"; - case PREF_ADV_NOTIFY_DISCO_OR_VERSION: - return "adv.notify.discoversion"; - case PREF_INTYPE: - return "intype"; - case PREF_HISTORY: - return "history"; - case PREF_CARBONS: - return "carbons"; - case PREF_RECEIPTS_SEND: - return "receipts.send"; - case PREF_RECEIPTS_REQUEST: - return "receipts.request"; - case PREF_REVEAL_OS: - return "reveal.os"; - case PREF_OCCUPANTS: - return "occupants"; - case PREF_OCCUPANTS_JID: - return "occupants.jid"; - case PREF_OCCUPANTS_WRAP: - return "occupants.wrap"; - case PREF_MUC_PRIVILEGES: - return "privileges"; - case PREF_STATUSES: - return "statuses"; - case PREF_STATUSES_CONSOLE: - return "statuses.console"; - case PREF_STATUSES_CHAT: - return "statuses.chat"; - case PREF_STATUSES_MUC: - return "statuses.muc"; - case PREF_STATES: - return "enabled"; - case PREF_OUTTYPE: - return "outtype"; - case PREF_NOTIFY_TYPING: - return "typing"; - case PREF_NOTIFY_TYPING_CURRENT: - return "typing.current"; - case PREF_NOTIFY_CHAT: - return "message"; - case PREF_NOTIFY_CHAT_CURRENT: - return "message.current"; - case PREF_NOTIFY_CHAT_TEXT: - return "message.text"; - case PREF_NOTIFY_ROOM: - return "room"; - case PREF_NOTIFY_ROOM_TRIGGER: - return "room.trigger"; - case PREF_NOTIFY_ROOM_MENTION: - return "room.mention"; - case PREF_NOTIFY_ROOM_CURRENT: - return "room.current"; - case PREF_NOTIFY_ROOM_TEXT: - return "room.text"; - case PREF_NOTIFY_INVITE: - return "invite"; - case PREF_NOTIFY_SUB: - return "sub"; - case PREF_NOTIFY_MENTION_CASE_SENSITIVE: - return "room.mention.casesensitive"; - case PREF_NOTIFY_MENTION_WHOLE_WORD: - return "room.mention.wholeword"; - case PREF_CHLOG: - return "chlog"; - case PREF_GRLOG: - return "grlog"; - case PREF_AUTOAWAY_CHECK: - return "autoaway.check"; - case PREF_AUTOAWAY_MODE: - return "autoaway.mode"; - case PREF_AUTOAWAY_MESSAGE: - return "autoaway.awaymessage"; - case PREF_AUTOXA_MESSAGE: - return "autoaway.xamessage"; - case PREF_CONNECT_ACCOUNT: - return "account"; - case PREF_DEFAULT_ACCOUNT: - return "defaccount"; - case PREF_OTR_LOG: - return "log"; - case PREF_OTR_POLICY: - return "policy"; - case PREF_OTR_SENDFILE: - return "sendfile"; - case PREF_LOG_ROTATE: - return "rotate"; - case PREF_LOG_SHARED: - return "shared"; - case PREF_PRESENCE: - return "presence"; - case PREF_WRAP: - return "wrap"; - case PREF_TIME_CONSOLE: - return "time.console"; - case PREF_TIME_CHAT: - return "time.chat"; - case PREF_TIME_MUC: - return "time.muc"; - case PREF_TIME_CONFIG: - return "time.config"; - case PREF_TIME_PRIVATE: - return "time.private"; - case PREF_TIME_XMLCONSOLE: - return "time.xmlconsole"; - case PREF_TIME_STATUSBAR: - return "time.statusbar"; - case PREF_TIME_LASTACTIVITY: - return "time.lastactivity"; - case PREF_ROSTER: - return "roster"; - case PREF_ROSTER_OFFLINE: - return "roster.offline"; - case PREF_ROSTER_RESOURCE: - return "roster.resource"; - case PREF_ROSTER_PRESENCE: - return "roster.presence"; - case PREF_ROSTER_STATUS: - return "roster.status"; - case PREF_ROSTER_EMPTY: - return "roster.empty"; - case PREF_ROSTER_BY: - return "roster.by"; - case PREF_ROSTER_ORDER: - return "roster.order"; - case PREF_ROSTER_UNREAD: - return "roster.unread"; - case PREF_ROSTER_COUNT: - return "roster.count"; - case PREF_ROSTER_COUNT_ZERO: - return "roster.count.zero"; - case PREF_ROSTER_PRIORITY: - return "roster.priority"; - case PREF_ROSTER_WRAP: - return "roster.wrap"; - case PREF_ROSTER_RESOURCE_JOIN: - return "roster.resource.join"; - case PREF_ROSTER_CONTACTS: - return "roster.contacts"; - case PREF_ROSTER_UNSUBSCRIBED: - return "roster.unsubscribed"; - case PREF_ROSTER_ROOMS: - return "roster.rooms"; - case PREF_ROSTER_ROOMS_POS: - return "roster.rooms.pos"; - case PREF_ROSTER_ROOMS_BY: - return "roster.rooms.by"; - case PREF_ROSTER_ROOMS_ORDER: - return "roster.rooms.order"; - case PREF_ROSTER_ROOMS_UNREAD: - return "roster.rooms.unread"; - case PREF_ROSTER_ROOMS_SERVER: - return "roster.rooms.server"; - case PREF_ROSTER_ROOMS_USE_AS_NAME: - return "roster.rooms.use.name"; - case PREF_ROSTER_PRIVATE: - return "roster.private"; - case PREF_RESOURCE_TITLE: - return "resource.title"; - case PREF_RESOURCE_MESSAGE: - return "resource.message"; - case PREF_INPBLOCK_DYNAMIC: - return "inpblock.dynamic"; - case PREF_ENC_WARN: - return "enc.warn"; - case PREF_TITLEBAR_MUC_TITLE_JID: - return "titlebar.muc.title.jid"; - case PREF_TITLEBAR_MUC_TITLE_NAME: - return "titlebar.muc.title.name"; - case PREF_PGP_LOG: - return "log"; - case PREF_PGP_SENDFILE: - return "sendfile"; - case PREF_TLS_CERTPATH: - return "tls.certpath"; - case PREF_TLS_SHOW: - return "tls.show"; - case PREF_LASTACTIVITY: - return "lastactivity"; - case PREF_CONSOLE_MUC: - return "console.muc"; - case PREF_CONSOLE_PRIVATE: - return "console.private"; - case PREF_CONSOLE_CHAT: - return "console.chat"; - case PREF_COLOR_NICK: - return "color.nick"; - case PREF_COLOR_NICK_OWN: - return "color.nick.own"; - case PREF_ROSTER_COLOR_NICK: - return "color.roster.nick"; - case PREF_OCCUPANTS_COLOR_NICK: - return "color.occupants.nick"; - case PREF_BOOKMARK_INVITE: - return "bookmark.invite"; - case PREF_PLUGINS_SOURCEPATH: - return "sourcepath"; - case PREF_ROOM_LIST_CACHE: - return "rooms.cache"; - case PREF_STATUSBAR_SHOW_NAME: - return "statusbar.show.name"; - case PREF_STATUSBAR_SHOW_NUMBER: - return "statusbar.show.number"; - case PREF_STATUSBAR_SHOW_READ: - return "statusbar.show.read"; - case PREF_STATUSBAR_SELF: - return "statusbar.self"; - case PREF_STATUSBAR_CHAT: - return "statusbar.chat"; - case PREF_STATUSBAR_ROOM: - return "statusbar.room"; - case PREF_OMEMO_LOG: - return "log"; - case PREF_OMEMO_POLICY: - return "policy"; - case PREF_CORRECTION_ALLOW: - return "correction.allow"; - case PREF_AVATAR_CMD: - return "avatar.cmd"; - case PREF_SLASH_GUARD: - return "slashguard"; - case PREF_MAM: - return "mam"; - case PREF_URL_OPEN_CMD: - return "url.open.cmd"; - case PREF_URL_SAVE_CMD: - return "url.save.cmd"; - default: - return NULL; + switch (pref) { + case PREF_CLEAR_PERSIST_HISTORY: + return "clear.persist_history"; + case PREF_SPLASH: + return "splash"; + case PREF_BEEP: + return "beep"; + case PREF_THEME: + return "theme"; + case PREF_VERCHECK: + return "vercheck"; + case PREF_WINTITLE_SHOW: + return "wintitle.show"; + case PREF_WINTITLE_GOODBYE: + return "wintitle.goodbye"; + case PREF_FLASH: + return "flash"; + case PREF_TRAY: + return "tray"; + case PREF_TRAY_READ: + return "tray.read"; + case PREF_ADV_NOTIFY_DISCO_OR_VERSION: + return "adv.notify.discoversion"; + case PREF_INTYPE: + return "intype"; + case PREF_HISTORY: + return "history"; + case PREF_CARBONS: + return "carbons"; + case PREF_RECEIPTS_SEND: + return "receipts.send"; + case PREF_RECEIPTS_REQUEST: + return "receipts.request"; + case PREF_REVEAL_OS: + return "reveal.os"; + case PREF_OCCUPANTS: + return "occupants"; + case PREF_OCCUPANTS_JID: + return "occupants.jid"; + case PREF_OCCUPANTS_WRAP: + return "occupants.wrap"; + case PREF_MUC_PRIVILEGES: + return "privileges"; + case PREF_STATUSES: + return "statuses"; + case PREF_STATUSES_CONSOLE: + return "statuses.console"; + case PREF_STATUSES_CHAT: + return "statuses.chat"; + case PREF_STATUSES_MUC: + return "statuses.muc"; + case PREF_STATES: + return "enabled"; + case PREF_OUTTYPE: + return "outtype"; + case PREF_NOTIFY_TYPING: + return "typing"; + case PREF_NOTIFY_TYPING_CURRENT: + return "typing.current"; + case PREF_NOTIFY_CHAT: + return "message"; + case PREF_NOTIFY_CHAT_CURRENT: + return "message.current"; + case PREF_NOTIFY_CHAT_TEXT: + return "message.text"; + case PREF_NOTIFY_ROOM: + return "room"; + case PREF_NOTIFY_ROOM_TRIGGER: + return "room.trigger"; + case PREF_NOTIFY_ROOM_MENTION: + return "room.mention"; + case PREF_NOTIFY_ROOM_CURRENT: + return "room.current"; + case PREF_NOTIFY_ROOM_TEXT: + return "room.text"; + case PREF_NOTIFY_INVITE: + return "invite"; + case PREF_NOTIFY_SUB: + return "sub"; + case PREF_NOTIFY_MENTION_CASE_SENSITIVE: + return "room.mention.casesensitive"; + case PREF_NOTIFY_MENTION_WHOLE_WORD: + return "room.mention.wholeword"; + case PREF_CHLOG: + return "chlog"; + case PREF_GRLOG: + return "grlog"; + case PREF_AUTOAWAY_CHECK: + return "autoaway.check"; + case PREF_AUTOAWAY_MODE: + return "autoaway.mode"; + case PREF_AUTOAWAY_MESSAGE: + return "autoaway.awaymessage"; + case PREF_AUTOXA_MESSAGE: + return "autoaway.xamessage"; + case PREF_CONNECT_ACCOUNT: + return "account"; + case PREF_DEFAULT_ACCOUNT: + return "defaccount"; + case PREF_OTR_LOG: + return "log"; + case PREF_OTR_POLICY: + return "policy"; + case PREF_OTR_SENDFILE: + return "sendfile"; + case PREF_LOG_ROTATE: + return "rotate"; + case PREF_LOG_SHARED: + return "shared"; + case PREF_PRESENCE: + return "presence"; + case PREF_WRAP: + return "wrap"; + case PREF_TIME_CONSOLE: + return "time.console"; + case PREF_TIME_CHAT: + return "time.chat"; + case PREF_TIME_MUC: + return "time.muc"; + case PREF_TIME_CONFIG: + return "time.config"; + case PREF_TIME_PRIVATE: + return "time.private"; + case PREF_TIME_XMLCONSOLE: + return "time.xmlconsole"; + case PREF_TIME_STATUSBAR: + return "time.statusbar"; + case PREF_TIME_LASTACTIVITY: + return "time.lastactivity"; + case PREF_ROSTER: + return "roster"; + case PREF_ROSTER_OFFLINE: + return "roster.offline"; + case PREF_ROSTER_RESOURCE: + return "roster.resource"; + case PREF_ROSTER_PRESENCE: + return "roster.presence"; + case PREF_ROSTER_STATUS: + return "roster.status"; + case PREF_ROSTER_EMPTY: + return "roster.empty"; + case PREF_ROSTER_BY: + return "roster.by"; + case PREF_ROSTER_ORDER: + return "roster.order"; + case PREF_ROSTER_UNREAD: + return "roster.unread"; + case PREF_ROSTER_COUNT: + return "roster.count"; + case PREF_ROSTER_COUNT_ZERO: + return "roster.count.zero"; + case PREF_ROSTER_PRIORITY: + return "roster.priority"; + case PREF_ROSTER_WRAP: + return "roster.wrap"; + case PREF_ROSTER_RESOURCE_JOIN: + return "roster.resource.join"; + case PREF_ROSTER_CONTACTS: + return "roster.contacts"; + case PREF_ROSTER_UNSUBSCRIBED: + return "roster.unsubscribed"; + case PREF_ROSTER_ROOMS: + return "roster.rooms"; + case PREF_ROSTER_ROOMS_POS: + return "roster.rooms.pos"; + case PREF_ROSTER_ROOMS_BY: + return "roster.rooms.by"; + case PREF_ROSTER_ROOMS_ORDER: + return "roster.rooms.order"; + case PREF_ROSTER_ROOMS_UNREAD: + return "roster.rooms.unread"; + case PREF_ROSTER_ROOMS_SERVER: + return "roster.rooms.server"; + case PREF_ROSTER_ROOMS_USE_AS_NAME: + return "roster.rooms.use.name"; + case PREF_ROSTER_PRIVATE: + return "roster.private"; + case PREF_RESOURCE_TITLE: + return "resource.title"; + case PREF_RESOURCE_MESSAGE: + return "resource.message"; + case PREF_INPBLOCK_DYNAMIC: + return "inpblock.dynamic"; + case PREF_ENC_WARN: + return "enc.warn"; + case PREF_TITLEBAR_MUC_TITLE_JID: + return "titlebar.muc.title.jid"; + case PREF_TITLEBAR_MUC_TITLE_NAME: + return "titlebar.muc.title.name"; + case PREF_PGP_LOG: + return "log"; + case PREF_PGP_SENDFILE: + return "sendfile"; + case PREF_TLS_CERTPATH: + return "tls.certpath"; + case PREF_TLS_SHOW: + return "tls.show"; + case PREF_LASTACTIVITY: + return "lastactivity"; + case PREF_CONSOLE_MUC: + return "console.muc"; + case PREF_CONSOLE_PRIVATE: + return "console.private"; + case PREF_CONSOLE_CHAT: + return "console.chat"; + case PREF_COLOR_NICK: + return "color.nick"; + case PREF_COLOR_NICK_OWN: + return "color.nick.own"; + case PREF_ROSTER_COLOR_NICK: + return "color.roster.nick"; + case PREF_OCCUPANTS_COLOR_NICK: + return "color.occupants.nick"; + case PREF_BOOKMARK_INVITE: + return "bookmark.invite"; + case PREF_PLUGINS_SOURCEPATH: + return "sourcepath"; + case PREF_ROOM_LIST_CACHE: + return "rooms.cache"; + case PREF_STATUSBAR_SHOW_NAME: + return "statusbar.show.name"; + case PREF_STATUSBAR_SHOW_NUMBER: + return "statusbar.show.number"; + case PREF_STATUSBAR_SHOW_READ: + return "statusbar.show.read"; + case PREF_STATUSBAR_SELF: + return "statusbar.self"; + case PREF_STATUSBAR_CHAT: + return "statusbar.chat"; + case PREF_STATUSBAR_ROOM: + return "statusbar.room"; + case PREF_OMEMO_LOG: + return "log"; + case PREF_OMEMO_POLICY: + return "policy"; + case PREF_CORRECTION_ALLOW: + return "correction.allow"; + case PREF_AVATAR_CMD: + return "avatar.cmd"; + case PREF_SLASH_GUARD: + return "slashguard"; + case PREF_MAM: + return "mam"; + case PREF_URL_OPEN_CMD: + return "url.open.cmd"; + case PREF_URL_SAVE_CMD: + return "url.save.cmd"; + default: + return NULL; } } diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index 3be64636..a9f72626 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -41,7 +41,7 @@ #include "omemo/omemo.h" #include "omemo/crypto.h" -#define AES256_GCM_TAG_LENGTH 16 +#define AES256_GCM_TAG_LENGTH 16 #define AES256_GCM_BUFFER_SIZE 1024 int @@ -377,8 +377,10 @@ out: return res; } -int aes256gcm_crypt_file(FILE *in, FILE *out, off_t file_size, - unsigned char key[], unsigned char nonce[], bool encrypt) { +int +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); @@ -393,7 +395,7 @@ int aes256gcm_crypt_file(FILE *in, FILE *out, off_t file_size, gcry_cipher_hd_t hd; res = gcry_cipher_open(&hd, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_GCM, - GCRY_CIPHER_SECURE); + GCRY_CIPHER_SECURE); if (res != GPG_ERR_NO_ERROR) { goto out; } @@ -463,18 +465,20 @@ out: return res; } -char *aes256gcm_create_secure_fragment(unsigned char *key, unsigned char *nonce) { +char* +aes256gcm_create_secure_fragment(unsigned char* key, unsigned char* nonce) +{ int key_size = AES256_GCM_KEY_LENGTH; int nonce_size = AES256_GCM_NONCE_LENGTH; - char *fragment = gcry_malloc_secure((nonce_size+key_size)*2+1); + 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]); + sprintf(&(fragment[i * 2]), "%02x", nonce[i]); } for (int i = 0; i < key_size; i++) { - sprintf(&(fragment[(i+nonce_size)*2]), "%02x", key[i]); + sprintf(&(fragment[(i + nonce_size) * 2]), "%02x", key[i]); } return fragment; diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index e8d91ecc..f0090daf 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -40,7 +40,7 @@ #define AES128_GCM_IV_LENGTH 12 #define AES128_GCM_TAG_LENGTH 16 -#define AES256_GCM_KEY_LENGTH 32 +#define AES256_GCM_KEY_LENGTH 32 #define AES256_GCM_NONCE_LENGTH 12 int omemo_crypto_init(void); @@ -181,13 +181,13 @@ int aes128gcm_encrypt(unsigned char* ciphertext, size_t* ciphertext_len, const unsigned char* const plaintext, size_t plaintext_len, const unsigned char* const iv, const unsigned char* const key); -int aes128gcm_decrypt(unsigned char *plaintext, - size_t *plaintext_len, const unsigned char *const ciphertext, - size_t ciphertext_len, const unsigned char *const iv, size_t iv_len, - const unsigned char *const key, const unsigned char *const tag); +int aes128gcm_decrypt(unsigned char* plaintext, + size_t* plaintext_len, const unsigned char* const ciphertext, + size_t ciphertext_len, const unsigned char* const iv, size_t iv_len, + const unsigned char* const key, const unsigned char* const tag); -int aes256gcm_crypt_file(FILE *in, FILE *out, off_t file_size, - unsigned char key[], unsigned char nonce[], bool encrypt); +int 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); +char* aes256gcm_create_secure_fragment(unsigned char* key, + unsigned char* nonce); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index c6f92e46..e08d3f06 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1654,13 +1654,16 @@ _generate_signed_pre_key(void) SIGNAL_UNREF(signed_pre_key); } - -void omemo_free(void *a) { +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( +char* +omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res) +{ + unsigned char* key = gcry_random_bytes_secure( AES256_GCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM); @@ -1668,7 +1671,7 @@ char *omemo_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res) { unsigned char nonce[AES256_GCM_NONCE_LENGTH]; gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH); - char *fragment = aes256gcm_create_secure_fragment(key, nonce); + 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) { diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 0bddd9cd..e875dadd 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -95,8 +95,8 @@ void omemo_start_muc_sessions(const char* const roomjid); void omemo_start_device_session(const char* const jid, uint32_t device_id, GList* prekeys, uint32_t signed_prekey_id, const unsigned char* const signed_prekey, size_t signed_prekey_len, const unsigned char* const signature, size_t signature_len, const unsigned char* const identity_key, size_t identity_key_len); 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_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_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_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res); -void omemo_free(void *a); +char* omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res); +void omemo_free(void* a); diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 3d4003f9..09e6bb6e 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -58,12 +58,12 @@ #define FALLBACK_MSG "" -GSList *download_processes = NULL; +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) +_xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - HTTPDownload *download = (HTTPDownload *)userdata; + HTTPDownload* download = (HTTPDownload*)userdata; pthread_mutex_lock(&lock); @@ -84,7 +84,7 @@ _xferinfo(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot dlperc = (100 * dlnow) / dltotal; } - char *msg; + char* msg; if (asprintf(&msg, "Downloading '%s': %d%%", download->url, dlperc) == -1) { msg = strdup(FALLBACK_MSG); } @@ -98,20 +98,20 @@ _xferinfo(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot #if LIBCURL_VERSION_NUM < 0x072000 static int -_older_progress(void *p, double dltotal, double dlnow, double ultotal, double ulnow) +_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) +void* +http_file_get(void* userdata) { - HTTPDownload *download = (HTTPDownload *)userdata; + HTTPDownload* download = (HTTPDownload*)userdata; - char *err = NULL; + char* err = NULL; - CURL *curl; + CURL* curl; CURLcode res; download->cancel = 0; @@ -125,7 +125,7 @@ http_file_get(void *userdata) win_print_http_transfer(download->window, msg, download->url); free(msg); - char *cert_path = prefs_get_string(PREF_TLS_CERTPATH); + char* cert_path = prefs_get_string(PREF_TLS_CERTPATH); pthread_mutex_unlock(&lock); curl_global_init(CURL_GLOBAL_ALL); @@ -133,16 +133,16 @@ http_file_get(void *userdata) curl_easy_setopt(curl, CURLOPT_URL, download->url); - #if LIBCURL_VERSION_NUM >= 0x072000 +#if LIBCURL_VERSION_NUM >= 0x072000 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, _xferinfo); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, download); - #else +#else curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, _older_progress); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, download); - #endif +#endif curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)download->filehandle); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)download->filehandle); curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity"); @@ -165,7 +165,7 @@ http_file_get(void *userdata) g_free(cert_path); if (err) { - char *msg; + char* msg; if (download->cancel) { if (asprintf(&msg, "Downloading '%s' failed: Download was canceled", download->url) == -1) { msg = strdup(FALLBACK_MSG); @@ -200,11 +200,11 @@ http_file_get(void *userdata) } void -http_download_cancel_processes(ProfWin *window) +http_download_cancel_processes(ProfWin* window) { - GSList *download_process = download_processes; + GSList* download_process = download_processes; while (download_process) { - HTTPDownload *download = download_process->data; + HTTPDownload* download = download_process->data; if (download->window == window) { download->cancel = 1; break; @@ -214,21 +214,23 @@ http_download_cancel_processes(ProfWin *window) } void -http_download_add_download(HTTPDownload *download) +http_download_add_download(HTTPDownload* download) { download_processes = g_slist_append(download_processes, download); } -char *http_basename_from_url(const char *url) { - const char *default_name = "index.html"; +char* +http_basename_from_url(const char* url) +{ + const char* default_name = "index.html"; - GFile *file = g_file_new_for_uri(url); - char *filename = g_file_get_basename(file); + GFile* file = g_file_new_for_uri(url); + char* filename = g_file_get_basename(file); g_object_unref(file); if (g_strcmp0(filename, ".") == 0 - || g_strcmp0(filename, "..") == 0 - || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { + || g_strcmp0(filename, "..") == 0 + || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { g_free(filename); return strdup(default_name); } diff --git a/src/tools/http_download.h b/src/tools/http_download.h index 868b99f2..ba8b5023 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -46,20 +46,21 @@ #include "ui/win_types.h" -typedef struct http_download_t { - char *url; - FILE *filehandle; +typedef struct http_download_t +{ + char* url; + FILE* filehandle; curl_off_t bytes_received; - ProfWin *window; + ProfWin* window; pthread_t worker; int cancel; } HTTPDownload; -void* http_file_get(void *userdata); +void* http_file_get(void* userdata); -void http_download_cancel_processes(ProfWin *window); -void http_download_add_download(HTTPDownload *download); +void http_download_cancel_processes(ProfWin* window); +void http_download_add_download(HTTPDownload* download); -char *http_basename_from_url(const char *url); +char* http_basename_from_url(const char* url); #endif diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 2fb32062..fcdd582a 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -128,9 +128,11 @@ _data_callback(void* ptr, size_t size, size_t nmemb, void* data) return realsize; } -int format_alt_url(char *original_url, char *new_scheme, char *new_fragment, char **new_url) { +int +format_alt_url(char* original_url, char* new_scheme, char* new_fragment, char** new_url) +{ int ret = 0; - CURLU *h = curl_url(); + CURLU* h = curl_url(); if ((ret = curl_url_set(h, CURLUPART_URL, original_url, 0)) != 0) { goto out; @@ -155,12 +157,12 @@ out: return ret; } -void * -http_file_put(void *userdata) +void* +http_file_put(void* userdata) { HTTPUpload* upload = (HTTPUpload*)userdata; - FILE *fh = NULL; + FILE* fh = NULL; char* err = NULL; char* content_type_header; @@ -283,10 +285,10 @@ http_file_put(void *userdata) win_mark_received(upload->window, upload->put_url); free(msg); - char *url = NULL; + 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) { + 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); @@ -295,21 +297,21 @@ http_file_put(void *userdata) switch (upload->window->type) { case WIN_CHAT: { - ProfChatWin *chatwin = (ProfChatWin*)(upload->window); + ProfChatWin* chatwin = (ProfChatWin*)(upload->window); assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); cl_ev_send_msg(chatwin, url, url); break; } case WIN_PRIVATE: { - ProfPrivateWin *privatewin = (ProfPrivateWin*)(upload->window); + ProfPrivateWin* privatewin = (ProfPrivateWin*)(upload->window); assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK); cl_ev_send_priv_msg(privatewin, url, url); break; } case WIN_MUC: { - ProfMucWin *mucwin = (ProfMucWin*)(upload->window); + ProfMucWin* mucwin = (ProfMucWin*)(upload->window); assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); cl_ev_send_muc_msg(mucwin, url, url); break; @@ -342,14 +344,14 @@ file_mime_type(const char* const filename) { char* out_mime_type; char file_header[FILE_HEADER_BYTES]; - FILE *fh; + FILE* fh; if (!(fh = fopen(filename, "rb"))) { return strdup(FALLBACK_MIMETYPE); } size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fh); fclose(fh); - char *content_type = g_content_type_guess(filename, (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) { char* mime_type = g_content_type_get_mime_type(content_type); out_mime_type = strdup(mime_type); @@ -361,7 +363,8 @@ file_mime_type(const char* const filename) return out_mime_type; } -off_t file_size(int filedes) +off_t +file_size(int filedes) { struct stat st; fstat(filedes, &st); diff --git a/src/tools/http_upload.h b/src/tools/http_upload.h index 9e801973..4e95d4d8 100644 --- a/src/tools/http_upload.h +++ b/src/tools/http_upload.h @@ -45,17 +45,18 @@ #include "ui/win_types.h" -typedef struct http_upload_t { - char *filename; - FILE *filehandle; +typedef struct http_upload_t +{ + char* filename; + FILE* filehandle; off_t filesize; curl_off_t bytes_sent; - char *mime_type; - char *get_url; - char *put_url; - char *alt_scheme; - char *alt_fragment; - ProfWin *window; + char* mime_type; + char* get_url; + char* put_url; + char* alt_scheme; + char* alt_fragment; + ProfWin* window; pthread_t worker; int cancel; } HTTPUpload; diff --git a/src/ui/console.c b/src/ui/console.c index 03a5b21f..8d028139 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2192,7 +2192,7 @@ cons_show_omemo_prefs(void) cons_show("OMEMO char (/omemo char) : %s", ch); free(ch); - cons_alert(); + cons_alert(NULL); } void diff --git a/src/ui/window.c b/src/ui/window.c index c03b3aab..6f77f107 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -1390,7 +1390,7 @@ win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* con } void -win_print_http_transfer(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); } diff --git a/src/ui/window.h b/src/ui/window.h index 86d1dbfd..7ff25a87 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -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_history(ProfWin* window, const ProfMessage* const message); -void win_print_http_transfer(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_redraw(ProfWin* window); diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c index 7cca34d5..f6cc4491 100644 --- a/tests/unittests/omemo/stub_omemo.c +++ b/tests/unittests/omemo/stub_omemo.c @@ -79,15 +79,38 @@ omemo_own_fingerprint(gboolean formatted) return NULL; } -void omemo_start_muc_sessions(const char *const roomjid) {} -void omemo_start_session(const char *const barejid) {} -void omemo_trust(const char *const jid, const char *const fingerprint_formatted) {} -void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) {} -void omemo_devicelist_publish(GList *device_list) {} -void omemo_publish_crypto_materials(void) {} -void omemo_start_sessions(void) {} +void +omemo_start_muc_sessions(const char* const roomjid) +{ +} +void +omemo_start_session(const char* const barejid) +{ +} +void +omemo_trust(const char* const jid, const char* const fingerprint_formatted) +{ +} +void +omemo_untrust(const char* const jid, const char* const fingerprint_formatted) +{ +} +void +omemo_devicelist_publish(GList* device_list) +{ +} +void +omemo_publish_crypto_materials(void) +{ +} +void +omemo_start_sessions(void) +{ +} -char *omemo_encrypt_file(FILE *in, FILE *out, off_t file_size, int *gcry_res) { +char* +omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res) +{ return NULL; }; -void omemo_free(void *a) {}; +void omemo_free(void* a){}; diff --git a/tests/unittests/test_http_download.c b/tests/unittests/test_http_download.c index c0516a66..96d45d03 100644 --- a/tests/unittests/test_http_download.c +++ b/tests/unittests/test_http_download.c @@ -10,62 +10,65 @@ #include "tools/http_download.h" -typedef struct { - char *url; - char *basename; +typedef struct +{ + char* url; + char* basename; } url_test_t; -void http_basename_from_url_td(void **state) { +void +http_basename_from_url_td(void** state) +{ int num_tests = 11; url_test_t tests[] = { - (url_test_t){ - .url = "https://host.test/image.jpeg", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/image.jpeg#somefragment", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/image.jpeg?query=param", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/image.jpeg?query=param&another=one", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/images/", - .basename = "images", - }, - (url_test_t){ - .url = "https://host.test/images/../../file", - .basename = "file", - }, - (url_test_t){ - .url = "https://host.test/images/../../file/..", - .basename = "index.html", - }, - (url_test_t){ - .url = "https://host.test/images/..//", - .basename = "index.html", - }, - (url_test_t){ - .url = "https://host.test/", - .basename = "index.html", - }, - (url_test_t){ - .url = "https://host.test", - .basename = "index.html", - }, - (url_test_t){ - .url = "aesgcm://host.test", - .basename = "index.html", - }, + (url_test_t){ + .url = "https://host.test/image.jpeg", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/image.jpeg#somefragment", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/image.jpeg?query=param", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/image.jpeg?query=param&another=one", + .basename = "image.jpeg", + }, + (url_test_t){ + .url = "https://host.test/images/", + .basename = "images", + }, + (url_test_t){ + .url = "https://host.test/images/../../file", + .basename = "file", + }, + (url_test_t){ + .url = "https://host.test/images/../../file/..", + .basename = "index.html", + }, + (url_test_t){ + .url = "https://host.test/images/..//", + .basename = "index.html", + }, + (url_test_t){ + .url = "https://host.test/", + .basename = "index.html", + }, + (url_test_t){ + .url = "https://host.test", + .basename = "index.html", + }, + (url_test_t){ + .url = "aesgcm://host.test", + .basename = "index.html", + }, }; - char *basename; - for(int i = 0; i < num_tests; i++) { + char* basename; + for (int i = 0; i < num_tests; i++) { basename = http_basename_from_url(tests[i].url); assert_string_equal(basename, tests[i].basename); } diff --git a/tests/unittests/test_http_download.h b/tests/unittests/test_http_download.h index ce8913eb..a1c62a7f 100644 --- a/tests/unittests/test_http_download.h +++ b/tests/unittests/test_http_download.h @@ -1 +1 @@ -void http_basename_from_url_td(void **state); +void http_basename_from_url_td(void** state); diff --git a/tests/unittests/tools/stub_http_download.c b/tests/unittests/tools/stub_http_download.c index a07146b4..202c9cbf 100644 --- a/tests/unittests/tools/stub_http_download.c +++ b/tests/unittests/tools/stub_http_download.c @@ -6,23 +6,23 @@ typedef struct prof_win_t ProfWin; -typedef struct http_download_t { - char *url; - char *filename; - char *directory; - FILE *filehandle; +typedef struct http_download_t +{ + char* url; + char* filename; + char* directory; + FILE* filehandle; curl_off_t bytes_received; - ProfWin *window; + ProfWin* window; pthread_t worker; int cancel; } HTTPDownload; +void* http_file_get(void* userdata); -void* http_file_get(void *userdata); +void http_download_cancel_processes(ProfWin* window); +void http_download_add_download(HTTPDownload* download); -void http_download_cancel_processes(ProfWin *window); -void http_download_add_download(HTTPDownload *download); - -char *http_filename_from_url(const char *url); +char* http_filename_from_url(const char* url); #endif diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index f284d04b..06f0d988 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -236,97 +236,16 @@ ui_contact_online(char* barejid, Resource* resource, GDateTime* last_activity) check_expected(last_activity); } -void ui_contact_typing(const char * const barejid, const char * const resource) {} -void chatwin_incoming_msg(ProfChatWin *chatwin, ProfMessage *message, gboolean win_created) {} -void chatwin_receipt_received(ProfChatWin *chatwin, const char * const id) {} - -void privwin_incoming_msg(ProfPrivateWin *privatewin, ProfMessage *message) {} - -void ui_disconnected(void) {} -void chatwin_recipient_gone(ProfChatWin *chatwin) {} - -void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode, gboolean request_receipt, const char *const replace_id) {} -void chatwin_outgoing_carbon(ProfChatWin *chatwin, ProfMessage *message) {} -void privwin_outgoing_msg(ProfPrivateWin *privwin, const char * const message) {} - -void privwin_occupant_offline(ProfPrivateWin *privwin) {} -void privwin_occupant_kicked(ProfPrivateWin *privwin, const char *const actor, const char *const reason) {} -void privwin_occupant_banned(ProfPrivateWin *privwin, const char *const actor, const char *const reason) {} -void privwin_occupant_online(ProfPrivateWin *privwin) {} -void privwin_message_occupant_offline(ProfPrivateWin *privwin) {} - -void privwin_message_left_room(ProfPrivateWin *privwin) {} - -void ui_room_join(const char * const roomjid, gboolean focus) {} -void ui_switch_to_room(const char * const roomjid) {} - -void mucwin_role_change(ProfMucWin *mucwin, const char * const role, const char * const actor, - const char * const reason) {} -void mucwin_affiliation_change(ProfMucWin *mucwin, const char * const affiliation, const char * const actor, - const char * const reason) {} -void mucwin_role_and_affiliation_change(ProfMucWin *mucwin, const char * const role, - const char * const affiliation, const char * const actor, const char * const reason) {} -void mucwin_occupant_role_change(ProfMucWin *mucwin, const char * const nick, const char * const role, - const char * const actor, const char * const reason) {} -void mucwin_occupant_affiliation_change(ProfMucWin *mucwin, const char * const nick, const char * const affiliation, - const char * const actor, const char * const reason) {} -void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char * const nick, const char * const role, - const char * const affiliation, const char * const actor, const char * const reason) {} -void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {} -void mucwin_history(ProfMucWin *mucwin, const ProfMessage *const message) {} -void mucwin_incoming_msg(ProfMucWin *mucwin, const ProfMessage *const message, GSList *mentions, GList *triggers, gboolean filter_reflection) {} -void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode, const char *const replace_id) {} -void mucwin_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {} -void mucwin_requires_config(ProfMucWin *mucwin) {} -void ui_room_destroy(const char * const roomjid) {} -void mucwin_info(ProfMucWin *mucwin) {} -void mucwin_show_role_list(ProfMucWin *mucwin, muc_role_t role) {} -void mucwin_show_affiliation_list(ProfMucWin *mucwin, muc_affiliation_t affiliation) {} -void mucwin_room_info_error(ProfMucWin *mucwin, const char * const error) {} -void mucwin_room_disco_info(ProfMucWin *mucwin, GSList *identities, GSList *features) {} -void ui_room_destroyed(const char * const roomjid, const char * const reason, const char * const new_jid, - const char * const password) {} -void ui_room_kicked(const char * const roomjid, const char * const actor, const char * const reason) {} -void mucwin_occupant_kicked(ProfMucWin *mucwin, const char * const nick, const char * const actor, - const char * const reason) {} -void ui_room_banned(const char * const roomjid, const char * const actor, const char * const reason) {} -void mucwin_occupant_banned(ProfMucWin *mucwin, const char * const nick, const char * const actor, - const char * const reason) {} -void ui_leave_room(const char * const roomjid) {} -void mucwin_broadcast(ProfMucWin *mucwin, const char * const message) {} -void mucwin_occupant_offline(ProfMucWin *mucwin, const char * const nick) {} -void mucwin_occupant_online(ProfMucWin *mucwin, const char * const nick, const char * const roles, - const char * const affiliation, const char * const show, const char * const status) {} -void mucwin_occupant_nick_change(ProfMucWin *mucwin, const char * const old_nick, const char * const nick) {} -void mucwin_nick_change(ProfMucWin *mucwin, const char * const nick) {} -void mucwin_occupant_presence(ProfMucWin *mucwin, const char * const nick, const char * const show, - const char * const status) {} -void mucwin_update_occupants(ProfMucWin *mucwin) {} -void mucwin_show_occupants(ProfMucWin *mucwin) {} -void mucwin_hide_occupants(ProfMucWin *mucwin) {} -void mucwin_set_enctext(ProfMucWin *mucwin, const char *const enctext) {} -void mucwin_unset_enctext(ProfMucWin *mucwin) {} -void mucwin_set_message_char(ProfMucWin *mucwin, const char *const ch) {} -void 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 ui_show_roster(void) {} -void ui_hide_roster(void) {} -void ui_roster_add(const char * const barejid, const char * const name) {} -void ui_roster_remove(const char * const barejid) {} -void ui_contact_already_in_group(const char * const contact, const char * const group) {} -void ui_contact_not_in_group(const char * const contact, const char * const group) {} -void ui_group_added(const char * const contact, const char * const group) {} -void ui_group_removed(const char * const contact, const char * const group) {} -void chatwin_contact_online(ProfChatWin *chatwin, Resource *resource, GDateTime *last_activity) {} -void chatwin_contact_offline(ProfChatWin *chatwin, char *resource, char *status) {} - -void ui_contact_offline(char *barejid, char *resource, char *status) {} - -void ui_handle_recipient_error(const char * const recipient, const char * const err_msg) +void +ui_contact_typing(const char* const barejid, const char* const resource) +{ +} +void +chatwin_incoming_msg(ProfChatWin* chatwin, ProfMessage* message, gboolean win_created) +{ +} +void +chatwin_receipt_received(ProfChatWin* chatwin, const char* const id) { } @@ -552,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 ui_show_roster(void) { From fb002a59b6dd0e2656987e9bdb72f6d3478d4e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 20 Jul 2020 14:57:32 +0200 Subject: [PATCH 15/31] Use fallback method when /executable urlsave is unset --- src/command/cmd_funcs.c | 30 ++++++++++++++++++++++++------ src/config/preferences.c | 2 -- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index cfacd370..b3914e0d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9212,6 +9212,8 @@ _make_unique_filename(const char* filename) if (asprintf(&unique, "%s.%u", filename, i) < 0) { return NULL; } + + i++; } return unique; @@ -9295,11 +9297,14 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) gboolean cmd_executable(ProfWin* window, const char* const command, gchar** args) { + guint num_args = g_strv_length(args); + if (g_strcmp0(args[0], "avatar") == 0) { prefs_set_string(PREF_AVATAR_CMD, args[1]); cons_show("Avatar command set to: %s", args[1]); + } else if (g_strcmp0(args[0], "urlopen") == 0) { - if (g_strv_length(args) < 4) { + if (num_args < 4) { cons_bad_cmd_usage(command); return TRUE; } @@ -9309,16 +9314,29 @@ cmd_executable(ProfWin* window, const char* const command, gchar** args) 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) { + + if (num_args < 3) { + cons_bad_cmd_usage(command); + return TRUE; + } + + if (g_strcmp0(args[1], "set") == 0 && num_args >= 4) { + gchar* str = g_strjoinv(" ", &args[3]); + prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[2], str); + cons_show("`url save` command set to: %s for scheme %s", str, args[2]); + g_free(str); + + } else if (g_strcmp0(args[1], "clear") == 0) { + prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[2], NULL); + cons_show("`url save` will use internal download method for scheme %s", args[2]); + + } else { cons_bad_cmd_usage(command); return TRUE; } - gchar* str = g_strjoinv(" ", &args[2]); - prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[1], str); - cons_show("`url save` command set to: %s for scheme %s", str, args[1]); - g_free(str); } else { cons_bad_cmd_usage(command); } diff --git a/src/config/preferences.c b/src/config/preferences.c index 0511212e..c7a74429 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -2318,8 +2318,6 @@ _get_default_string(preference_t pref) return "false"; case PREF_AVATAR_CMD: return "xdg-open"; - case PREF_URL_SAVE_CMD: - return "curl -o %p %u"; default: return NULL; } From 73f313b9212d652fecb13bcb82a0f162abb897a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 20 Jul 2020 22:49:50 +0200 Subject: [PATCH 16/31] Refactor OMEMO download into AESGCMDownload tool --- Makefile.am | 2 + src/command/cmd_funcs.c | 61 ++++++++++++++-- src/omemo/crypto.c | 8 +-- src/omemo/crypto.h | 3 - src/omemo/omemo.c | 106 ++++++++++++++++++++++++++-- src/omemo/omemo.h | 6 +- src/tools/aesgcm_download.c | 134 ++++++++++++++++++++++++++++++++++++ src/tools/aesgcm_download.h | 66 ++++++++++++++++++ src/tools/aesgcm_upload.c | 0 src/tools/aesgcm_upload.h | 0 src/tools/http_download.c | 20 +++++- src/tools/http_download.h | 3 + 12 files changed, 386 insertions(+), 23 deletions(-) create mode 100644 src/tools/aesgcm_download.c create mode 100644 src/tools/aesgcm_download.h create mode 100644 src/tools/aesgcm_upload.c create mode 100644 src/tools/aesgcm_upload.h diff --git a/Makefile.am b/Makefile.am index a2cf5598..5c34b35a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,8 @@ core_sources = \ src/tools/http_upload.h \ src/tools/http_download.c \ src/tools/http_download.h \ + src/tools/aesgcm_download.c \ + src/tools/aesgcm_download.h \ src/tools/bookmark_ignore.c \ src/tools/bookmark_ignore.h \ src/tools/autocomplete.c src/tools/autocomplete.h \ diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index b3914e0d..bf6d6843 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -69,6 +69,7 @@ #include "event/client_events.h" #include "tools/http_upload.h" #include "tools/http_download.h" +#include "tools/aesgcm_download.h" #include "tools/autocomplete.h" #include "tools/parser.h" #include "tools/bookmark_ignore.h" @@ -9154,6 +9155,40 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) return TRUE; } +void +_url_open_fallback_method(ProfWin* window, const char* url) +{ + /* + gboolean is_omemo_aesgcm = false; + gchar* scheme = g_uri_parse_scheme(url); + if (g_strcmp0(scheme, "aesgcm")) { + is_omemo_aesgcm = true; + } + free(scheme); + + if (is_omemo_aesgcm) { + int tmpfd; + char* tmpname = NULL; + if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) { + *err = "Unable to create temporary file for decryption stream."; + return NULL; + } + FILE* tmpfh = fdopen(tmpfd, "wb"); + + unsigned char* nonce; + unsigned char* key; + char* https_url = omemo_parse_aesgcm_url(url, nonce, key); + + _url_save_fallback_method(window, https_url, tmpname); + + int crypt_res = omemo_decrypt_file(tmpfh, + + remove(tmpname); + free(tmpname); + } + */ +} + void _url_save_fallback_method(ProfWin* window, const char* url, const char* filename) { @@ -9163,13 +9198,27 @@ _url_save_fallback_method(ProfWin* window, const char* url, const char* filename return; } - HTTPDownload* download = malloc(sizeof(HTTPDownload)); - download->window = window; - download->url = strdup(url); - download->filehandle = fh; + gchar* scheme = g_uri_parse_scheme(url); - pthread_create(&(download->worker), NULL, &http_file_get, download); - http_download_add_download(download); + if (g_strcmp0(scheme, "aesgcm") == 0) { + AESGCMDownload* download = malloc(sizeof(AESGCMDownload)); + download->window = window; + download->url = strdup(url); + download->filehandle = fh; + + pthread_create(&(download->worker), NULL, &aesgcm_file_get, download); + aesgcm_download_add_download(download); + } else { + HTTPDownload* download = malloc(sizeof(HTTPDownload)); + download->window = window; + download->url = strdup(url); + download->filehandle = fh; + + pthread_create(&(download->worker), NULL, &http_file_get, download); + http_download_add_download(download); + } + + free(scheme); } void diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index a9f72626..a05e160e 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -400,12 +400,12 @@ aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size, goto out; } - res = gcry_cipher_setkey(hd, key, AES256_GCM_KEY_LENGTH); + res = gcry_cipher_setkey(hd, key, OMEMO_AESGCM_KEY_LENGTH); if (res != GPG_ERR_NO_ERROR) { goto out; } - res = gcry_cipher_setiv(hd, nonce, AES256_GCM_NONCE_LENGTH); + res = gcry_cipher_setiv(hd, nonce, OMEMO_AESGCM_NONCE_LENGTH); if (res != GPG_ERR_NO_ERROR) { goto out; } @@ -468,8 +468,8 @@ out: char* aes256gcm_create_secure_fragment(unsigned char* key, unsigned char* nonce) { - int key_size = AES256_GCM_KEY_LENGTH; - int nonce_size = AES256_GCM_NONCE_LENGTH; + 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); diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index f0090daf..c1d508b9 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -40,9 +40,6 @@ #define AES128_GCM_IV_LENGTH 12 #define AES128_GCM_TAG_LENGTH 16 -#define AES256_GCM_KEY_LENGTH 32 -#define AES256_GCM_NONCE_LENGTH 12 - int omemo_crypto_init(void); /** * Callback for a secure random number generator. diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index e08d3f06..e19b724e 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -62,6 +62,9 @@ #include "xmpp/roster_list.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 void _generate_pre_keys(int count); @@ -1664,12 +1667,12 @@ char* omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res) { unsigned char* key = gcry_random_bytes_secure( - AES256_GCM_KEY_LENGTH, + OMEMO_AESGCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM); // Create nonce/IV with random bytes. - unsigned char nonce[AES256_GCM_NONCE_LENGTH]; - gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH); + 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); @@ -1684,7 +1687,96 @@ omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res) return fragment; } -//int omemo_decrypt_file(FILE *in, FILE *out, off_t file_size, -// unsigned char key[], unsigned char nonce[]) { -// return aes256gcm_crypt_file(in, out, file_size, key, nonce, false); -//} +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]; + } + } +} + +int +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); + + int 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; + } + + 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; +} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index e875dadd..a0e89916 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -40,7 +40,9 @@ #define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000 #define OMEMO_ERR_GCRYPT -20000 -#define OMEMO_AESGCM_URL_SCHEME "aesgcm" +#define OMEMO_AESGCM_NONCE_LENGTH 12 +#define OMEMO_AESGCM_KEY_LENGTH 32 +#define OMEMO_AESGCM_URL_SCHEME "aesgcm" typedef enum { PROF_OMEMOPOLICY_MANUAL, @@ -99,4 +101,6 @@ char* omemo_on_message_send(ProfWin* win, const char* const message, gboolean re 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); +int 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); diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c new file mode 100644 index 00000000..693eabe7 --- /dev/null +++ b/src/tools/aesgcm_download.c @@ -0,0 +1,134 @@ +/* + * aesgcm_download.c + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2020 William Wennerström + * + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "profanity.h" +#include "event/client_events.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; + + 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."); + return NULL; + } + + int tmpfd; + char* tmpname = NULL; + 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.", + https_url); + return NULL; + } + FILE* tmpfh = fdopen(tmpfd, "wb"); + + // Remove the file once it is closed. + remove(tmpname); + free(tmpname); + + HTTPDownload* http_dl = malloc(sizeof(HTTPDownload)); + http_dl->window = aesgcm_dl->window; + http_dl->worker = aesgcm_dl->worker; + http_dl->url = https_url; + http_dl->filehandle = tmpfh; + http_dl->close = 0; + + aesgcm_dl->http_dl = http_dl; + + // TODO: Verify result. + http_file_get(http_dl); + + // Force flush as the decrypt function will read from the same stream. + fflush(tmpfh); + rewind(tmpfh); + + int crypt_res = omemo_decrypt_file(tmpfh, aesgcm_dl->filehandle, + http_dl->bytes_received, fragment); + + fclose(tmpfh); + + if (crypt_res != 0) { + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url, + "Downloading '%s' failed: Failed to decrypt" + "file.", + https_url); + } + + fclose(aesgcm_dl->filehandle); + + 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); +} diff --git a/src/tools/aesgcm_download.h b/src/tools/aesgcm_download.h new file mode 100644 index 00000000..fc29a99e --- /dev/null +++ b/src/tools/aesgcm_download.h @@ -0,0 +1,66 @@ +/* + * aesgcm_download.h + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2020 William Wennerström + * + * 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 . + * + * 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 +#include +#include "tools/http_download.h" + +#include "ui/win_types.h" + +typedef struct aesgcm_download_t +{ + char* url; + FILE* filehandle; + 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); + +char* http_basename_from_url(const char* url); + +#endif diff --git a/src/tools/aesgcm_upload.c b/src/tools/aesgcm_upload.c new file mode 100644 index 00000000..e69de29b diff --git a/src/tools/aesgcm_upload.h b/src/tools/aesgcm_upload.h new file mode 100644 index 00000000..e69de29b diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 09e6bb6e..a86af172 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -157,13 +157,12 @@ http_file_get(void* userdata) curl_easy_cleanup(curl); curl_global_cleanup(); - if (download->filehandle) { + if (download->filehandle && download->close) { fclose(download->filehandle); } pthread_mutex_lock(&lock); g_free(cert_path); - if (err) { char* msg; if (download->cancel) { @@ -237,3 +236,20 @@ http_basename_from_url(const char* url) return filename; } + +void +http_print_transfer_update(ProfWin* window, char* url, + const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + char* msg; + if (vasprintf(&msg, fmt, args) == -1) { + msg = strdup(FALLBACK_MSG); + } + va_end(args); + + win_print_http_transfer(window, msg, url); + free(msg); +} diff --git a/src/tools/http_download.h b/src/tools/http_download.h index ba8b5023..797e1603 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -54,6 +54,7 @@ typedef struct http_download_t ProfWin* window; pthread_t worker; int cancel; + int close; } HTTPDownload; void* http_file_get(void* userdata); @@ -62,5 +63,7 @@ void http_download_cancel_processes(ProfWin* window); void http_download_add_download(HTTPDownload* download); char* http_basename_from_url(const char* url); +void http_print_transfer_update(ProfWin* window, char* url, + const char* fmt, ...); #endif From 62cbad1c6e43c64c02d61b732f2f25d4c8611403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 21 Jul 2020 09:31:47 +0200 Subject: [PATCH 17/31] Add I/O error handling and use filenames instead of file descriptors --- .gitignore | 1 + src/command/cmd_funcs.c | 10 +---- src/omemo/crypto.c | 5 +-- src/omemo/crypto.h | 5 ++- src/omemo/omemo.c | 5 ++- src/omemo/omemo.h | 3 +- src/tools/aesgcm_download.c | 87 +++++++++++++++++++++++++------------ src/tools/aesgcm_download.h | 2 +- src/tools/http_download.c | 52 ++++++++++++---------- src/tools/http_download.h | 3 +- 10 files changed, 102 insertions(+), 71 deletions(-) diff --git a/.gitignore b/.gitignore index da19d7c6..e06f5136 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,4 @@ breaks *.tar.* *.zip +*.log* diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index bf6d6843..1a554ce4 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9192,19 +9192,13 @@ _url_open_fallback_method(ProfWin* window, const char* url) void _url_save_fallback_method(ProfWin* window, const char* url, const char* filename) { - FILE* fh = fopen(filename, "wb"); - if (!fh) { - cons_show_error("Cannot open file '%s' for writing.", filename); - return; - } - gchar* scheme = g_uri_parse_scheme(url); if (g_strcmp0(scheme, "aesgcm") == 0) { AESGCMDownload* download = malloc(sizeof(AESGCMDownload)); download->window = window; download->url = strdup(url); - download->filehandle = fh; + download->filename = strdup(filename); pthread_create(&(download->worker), NULL, &aesgcm_file_get, download); aesgcm_download_add_download(download); @@ -9212,7 +9206,7 @@ _url_save_fallback_method(ProfWin* window, const char* url, const char* filename HTTPDownload* download = malloc(sizeof(HTTPDownload)); download->window = window; download->url = strdup(url); - download->filehandle = fh; + download->filename = strdup(filename); pthread_create(&(download->worker), NULL, &http_file_get, download); http_download_add_download(download); diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c index a05e160e..a4a2d5fc 100644 --- a/src/omemo/crypto.c +++ b/src/omemo/crypto.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "log.h" #include "omemo/omemo.h" @@ -377,7 +376,7 @@ out: return res; } -int +gcry_error_t aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size, unsigned char key[], unsigned char nonce[], bool encrypt) { @@ -416,7 +415,7 @@ aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size, 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) { + if (!bytes_available || ferror(in) != 0) { break; } diff --git a/src/omemo/crypto.h b/src/omemo/crypto.h index c1d508b9..5adbffd8 100644 --- a/src/omemo/crypto.h +++ b/src/omemo/crypto.h @@ -35,6 +35,7 @@ #include #include #include +#include #define AES128_GCM_KEY_LENGTH 16 #define AES128_GCM_IV_LENGTH 12 @@ -183,8 +184,8 @@ int aes128gcm_decrypt(unsigned char* plaintext, size_t ciphertext_len, const unsigned char* const iv, size_t iv_len, const unsigned char* const key, const unsigned char* const tag); -int aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size, - unsigned char key[], unsigned char nonce[], bool encrypt); +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); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index e19b724e..8b426320 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1713,7 +1713,7 @@ _bytes_from_hex(const char* hex, size_t hex_size, } } -int +gcry_error_t omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment) { char nonce_hex[AESGCM_URL_NONCE_LEN]; @@ -1733,7 +1733,8 @@ omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment) _bytes_from_hex(key_hex, AESGCM_URL_KEY_LEN, key, OMEMO_AESGCM_KEY_LENGTH); - int crypt_res = aes256gcm_crypt_file(in, out, file_size, key, nonce, false); + gcry_error_t crypt_res; + crypt_res = aes256gcm_crypt_file(in, out, file_size, key, nonce, false); gcry_free(key); diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index a0e89916..d90f11cf 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -101,6 +101,5 @@ char* omemo_on_message_send(ProfWin* win, const char* const message, gboolean re 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); -int omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment); -void omemo_free(void* a); +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); diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index 693eabe7..6e8b89c5 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -67,56 +67,87 @@ aesgcm_file_get(void* userdata) char* https_url = NULL; char* fragment = NULL; + const size_t err_len = 100; + char err_buf[err_len]; + 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."); + "Download failed: Cannot parse URL '%s'.", + aesgcm_dl->url); return NULL; } - int tmpfd; char* tmpname = NULL; - if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) { + if (g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL) == -1) { + strerror_r(errno, err_buf, err_len); http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url, "Downloading '%s' failed: Unable to create " - "temporary ciphertext file for writing.", - https_url); + "temporary ciphertext file for writing " + "(%s).", + https_url, err_buf); return NULL; } - FILE* tmpfh = fdopen(tmpfd, "wb"); - // Remove the file once it is closed. - remove(tmpname); - free(tmpname); + FILE* outfh = fopen(aesgcm_dl->filename, "wb"); + if (outfh == NULL) { + strerror_r(errno, err_buf, err_len); + 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, err_buf); + return NULL; + } HTTPDownload* http_dl = malloc(sizeof(HTTPDownload)); http_dl->window = aesgcm_dl->window; http_dl->worker = aesgcm_dl->worker; - http_dl->url = https_url; - http_dl->filehandle = tmpfh; - http_dl->close = 0; + http_dl->url = strdup(https_url); + http_dl->filename = strdup(tmpname); aesgcm_dl->http_dl = http_dl; - // TODO: Verify result. - http_file_get(http_dl); + http_file_get(http_dl); // TODO(wstrm): Verify result. - // Force flush as the decrypt function will read from the same stream. - fflush(tmpfh); - rewind(tmpfh); - - int crypt_res = omemo_decrypt_file(tmpfh, aesgcm_dl->filehandle, - http_dl->bytes_received, fragment); - - fclose(tmpfh); - - if (crypt_res != 0) { + FILE* tmpfh = fopen(tmpname, "rb"); + if (tmpfh == NULL) { + strerror_r(errno, err_buf, err_len); http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url, - "Downloading '%s' failed: Failed to decrypt" - "file.", - https_url); + "Downloading '%s' failed: Unable to open " + "temporary file at '%s' for reading (%s).", + aesgcm_dl->url, aesgcm_dl->filename, err_buf); + return NULL; } - fclose(aesgcm_dl->filehandle); + gcry_error_t crypt_res; + crypt_res = omemo_decrypt_file(tmpfh, outfh, + http_dl->bytes_received, fragment); + + if (fclose(tmpfh) == EOF) { + strerror_r(errno, err_buf, err_len); + cons_show_error(err_buf); + } + + remove(tmpname); + 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) { + strerror_r(errno, err_buf, err_len); + cons_show_error(err_buf); + } + + free(https_url); + free(fragment); + + free(aesgcm_dl->filename); + free(aesgcm_dl->url); + free(aesgcm_dl); return NULL; } diff --git a/src/tools/aesgcm_download.h b/src/tools/aesgcm_download.h index fc29a99e..ebc8a5f9 100644 --- a/src/tools/aesgcm_download.h +++ b/src/tools/aesgcm_download.h @@ -50,7 +50,7 @@ typedef struct aesgcm_download_t { char* url; - FILE* filehandle; + char* filename; ProfWin* window; pthread_t worker; HTTPDownload* http_dl; diff --git a/src/tools/http_download.c b/src/tools/http_download.c index a86af172..5859dc70 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -109,6 +109,9 @@ http_file_get(void* userdata) { HTTPDownload* download = (HTTPDownload*)userdata; + const size_t err_len = 100; + char err_buf[err_len]; + char* err = NULL; CURL* curl; @@ -118,12 +121,18 @@ http_file_get(void* userdata) download->bytes_received = 0; pthread_mutex_lock(&lock); - char* msg; - if (asprintf(&msg, "Downloading '%s': 0%%", download->url) == -1) { - msg = strdup(FALLBACK_MSG); + http_print_transfer_update(download->window, download->url, + "Downloading '%s': 0%%", download->url); + + FILE* outfh = fopen(download->filename, "wb"); + if (outfh == NULL) { + strerror_r(errno, err_buf, err_len); + 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, err_buf); + return NULL; } - win_print_http_transfer(download->window, msg, download->url); - free(msg); char* cert_path = prefs_get_string(PREF_TLS_CERTPATH); pthread_mutex_unlock(&lock); @@ -142,7 +151,7 @@ http_file_get(void* userdata) #endif curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)download->filehandle); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)outfh); curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity"); @@ -157,35 +166,31 @@ http_file_get(void* userdata) curl_easy_cleanup(curl); curl_global_cleanup(); - if (download->filehandle && download->close) { - fclose(download->filehandle); + if (fclose(outfh) == EOF) { + strerror_r(errno, err_buf, err_len); + err = strdup(err_buf); } pthread_mutex_lock(&lock); g_free(cert_path); if (err) { - char* msg; if (download->cancel) { - if (asprintf(&msg, "Downloading '%s' failed: Download was canceled", download->url) == -1) { - msg = strdup(FALLBACK_MSG); - } + http_print_transfer_update(download->window, download->url, + "Downloading '%s' failed: " + "Download was canceled", + download->url); } else { - if (asprintf(&msg, "Downloading '%s' failed: %s", download->url, err) == -1) { - msg = strdup(FALLBACK_MSG); - } - win_update_entry_message(download->window, download->url, msg); + http_print_transfer_update(download->window, download->url, + "Downloading '%s' failed: %s", + download->url, err); } - cons_show_error(msg); - free(msg); free(err); } else { if (!download->cancel) { - if (asprintf(&msg, "Downloading '%s': 100%%", download->url) == -1) { - msg = strdup(FALLBACK_MSG); - } - win_update_entry_message(download->window, download->url, msg); + http_print_transfer_update(download->window, download->url, + "Downloading '%s': 100%%", + download->url); win_mark_received(download->window, download->url); - free(msg); } } @@ -193,6 +198,7 @@ http_file_get(void* userdata) pthread_mutex_unlock(&lock); free(download->url); + free(download->filename); free(download); return NULL; diff --git a/src/tools/http_download.h b/src/tools/http_download.h index 797e1603..077e3e3d 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -49,12 +49,11 @@ typedef struct http_download_t { char* url; - FILE* filehandle; + char* filename; curl_off_t bytes_received; ProfWin* window; pthread_t worker; int cancel; - int close; } HTTPDownload; void* http_file_get(void* userdata); From 7a1eb730b1fff7b49def2b86730642cbe0f74d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 21 Jul 2020 09:48:22 +0200 Subject: [PATCH 18/31] Explicitly clear fragment from HTTP URL --- src/omemo/omemo.c | 6 ++++++ src/omemo/omemo.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 8b426320..2e698591 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -1767,6 +1767,12 @@ omemo_parse_aesgcm_url(const char* aesgcm_url, 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; diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index d90f11cf..b8d84498 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -101,5 +101,6 @@ char* omemo_on_message_send(ProfWin* win, const char* const message, gboolean re 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); +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); From 3d344cfeaa7708b5786e55d18af0cbcfc8ed25ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 21 Jul 2020 10:34:29 +0200 Subject: [PATCH 19/31] Move common http tool code to http_common --- Makefile.am | 2 + src/tools/aesgcm_download.c | 1 + src/tools/aesgcm_download.h | 3 +- src/tools/http_common.c | 98 +++++++++++++++++++++++++++++++++++++ src/tools/http_common.h | 48 ++++++++++++++++++ src/tools/http_download.c | 52 ++------------------ src/tools/http_download.h | 5 +- 7 files changed, 156 insertions(+), 53 deletions(-) create mode 100644 src/tools/http_common.c create mode 100644 src/tools/http_common.h diff --git a/Makefile.am b/Makefile.am index 5c34b35a..e6c38066 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,8 @@ core_sources = \ src/command/cmd_ac.h src/command/cmd_ac.c \ src/tools/parser.c \ src/tools/parser.h \ + src/tools/http_common.c \ + src/tools/http_common.h \ src/tools/http_upload.c \ src/tools/http_upload.h \ src/tools/http_download.c \ diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index 6e8b89c5..6b60ba08 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -50,6 +50,7 @@ #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" diff --git a/src/tools/aesgcm_download.h b/src/tools/aesgcm_download.h index ebc8a5f9..e172b89a 100644 --- a/src/tools/aesgcm_download.h +++ b/src/tools/aesgcm_download.h @@ -43,6 +43,7 @@ #include #include +#include "tools/http_common.h" #include "tools/http_download.h" #include "ui/win_types.h" @@ -61,6 +62,4 @@ void* aesgcm_file_get(void* userdata); void aesgcm_download_cancel_processes(ProfWin* window); void aesgcm_download_add_download(AESGCMDownload* download); -char* http_basename_from_url(const char* url); - #endif diff --git a/src/tools/http_common.c b/src/tools/http_common.c new file mode 100644 index 00000000..df6f9a64 --- /dev/null +++ b/src/tools/http_common.c @@ -0,0 +1,98 @@ +/* + * http_common.c + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2020 William Wennerström + * + * 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 . + * + * 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 +#include +#include +#include + +#include "tools/http_common.h" + +#define FALLBACK_MSG "" + +char* +http_basename_from_url(const char* url) +{ + const char* default_name = "index.html"; + + GFile* file = g_file_new_for_uri(url); + char* filename = g_file_get_basename(file); + g_object_unref(file); + + if (g_strcmp0(filename, ".") == 0 + || g_strcmp0(filename, "..") == 0 + || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { + g_free(filename); + return strdup(default_name); + } + + return filename; +} + +void +http_print_transfer_update(ProfWin* window, char* url, + const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + char* msg; + if (vasprintf(&msg, fmt, args) == -1) { + msg = strdup(FALLBACK_MSG); + } + va_end(args); + + win_update_entry_message(window, url, msg); + free(msg); +} + +void +http_print_transfer(ProfWin* window, char* url, + const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + char* msg; + if (vasprintf(&msg, fmt, args) == -1) { + msg = strdup(FALLBACK_MSG); + } + va_end(args); + + win_print_http_transfer(window, msg, url); + free(msg); +} diff --git a/src/tools/http_common.h b/src/tools/http_common.h new file mode 100644 index 00000000..41f16200 --- /dev/null +++ b/src/tools/http_common.h @@ -0,0 +1,48 @@ +/* + * http_common.h + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2020 William Wennerström + * + * 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 . + * + * 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 + +#define _GNU_SOURCE 1 + +#include "ui/window.h" + +char* http_basename_from_url(const char* url); +void http_print_transfer(ProfWin* window, char* url, const char* fmt, ...); +void http_print_transfer_update(ProfWin* window, char* url, + const char* fmt, ...); + +#endif diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 5859dc70..a470feec 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -56,8 +56,6 @@ #include "ui/window.h" #include "common.h" -#define FALLBACK_MSG "" - GSList* download_processes = NULL; static int @@ -84,12 +82,8 @@ _xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot dlperc = (100 * dlnow) / dltotal; } - char* msg; - if (asprintf(&msg, "Downloading '%s': %d%%", download->url, dlperc) == -1) { - msg = strdup(FALLBACK_MSG); - } - win_update_entry_message(download->window, download->url, msg); - free(msg); + http_print_transfer_update(download->window, download->url, + "Downloading '%s': %d%%", download->url, dlperc); pthread_mutex_unlock(&lock); @@ -121,8 +115,8 @@ http_file_get(void* userdata) download->bytes_received = 0; pthread_mutex_lock(&lock); - http_print_transfer_update(download->window, download->url, - "Downloading '%s': 0%%", download->url); + http_print_transfer(download->window, download->url, + "Downloading '%s': 0%%", download->url); FILE* outfh = fopen(download->filename, "wb"); if (outfh == NULL) { @@ -188,7 +182,7 @@ http_file_get(void* userdata) } else { if (!download->cancel) { http_print_transfer_update(download->window, download->url, - "Downloading '%s': 100%%", + "Downloading '%s': done", download->url); win_mark_received(download->window, download->url); } @@ -223,39 +217,3 @@ http_download_add_download(HTTPDownload* download) { download_processes = g_slist_append(download_processes, download); } - -char* -http_basename_from_url(const char* url) -{ - const char* default_name = "index.html"; - - GFile* file = g_file_new_for_uri(url); - char* filename = g_file_get_basename(file); - g_object_unref(file); - - if (g_strcmp0(filename, ".") == 0 - || g_strcmp0(filename, "..") == 0 - || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { - g_free(filename); - return strdup(default_name); - } - - return filename; -} - -void -http_print_transfer_update(ProfWin* window, char* url, - const char* fmt, ...) -{ - va_list args; - - va_start(args, fmt); - char* msg; - if (vasprintf(&msg, fmt, args) == -1) { - msg = strdup(FALLBACK_MSG); - } - va_end(args); - - win_print_http_transfer(window, msg, url); - free(msg); -} diff --git a/src/tools/http_download.h b/src/tools/http_download.h index 077e3e3d..b6ce42ca 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -45,6 +45,7 @@ #include #include "ui/win_types.h" +#include "tools/http_common.h" typedef struct http_download_t { @@ -61,8 +62,4 @@ void* http_file_get(void* userdata); void http_download_cancel_processes(ProfWin* window); void http_download_add_download(HTTPDownload* download); -char* http_basename_from_url(const char* url); -void http_print_transfer_update(ProfWin* window, char* url, - const char* fmt, ...); - #endif From ab83afe21b1a7d5d2aff5f029687ea659e05360f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 21 Jul 2020 11:15:48 +0200 Subject: [PATCH 20/31] Switch to g_strerror --- src/command/cmd_funcs.c | 34 ++++------------------------------ src/tools/aesgcm_download.c | 32 ++++++++++++++++---------------- src/tools/http_download.c | 10 +++------- 3 files changed, 23 insertions(+), 53 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 1a554ce4..ada53f9d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9156,37 +9156,11 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) } void -_url_open_fallback_method(ProfWin* window, const char* url) +_url_open_fallback_method(ProfWin* window, const char* url, const char* filename) { - /* - gboolean is_omemo_aesgcm = false; - gchar* scheme = g_uri_parse_scheme(url); - if (g_strcmp0(scheme, "aesgcm")) { - is_omemo_aesgcm = true; - } - free(scheme); - - if (is_omemo_aesgcm) { - int tmpfd; - char* tmpname = NULL; - if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) { - *err = "Unable to create temporary file for decryption stream."; - return NULL; - } - FILE* tmpfh = fdopen(tmpfd, "wb"); - - unsigned char* nonce; - unsigned char* key; - char* https_url = omemo_parse_aesgcm_url(url, nonce, key); - - _url_save_fallback_method(window, https_url, tmpname); - - int crypt_res = omemo_decrypt_file(tmpfh, - - remove(tmpname); - free(tmpname); - } - */ + // TODO(wstrm): Use _url_save_fallback_method?. + // We probably want to do the cmd_url_open in a separate thread and wait for + // the transfer to be finished before calling call_external. } void diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index 6b60ba08..d6a85d06 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -68,9 +68,6 @@ aesgcm_file_get(void* userdata) char* https_url = NULL; char* fragment = NULL; - const size_t err_len = 100; - char err_buf[err_len]; - 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'.", @@ -78,24 +75,29 @@ aesgcm_file_get(void* userdata) return NULL; } - char* tmpname = NULL; - if (g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL) == -1) { - strerror_r(errno, err_buf, err_len); + 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, err_buf); + https_url, g_strerror(errno)); return NULL; + } else { + // TODO(wstrm): Maybe refactor this to use file handles so we do not + // have to open a dummy file descriptor and then close it. + // It's pretty ugly this way... + close(tmpfd); } FILE* outfh = fopen(aesgcm_dl->filename, "wb"); if (outfh == NULL) { - strerror_r(errno, err_buf, err_len); 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, err_buf); + https_url, aesgcm_dl->filename, + g_strerror(errno)); return NULL; } @@ -111,11 +113,11 @@ aesgcm_file_get(void* userdata) FILE* tmpfh = fopen(tmpname, "rb"); if (tmpfh == NULL) { - strerror_r(errno, err_buf, err_len); 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, aesgcm_dl->filename, err_buf); + aesgcm_dl->url, aesgcm_dl->filename, + g_strerror(errno)); return NULL; } @@ -124,12 +126,11 @@ aesgcm_file_get(void* userdata) http_dl->bytes_received, fragment); if (fclose(tmpfh) == EOF) { - strerror_r(errno, err_buf, err_len); - cons_show_error(err_buf); + cons_show_error(g_strerror(errno)); } remove(tmpname); - free(tmpname); + g_free(tmpname); if (crypt_res != GPG_ERR_NO_ERROR) { http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url, @@ -139,8 +140,7 @@ aesgcm_file_get(void* userdata) } if (fclose(outfh) == EOF) { - strerror_r(errno, err_buf, err_len); - cons_show_error(err_buf); + cons_show_error(g_strerror(errno)); } free(https_url); diff --git a/src/tools/http_download.c b/src/tools/http_download.c index a470feec..d14ab0e8 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -103,9 +103,6 @@ http_file_get(void* userdata) { HTTPDownload* download = (HTTPDownload*)userdata; - const size_t err_len = 100; - char err_buf[err_len]; - char* err = NULL; CURL* curl; @@ -120,11 +117,11 @@ http_file_get(void* userdata) FILE* outfh = fopen(download->filename, "wb"); if (outfh == NULL) { - strerror_r(errno, err_buf, err_len); 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, err_buf); + download->url, download->filename, + g_strerror(errno)); return NULL; } @@ -161,8 +158,7 @@ http_file_get(void* userdata) curl_global_cleanup(); if (fclose(outfh) == EOF) { - strerror_r(errno, err_buf, err_len); - err = strdup(err_buf); + err = strdup(g_strerror(errno)); } pthread_mutex_lock(&lock); From be62b446f778ba6c064d6e054dfb7b5eacb1f1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 21 Jul 2020 11:36:09 +0200 Subject: [PATCH 21/31] Add stubs --- Makefile.am | 2 ++ src/command/cmd_funcs.c | 22 ++++++++++++------- tests/unittests/tools/stub_aesgcm_download.c | 23 ++++++++++++++++++++ tests/unittests/tools/stub_http_common.c | 16 ++++++++++++++ tests/unittests/tools/stub_http_download.c | 2 -- 5 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 tests/unittests/tools/stub_aesgcm_download.c create mode 100644 tests/unittests/tools/stub_http_common.c diff --git a/Makefile.am b/Makefile.am index e6c38066..9f2a99de 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,8 +126,10 @@ unittest_sources = \ tests/unittests/log/stub_log.c \ tests/unittests/database/stub_database.c \ tests/unittests/config/stub_accounts.c \ + tests/unittests/tools/stub_http_common.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/test_form.c tests/unittests/test_form.h \ tests/unittests/test_common.c tests/unittests/test_common.h \ diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index ada53f9d..c6557159 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -69,7 +69,6 @@ #include "event/client_events.h" #include "tools/http_upload.h" #include "tools/http_download.h" -#include "tools/aesgcm_download.h" #include "tools/autocomplete.h" #include "tools/parser.h" #include "tools/bookmark_ignore.h" @@ -97,6 +96,7 @@ #ifdef HAVE_OMEMO #include "omemo/omemo.h" #include "xmpp/omemo.h" +#include "tools/aesgcm_download.h" #endif #ifdef HAVE_GTK @@ -9168,6 +9168,7 @@ _url_save_fallback_method(ProfWin* window, const char* url, const char* filename { gchar* scheme = g_uri_parse_scheme(url); +#ifdef HAVE_OMEMO if (g_strcmp0(scheme, "aesgcm") == 0) { AESGCMDownload* download = malloc(sizeof(AESGCMDownload)); download->window = window; @@ -9176,15 +9177,20 @@ _url_save_fallback_method(ProfWin* window, const char* url, const char* filename pthread_create(&(download->worker), NULL, &aesgcm_file_get, download); aesgcm_download_add_download(download); - } else { - HTTPDownload* download = malloc(sizeof(HTTPDownload)); - download->window = window; - download->url = strdup(url); - download->filename = strdup(filename); - pthread_create(&(download->worker), NULL, &http_file_get, download); - http_download_add_download(download); + free(scheme); + + return; } +#endif + + HTTPDownload* download = malloc(sizeof(HTTPDownload)); + download->window = window; + download->url = strdup(url); + download->filename = strdup(filename); + + pthread_create(&(download->worker), NULL, &http_file_get, download); + http_download_add_download(download); free(scheme); } diff --git a/tests/unittests/tools/stub_aesgcm_download.c b/tests/unittests/tools/stub_aesgcm_download.c new file mode 100644 index 00000000..58696e80 --- /dev/null +++ b/tests/unittests/tools/stub_aesgcm_download.c @@ -0,0 +1,23 @@ +#ifndef TOOLS_AESGCM_DOWNLOAD_H +#define TOOLS_AESGCM_DOWNLOAD_H + +#include + +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); + +void aesgcm_download_cancel_processes(ProfWin* window); +void aesgcm_download_add_download(AESGCMDownload* download); + +#endif diff --git a/tests/unittests/tools/stub_http_common.c b/tests/unittests/tools/stub_http_common.c new file mode 100644 index 00000000..23e0a23f --- /dev/null +++ b/tests/unittests/tools/stub_http_common.c @@ -0,0 +1,16 @@ +#ifndef TOOLS_HTTP_COMMON_H +#define TOOLS_HTTP_COMMON_H + +typedef struct prof_win_t ProfWin; + +char* +http_basename_from_url(const char* url) +{ + return ""; +} + +void http_print_transfer(ProfWin* window, char* url, const char* fmt, ...); +void http_print_transfer_update(ProfWin* window, char* url, + const char* fmt, ...); + +#endif diff --git a/tests/unittests/tools/stub_http_download.c b/tests/unittests/tools/stub_http_download.c index 202c9cbf..fb2cb1b8 100644 --- a/tests/unittests/tools/stub_http_download.c +++ b/tests/unittests/tools/stub_http_download.c @@ -23,6 +23,4 @@ void* http_file_get(void* userdata); void http_download_cancel_processes(ProfWin* window); void http_download_add_download(HTTPDownload* download); -char* http_filename_from_url(const char* url); - #endif From 1bb6cecee69d5167220a18cc4c125c215784de66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 21 Jul 2020 13:11:50 +0200 Subject: [PATCH 22/31] Fix stubs and move some tests to http_common --- Makefile.am | 3 +- src/tools/http_common.c | 28 ++++++++----------- src/tools/http_common.h | 2 -- ...est_http_download.c => test_http_common.c} | 2 +- ...est_http_download.h => test_http_common.h} | 0 tests/unittests/tools/stub_aesgcm_download.c | 10 +++++-- tests/unittests/tools/stub_http_common.c | 16 ----------- tests/unittests/tools/stub_http_download.c | 5 ---- tests/unittests/tools/stub_http_upload.c | 3 +- tests/unittests/unittests.c | 2 +- 10 files changed, 23 insertions(+), 48 deletions(-) rename tests/unittests/{test_http_download.c => test_http_common.c} (98%) rename tests/unittests/{test_http_download.h => test_http_common.h} (100%) delete mode 100644 tests/unittests/tools/stub_http_common.c diff --git a/Makefile.am b/Makefile.am index 9f2a99de..aeb52abd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,7 +126,6 @@ unittest_sources = \ tests/unittests/log/stub_log.c \ tests/unittests/database/stub_database.c \ tests/unittests/config/stub_accounts.c \ - tests/unittests/tools/stub_http_common.c \ tests/unittests/tools/stub_http_upload.c \ tests/unittests/tools/stub_http_download.c \ tests/unittests/tools/stub_aesgcm_download.c \ @@ -156,7 +155,7 @@ unittest_sources = \ tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \ tests/unittests/test_callbacks.c tests/unittests/test_callbacks.h \ tests/unittests/test_plugins_disco.c tests/unittests/test_plugins_disco.h \ - tests/unittests/test_http_download.c tests/unittests/test_http_download.h \ + tests/unittests/test_http_common.c tests/unittests/test_http_common.h \ tests/unittests/unittests.c functionaltest_sources = \ diff --git a/src/tools/http_common.c b/src/tools/http_common.c index df6f9a64..dfd0aa87 100644 --- a/src/tools/http_common.c +++ b/src/tools/http_common.c @@ -64,35 +64,31 @@ http_basename_from_url(const char* url) } void -http_print_transfer_update(ProfWin* window, char* url, - const char* fmt, ...) +http_print_transfer_update(ProfWin* window, char* url, const char* fmt, ...) { va_list args; va_start(args, fmt); - char* msg; - if (vasprintf(&msg, fmt, args) == -1) { - msg = strdup(FALLBACK_MSG); - } + GString* msg = g_string_new(FALLBACK_MSG); + g_string_vprintf(msg, fmt, args); va_end(args); - win_update_entry_message(window, url, msg); - free(msg); + win_update_entry_message(window, url, msg->str); + + g_string_free(msg, TRUE); } void -http_print_transfer(ProfWin* window, char* url, - const char* fmt, ...) +http_print_transfer(ProfWin* window, char* url, const char* fmt, ...) { va_list args; va_start(args, fmt); - char* msg; - if (vasprintf(&msg, fmt, args) == -1) { - msg = strdup(FALLBACK_MSG); - } + GString* msg = g_string_new(FALLBACK_MSG); + g_string_vprintf(msg, fmt, args); va_end(args); - win_print_http_transfer(window, msg, url); - free(msg); + win_print_http_transfer(window, msg->str, url); + + g_string_free(msg, TRUE); } diff --git a/src/tools/http_common.h b/src/tools/http_common.h index 41f16200..3fbc6fcd 100644 --- a/src/tools/http_common.h +++ b/src/tools/http_common.h @@ -36,8 +36,6 @@ #ifndef TOOLS_HTTP_COMMON_H #define TOOLS_HTTP_COMMON_H -#define _GNU_SOURCE 1 - #include "ui/window.h" char* http_basename_from_url(const char* url); diff --git a/tests/unittests/test_http_download.c b/tests/unittests/test_http_common.c similarity index 98% rename from tests/unittests/test_http_download.c rename to tests/unittests/test_http_common.c index 96d45d03..195f370b 100644 --- a/tests/unittests/test_http_download.c +++ b/tests/unittests/test_http_common.c @@ -8,7 +8,7 @@ #include "config.h" -#include "tools/http_download.h" +#include "tools/http_common.c" typedef struct { diff --git a/tests/unittests/test_http_download.h b/tests/unittests/test_http_common.h similarity index 100% rename from tests/unittests/test_http_download.h rename to tests/unittests/test_http_common.h diff --git a/tests/unittests/tools/stub_aesgcm_download.c b/tests/unittests/tools/stub_aesgcm_download.c index 58696e80..6f4cc0ce 100644 --- a/tests/unittests/tools/stub_aesgcm_download.c +++ b/tests/unittests/tools/stub_aesgcm_download.c @@ -15,9 +15,13 @@ typedef struct aesgcm_download_t HTTPDownload* http_dl; } AESGCMDownload; -void* aesgcm_file_get(void* userdata); +void* +aesgcm_file_get(void* userdata) +{ + return NULL; +}; -void aesgcm_download_cancel_processes(ProfWin* window); -void aesgcm_download_add_download(AESGCMDownload* download); +void aesgcm_download_cancel_processes(ProfWin* window){}; +void aesgcm_download_add_download(AESGCMDownload* download){}; #endif diff --git a/tests/unittests/tools/stub_http_common.c b/tests/unittests/tools/stub_http_common.c deleted file mode 100644 index 23e0a23f..00000000 --- a/tests/unittests/tools/stub_http_common.c +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef TOOLS_HTTP_COMMON_H -#define TOOLS_HTTP_COMMON_H - -typedef struct prof_win_t ProfWin; - -char* -http_basename_from_url(const char* url) -{ - return ""; -} - -void http_print_transfer(ProfWin* window, char* url, const char* fmt, ...); -void http_print_transfer_update(ProfWin* window, char* url, - const char* fmt, ...); - -#endif diff --git a/tests/unittests/tools/stub_http_download.c b/tests/unittests/tools/stub_http_download.c index fb2cb1b8..5fa1c46e 100644 --- a/tests/unittests/tools/stub_http_download.c +++ b/tests/unittests/tools/stub_http_download.c @@ -18,9 +18,4 @@ typedef struct http_download_t int cancel; } HTTPDownload; -void* http_file_get(void* userdata); - -void http_download_cancel_processes(ProfWin* window); -void http_download_add_download(HTTPDownload* download); - #endif diff --git a/tests/unittests/tools/stub_http_upload.c b/tests/unittests/tools/stub_http_upload.c index 25a81708..1b79e02d 100644 --- a/tests/unittests/tools/stub_http_upload.c +++ b/tests/unittests/tools/stub_http_upload.c @@ -20,8 +20,6 @@ typedef struct http_upload_t int cancel; } HTTPUpload; -//GSList *upload_processes; - void* http_file_put(void* userdata) { @@ -33,6 +31,7 @@ file_mime_type(const char* const file_name) { return NULL; } + off_t file_size(const char* const file_name) { diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index 06c1b307..cab99bf5 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -38,7 +38,7 @@ #include "test_form.h" #include "test_callbacks.h" #include "test_plugins_disco.h" -#include "test_http_download.h" +#include "test_http_common.h" int main(int argc, char* argv[]) From 3a6597ee2967f91f49a1b4e17cf0595f37064587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Thu, 3 Dec 2020 16:43:07 +0100 Subject: [PATCH 23/31] Refactor for threaded external executable for built-in download methods --- Makefile.am | 5 +- src/command/cmd_funcs.c | 315 +++++++++++++++--------------------- src/common.c | 20 +++ src/common.h | 1 + src/config/files.h | 19 +-- src/config/preferences.c | 49 +----- src/config/preferences.h | 1 - src/omemo/omemo.c | 1 - src/omemo/omemo.h | 1 + src/tools/aesgcm_download.c | 21 +++ src/tools/aesgcm_download.h | 1 + src/tools/http_common.h | 4 +- src/tools/http_download.c | 20 +++ src/tools/http_download.h | 1 + src/ui/console.c | 4 +- 15 files changed, 216 insertions(+), 247 deletions(-) diff --git a/Makefile.am b/Makefile.am index aeb52abd..9b2f75c6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,8 +47,6 @@ core_sources = \ src/tools/http_upload.h \ src/tools/http_download.c \ src/tools/http_download.h \ - src/tools/aesgcm_download.c \ - src/tools/aesgcm_download.h \ src/tools/bookmark_ignore.c \ src/tools/bookmark_ignore.h \ src/tools/autocomplete.c src/tools/autocomplete.h \ @@ -200,7 +198,8 @@ otr4_sources = \ omemo_sources = \ 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 = \ tests/unittests/omemo/stub_omemo.c diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index c6557159..88461f1d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -60,6 +60,7 @@ #include "command/cmd_funcs.h" #include "command/cmd_defs.h" #include "command/cmd_ac.h" +#include "config/files.h" #include "config/accounts.h" #include "config/account.h" #include "config/preferences.h" @@ -1089,7 +1090,7 @@ _writecsv(int fd, const char* const str) size_t len = strlen(str); char* s = malloc(2 * len * sizeof(char)); char* c = s; - for (int i =0; i < strlen(str); i++) { + for (int i = 0; i < strlen(str); i++) { if (str[i] != '"') *c++ = str[i]; else { @@ -9058,169 +9059,59 @@ cmd_slashguard(ProfWin* window, const char* const command, gchar** args) return TRUE; } -gboolean -cmd_url_open(ProfWin* window, const char* const command, gchar** args) -{ - if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) { - cons_show("url open not supported in this window"); - return TRUE; - } - - if (args[1] == NULL) { - cons_bad_cmd_usage(command); - return TRUE; - } - - gboolean require_save = false; - - gchar* fileStart = g_strrstr(args[1], "/"); - if (fileStart == NULL) { - cons_show("URL '%s' is not valid.", args[1]); - return TRUE; - } - - fileStart++; - if (((char*)(fileStart - 2))[0] == '/' && ((char*)(fileStart - 3))[0] == ':') { - // If the '/' is last character of the '://' string, there will be no suffix - // Therefore, it is considered that there is no file name in the URL and - // fileStart is set to the end of the URL. - fileStart = args[1] + strlen(args[1]); - } - - gchar* suffix = NULL; - gchar* suffixStart = g_strrstr(fileStart, "."); - 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")) { - require_save = true; - } - 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; -} - -void -_url_open_fallback_method(ProfWin* window, const char* url, const char* filename) -{ - // TODO(wstrm): Use _url_save_fallback_method?. - // We probably want to do the cmd_url_open in a separate thread and wait for - // the transfer to be finished before calling call_external. -} - -void -_url_save_fallback_method(ProfWin* window, const char* url, const char* filename) -{ - gchar* scheme = g_uri_parse_scheme(url); - #ifdef HAVE_OMEMO - if (g_strcmp0(scheme, "aesgcm") == 0) { - AESGCMDownload* download = malloc(sizeof(AESGCMDownload)); - download->window = window; - download->url = strdup(url); - download->filename = strdup(filename); - - pthread_create(&(download->worker), NULL, &aesgcm_file_get, download); - aesgcm_download_add_download(download); - - free(scheme); - - return; +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); - - free(scheme); } void -_url_save_external_method(const char* scheme_cmd, const char* url, const char* filename) +_url_external_method(const char* cmd_template, const char* url, const char* filename) { - gchar** argv = g_strsplit(scheme_cmd, " ", 0); - - 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(url); - } else if (0 == g_strcmp0(argv[num_args], "%p")) { - g_free(argv[num_args]); - argv[num_args] = strdup(filename); - } - num_args++; - } + gchar** argv = format_call_external_argv(cmd_template, url, filename); if (!call_external(argv, NULL, NULL)) { - cons_show_error("Unable to save url: check the logs for more information."); + cons_show_error("Unable to call external executable for url: check the logs for more information."); } else { - cons_show("URL '%s' has been saved to '%s'.", url, filename); + cons_show("URL '%s' has been called with '%s'.", cmd_template); } + + g_strfreev(argv); } char* -_make_unique_filename(const char* filename) +_unique_filename(const char* filename) { char* unique = strdup(filename); @@ -9242,6 +9133,92 @@ _make_unique_filename(const char* filename) return unique; } +char* +_unique_filename_from_url(char* url, char* path) +{ + gchar* directory = NULL; + gchar* basename = NULL; + if (path != NULL) { + directory = g_path_get_dirname(path); + basename = g_path_get_basename(path); + } + + if (directory == NULL) { + // Explicitly use "./" as directory if no directory has been passed. + directory = "./"; + } + + if (!g_file_test(directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + cons_show_error("Directory '%s' does not exist or is not a directory.", directory); + return NULL; + } + + if (!basename) { + basename = http_basename_from_url(url); + } + + char* filename = NULL; + filename = g_build_filename(directory, basename, NULL); + + char* unique_filename = _unique_filename(filename); + if (!unique_filename) { + cons_show_error("Failed to generate an unique filename from '%s'.", filename); + free(filename); + return NULL; + } + + free(filename); + return unique_filename; +} + +gboolean +cmd_url_open(ProfWin* window, const char* const command, gchar** args) +{ + if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE) { + cons_show("url open not supported in this window"); + return TRUE; + } + + gchar* url = args[1]; + if (url == NULL) { + cons_bad_cmd_usage(command); + return TRUE; + } + + gchar* scheme = g_uri_parse_scheme(url); + if (scheme == NULL) { + cons_show("URL '%s' is not valid.", args[1]); + return TRUE; + } + + char* cmd_template = prefs_get_string_with_option(PREF_URL_OPEN_CMD, scheme); + if (cmd_template == NULL) { + cons_show("No default open command found in url open preferences"); + return TRUE; + } + +#ifdef HAVE_OMEMO + // OMEMO URLs (aesgcm://) must be saved and decrypted before being opened. + if (0 == g_strcmp0(scheme, "aesgcm")) { + char* filename = _unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS)); + _url_aesgcm_method(window, cmd_template, url, filename); + + free(filename); + goto out; + } +#endif + + _url_external_method(cmd_template, url, NULL); + +#ifdef HAVE_OMEMO +out: +#endif + free(cmd_template); + free(scheme); + + return TRUE; +} + gboolean cmd_url_save(ProfWin* window, const char* const command, gchar** args) { @@ -9260,59 +9237,29 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) gchar* scheme = g_uri_parse_scheme(url); if (scheme == NULL) { - cons_show("URL '%s' is not valid.", url); - g_free(url); + cons_show("URL '%s' is not valid.", args[1]); return TRUE; } - gchar* directory = NULL; - gchar* basename = NULL; - if (path != NULL) { - directory = g_path_get_dirname(path); - basename = g_path_get_basename(path); - } + char* filename = _unique_filename_from_url(url, path); - if (directory == NULL) { - // Explicitly use "./" as directory if no directory has been passed. - directory = "./"; - } - - if (!g_file_test(directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - cons_show_error("Directory '%s' does not exist or is not a directory.", directory); - return TRUE; - } - - if (!basename) { - basename = http_basename_from_url(url); - } - - char* filename = NULL; - filename = g_build_filename(directory, basename, NULL); - - char* unique_filename = _make_unique_filename(filename); - if (!unique_filename) { - cons_show_error("Failed to generate an unique filename from '%s'.", filename); - free(filename); - return TRUE; - } - - free(filename); - filename = unique_filename; - - gchar* scheme_cmd = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); - if (scheme_cmd == NULL) { + char* cmd_template = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); + if (cmd_template == NULL) { if (g_strcmp0(scheme, "http") == 0 - || g_strcmp0(scheme, "https") == 0 - || g_strcmp0(scheme, "aesgcm") == 0) { - _url_save_fallback_method(window, url, filename); + || g_strcmp0(scheme, "https") == 0) { + _url_http_method(window, url, filename, cmd_template); +#ifdef HAVE_OMEMO + } else if (g_strcmp0(scheme, "aesgcm") == 0) { + _url_aesgcm_method(window, url, filename, cmd_template); +#endif } else { cons_show_error("No download method defined for the scheme '%s'.", scheme); } } else { - _url_save_external_method(scheme_cmd, url, filename); + _url_external_method(cmd_template, url, filename); } - g_free(scheme_cmd); + free(cmd_template); return TRUE; } diff --git a/src/common.c b/src/common.c index c0bd6525..079c3af5 100644 --- a/src/common.c +++ b/src/common.c @@ -555,3 +555,23 @@ call_external(gchar** argv, gchar*** const output_ptr, gchar*** const error_ptr) 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; +} diff --git a/src/common.h b/src/common.h index 13332f7a..b56d31d4 100644 --- a/src/common.h +++ b/src/common.h @@ -105,5 +105,6 @@ void get_file_paths_recursive(const char* directory, GSList** contents); char* get_random_string(int length); 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); #endif diff --git a/src/config/files.h b/src/config/files.h index d5c96b0f..42499663 100644 --- a/src/config/files.h +++ b/src/config/files.h @@ -48,15 +48,16 @@ #define FILE_PROFANITY_IDENTIFIER "profident" #define FILE_BOOKMARK_AUTOJOIN_IGNORE "bookmark_ignore" -#define DIR_THEMES "themes" -#define DIR_ICONS "icons" -#define DIR_SCRIPTS "scripts" -#define DIR_CHATLOGS "chatlogs" -#define DIR_OTR "otr" -#define DIR_PGP "pgp" -#define DIR_OMEMO "omemo" -#define DIR_PLUGINS "plugins" -#define DIR_DATABASE "database" +#define DIR_THEMES "themes" +#define DIR_ICONS "icons" +#define DIR_SCRIPTS "scripts" +#define DIR_CHATLOGS "chatlogs" +#define DIR_OTR "otr" +#define DIR_PGP "pgp" +#define DIR_OMEMO "omemo" +#define DIR_PLUGINS "plugins" +#define DIR_DATABASE "database" +#define DIR_DOWNLOADS "downloads" void files_create_directories(void); diff --git a/src/config/preferences.c b/src/config/preferences.c index c7a74429..4b524fcf 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -81,7 +81,6 @@ static const char* _get_group(preference_t pref); static const char* _get_key(preference_t pref); static gboolean _get_default_boolean(preference_t pref); static char* _get_default_string(preference_t pref); -static char** _get_default_string_list(preference_t pref); static void _prefs_load(void) @@ -544,33 +543,6 @@ prefs_get_string_with_option(preference_t pref, gchar* option) 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 prefs_set_string(preference_t pref, char* value) { @@ -1893,6 +1865,7 @@ _get_group(preference_t pref) return PREF_GROUP_LOGGING; case PREF_AVATAR_CMD: case PREF_URL_OPEN_CMD: + return PREF_GROUP_EXECUTABLES; case PREF_URL_SAVE_CMD: return PREF_GROUP_EXECUTABLES; case PREF_AUTOAWAY_CHECK: @@ -2318,24 +2291,10 @@ _get_default_string(preference_t pref) return "false"; case PREF_AVATAR_CMD: return "xdg-open"; - 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: - str_array = g_malloc0(3); - str_array[0] = g_strdup("false"); - str_array[1] = g_strdup("xdg-open %u"); - return str_array; + return "xdg-open %u"; + case PREF_URL_SAVE_CMD: + return NULL; // Default to built-in method. default: return NULL; } diff --git a/src/config/preferences.h b/src/config/preferences.h index 141d8fce..bfad7d6b 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -320,7 +320,6 @@ gboolean prefs_get_boolean(preference_t pref); void prefs_set_boolean(preference_t pref, gboolean value); char* prefs_get_string(preference_t pref); 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_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); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 2e698591..22ada3a8 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -45,7 +45,6 @@ #include #include #include -#include #include "config/account.h" #include "config/files.h" diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index b8d84498..8c17f48d 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -33,6 +33,7 @@ * */ #include +#include #include "ui/ui.h" #include "config/account.h" diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index d6a85d06..d75cabe3 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "profanity.h" #include "event/client_events.h" @@ -146,8 +147,28 @@ aesgcm_file_get(void* userdata) free(https_url); free(fragment); + if (aesgcm_dl->cmd_template != NULL) { + gchar** argv = format_call_external_argv(aesgcm_dl->cmd_template, + aesgcm_dl->url, + aesgcm_dl->filename); + + // TODO(wstrm): 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(wstrm): Log the error"); + } + + g_strfreev(argv); + } + free(aesgcm_dl->filename); free(aesgcm_dl->url); + free(aesgcm_dl->cmd_template); free(aesgcm_dl); return NULL; diff --git a/src/tools/aesgcm_download.h b/src/tools/aesgcm_download.h index e172b89a..c0096f1d 100644 --- a/src/tools/aesgcm_download.h +++ b/src/tools/aesgcm_download.h @@ -52,6 +52,7 @@ typedef struct aesgcm_download_t { char* url; char* filename; + char* cmd_template; ProfWin* window; pthread_t worker; HTTPDownload* http_dl; diff --git a/src/tools/http_common.h b/src/tools/http_common.h index 3fbc6fcd..c0a553de 100644 --- a/src/tools/http_common.h +++ b/src/tools/http_common.h @@ -40,7 +40,7 @@ char* http_basename_from_url(const char* url); void http_print_transfer(ProfWin* window, char* url, const char* fmt, ...); -void http_print_transfer_update(ProfWin* window, char* url, - const char* fmt, ...); +void http_print_transfer_update(ProfWin* window, char* url, const char* fmt, ...); +gchar** http_format_external_argv(const char* cmd, const char* url, const char* filename); #endif diff --git a/src/tools/http_download.c b/src/tools/http_download.c index d14ab0e8..ef7e2906 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "profanity.h" #include "event/client_events.h" @@ -187,6 +188,25 @@ http_file_get(void* userdata) 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(wstrm): 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(wstrm): Log the error"); + } + + g_strfreev(argv); + } + free(download->url); free(download->filename); free(download); diff --git a/src/tools/http_download.h b/src/tools/http_download.h index b6ce42ca..23344f6c 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -51,6 +51,7 @@ typedef struct http_download_t { char* url; char* filename; + char* cmd_template; curl_off_t bytes_received; ProfWin* window; pthread_t worker; diff --git a/src/ui/console.c b/src/ui/console.c index 8d028139..dd217105 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2074,9 +2074,9 @@ cons_executable_setting(void) //TODO: there needs to be a way to get all the "locales"/schemes so we can //display the defualt openers for all filetypes - gchar** urlopen = prefs_get_string_list_with_option(PREF_URL_OPEN_CMD, ""); + char* urlopen = prefs_get_string_with_option(PREF_URL_OPEN_CMD, ""); cons_show("Default '/url open' command (/executable urlopen) : %s", urlopen[1]); - g_strfreev(urlopen); + g_free(urlopen); char* urlsave = prefs_get_string(PREF_URL_SAVE_CMD); cons_show("Default '/url save' command (/executable urlsave) : %s", urlsave); From 1d2c0a8836123c3b484826b974f87a0061bc110b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Fri, 4 Dec 2020 16:13:13 +0100 Subject: [PATCH 24/31] Move unique_filename_from_url functions to common --- Makefile.am | 3 - src/command/cmd_funcs.c | 65 +----------------- src/common.c | 80 ++++++++++++++++++++++ src/common.h | 2 + src/config/preferences.c | 1 - src/tools/http_common.c | 19 ----- src/tools/http_common.h | 2 - tests/unittests/test_common.c | 77 +++++++++++++++++++++ tests/unittests/test_common.h | 1 + tests/unittests/test_http_common.c | 75 -------------------- tests/unittests/test_http_common.h | 1 - tests/unittests/tools/stub_http_download.c | 9 +++ tests/unittests/unittests.c | 4 +- 13 files changed, 172 insertions(+), 167 deletions(-) delete mode 100644 tests/unittests/test_http_common.c delete mode 100644 tests/unittests/test_http_common.h diff --git a/Makefile.am b/Makefile.am index 9b2f75c6..c554f11c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -93,8 +93,6 @@ unittest_sources = \ src/tools/clipboard.c src/tools/clipboard.h \ src/tools/bookmark_ignore.c \ src/tools/bookmark_ignore.h \ - src/tools/http_download.c \ - src/tools/http_download.h \ src/config/accounts.h \ src/config/account.c src/config/account.h \ src/config/files.c src/config/files.h \ @@ -153,7 +151,6 @@ unittest_sources = \ tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \ tests/unittests/test_callbacks.c tests/unittests/test_callbacks.h \ tests/unittests/test_plugins_disco.c tests/unittests/test_plugins_disco.h \ - tests/unittests/test_http_common.c tests/unittests/test_http_common.h \ tests/unittests/unittests.c functionaltest_sources = \ diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 88461f1d..147eca4d 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9110,67 +9110,6 @@ _url_external_method(const char* cmd_template, const char* url, const char* file g_strfreev(argv); } -char* -_unique_filename(const char* filename) -{ - char* unique = 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; -} - -char* -_unique_filename_from_url(char* url, char* path) -{ - gchar* directory = NULL; - gchar* basename = NULL; - if (path != NULL) { - directory = g_path_get_dirname(path); - basename = g_path_get_basename(path); - } - - if (directory == NULL) { - // Explicitly use "./" as directory if no directory has been passed. - directory = "./"; - } - - if (!g_file_test(directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - cons_show_error("Directory '%s' does not exist or is not a directory.", directory); - return NULL; - } - - if (!basename) { - basename = http_basename_from_url(url); - } - - char* filename = NULL; - filename = g_build_filename(directory, basename, NULL); - - char* unique_filename = _unique_filename(filename); - if (!unique_filename) { - cons_show_error("Failed to generate an unique filename from '%s'.", filename); - free(filename); - return NULL; - } - - free(filename); - return unique_filename; -} - gboolean cmd_url_open(ProfWin* window, const char* const command, gchar** args) { @@ -9200,7 +9139,7 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) #ifdef HAVE_OMEMO // OMEMO URLs (aesgcm://) must be saved and decrypted before being opened. if (0 == g_strcmp0(scheme, "aesgcm")) { - char* filename = _unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS)); + char* filename = unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS)); _url_aesgcm_method(window, cmd_template, url, filename); free(filename); @@ -9241,7 +9180,7 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) return TRUE; } - char* filename = _unique_filename_from_url(url, path); + char* filename = unique_filename_from_url(url, path); char* cmd_template = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); if (cmd_template == NULL) { diff --git a/src/common.c b/src/common.c index 079c3af5..66e344c5 100644 --- a/src/common.c +++ b/src/common.c @@ -33,6 +33,9 @@ * source files in the program, then also delete it here. * */ + +#define _GNU_SOURCE 1 + #include "config.h" #include @@ -575,3 +578,80 @@ format_call_external_argv(const char* template, const char* url, const char* fil 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; +} + +gchar* +_basename_from_url(const char* url) +{ + const char* default_name = "index.html"; + + GFile* file = g_file_new_for_uri(url); + gchar* filename = g_file_get_basename(file); + g_object_unref(file); + + if (g_strcmp0(filename, ".") == 0 + || g_strcmp0(filename, "..") == 0 + || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { + g_free(filename); + return strdup(default_name); + } + + return filename; +} + +gchar* +unique_filename_from_url(const char* url, const char* path) +{ + gchar* directory = NULL; + gchar* basename = NULL; + if (path != NULL) { + directory = g_path_get_dirname(path); + basename = g_path_get_basename(path); + } + + if (!directory) { + // Explicitly use "./" as directory if no directory has been passed. + directory = "./"; + } + + if (!g_file_test(directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + return NULL; + } + + if (g_file_test(path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + basename = _basename_from_url(url); + } + + gchar* filename = g_build_filename(directory, basename, NULL); + + gchar* unique_filename = _unique_filename(filename); + if (!unique_filename) { + g_free(filename); + return NULL; + } + + g_free(filename); + return unique_filename; +} diff --git a/src/common.h b/src/common.h index b56d31d4..088ba953 100644 --- a/src/common.h +++ b/src/common.h @@ -107,4 +107,6 @@ char* get_random_string(int length); 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 diff --git a/src/config/preferences.c b/src/config/preferences.c index 4b524fcf..7a9b842b 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1865,7 +1865,6 @@ _get_group(preference_t pref) return PREF_GROUP_LOGGING; case PREF_AVATAR_CMD: case PREF_URL_OPEN_CMD: - return PREF_GROUP_EXECUTABLES; case PREF_URL_SAVE_CMD: return PREF_GROUP_EXECUTABLES; case PREF_AUTOAWAY_CHECK: diff --git a/src/tools/http_common.c b/src/tools/http_common.c index dfd0aa87..e066a6f6 100644 --- a/src/tools/http_common.c +++ b/src/tools/http_common.c @@ -44,25 +44,6 @@ #define FALLBACK_MSG "" -char* -http_basename_from_url(const char* url) -{ - const char* default_name = "index.html"; - - GFile* file = g_file_new_for_uri(url); - char* filename = g_file_get_basename(file); - g_object_unref(file); - - if (g_strcmp0(filename, ".") == 0 - || g_strcmp0(filename, "..") == 0 - || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { - g_free(filename); - return strdup(default_name); - } - - return filename; -} - void http_print_transfer_update(ProfWin* window, char* url, const char* fmt, ...) { diff --git a/src/tools/http_common.h b/src/tools/http_common.h index c0a553de..ac51b5a8 100644 --- a/src/tools/http_common.h +++ b/src/tools/http_common.h @@ -38,9 +38,7 @@ #include "ui/window.h" -char* http_basename_from_url(const char* url); void http_print_transfer(ProfWin* window, char* url, const char* fmt, ...); void http_print_transfer_update(ProfWin* window, char* url, const char* fmt, ...); -gchar** http_format_external_argv(const char* cmd, const char* url, const char* filename); #endif diff --git a/tests/unittests/test_common.c b/tests/unittests/test_common.c index b8958dda..462676cc 100644 --- a/tests/unittests/test_common.c +++ b/tests/unittests/test_common.c @@ -330,6 +330,83 @@ strip_quotes_strips_both(void** state) free(result); } +typedef struct +{ + char* url; + char* path; + char* filename; +} unique_filename_from_url_t; + +void +unique_filename_from_url_td(void** state) +{ + enum table { num_tests = 11 }; + + unique_filename_from_url_t tests[num_tests] = { + (unique_filename_from_url_t){ + .url = "https://host.test/image.jpeg", + .path = "./", + .filename = "./image.jpeg", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/image.jpeg#somefragment", + .path = "./", + .filename = "./image.jpeg", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/image.jpeg?query=param", + .path = "./", + .filename = "./image.jpeg", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/image.jpeg?query=param&another=one", + .path = "./", + .filename = "./image.jpeg", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/images/", + .path = "./", + .filename = "./images", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/images/../../file", + .path = "./", + .filename = "./file", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/images/../../file/..", + .path = "./", + .filename = "./index.html", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/images/..//", + .path = "./", + .filename = "./index.html", + }, + (unique_filename_from_url_t){ + .url = "https://host.test/", + .path = "./", + .filename = "./index.html", + }, + (unique_filename_from_url_t){ + .url = "https://host.test", + .path = "./", + .filename = "./index.html", + }, + (unique_filename_from_url_t){ + .url = "aesgcm://host.test", + .path = "./", + .filename = "./index.html", + }, + }; + + char* filename; + for (int i = 0; i < num_tests; i++) { + filename = unique_filename_from_url(tests[i].url, tests[i].path); + assert_string_equal(filename, tests[i].filename); + } +} + gboolean _lists_equal(GSList* a, GSList* b) { diff --git a/tests/unittests/test_common.h b/tests/unittests/test_common.h index b9e7291e..a1ef7c6e 100644 --- a/tests/unittests/test_common.h +++ b/tests/unittests/test_common.h @@ -31,3 +31,4 @@ void strip_quotes_strips_last(void** state); void strip_quotes_strips_both(void** state); void prof_partial_occurrences_tests(void** state); void prof_whole_occurrences_tests(void** state); +void unique_filename_from_url_td(void** state); diff --git a/tests/unittests/test_http_common.c b/tests/unittests/test_http_common.c deleted file mode 100644 index 195f370b..00000000 --- a/tests/unittests/test_http_common.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" - -#include "tools/http_common.c" - -typedef struct -{ - char* url; - char* basename; -} url_test_t; - -void -http_basename_from_url_td(void** state) -{ - int num_tests = 11; - url_test_t tests[] = { - (url_test_t){ - .url = "https://host.test/image.jpeg", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/image.jpeg#somefragment", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/image.jpeg?query=param", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/image.jpeg?query=param&another=one", - .basename = "image.jpeg", - }, - (url_test_t){ - .url = "https://host.test/images/", - .basename = "images", - }, - (url_test_t){ - .url = "https://host.test/images/../../file", - .basename = "file", - }, - (url_test_t){ - .url = "https://host.test/images/../../file/..", - .basename = "index.html", - }, - (url_test_t){ - .url = "https://host.test/images/..//", - .basename = "index.html", - }, - (url_test_t){ - .url = "https://host.test/", - .basename = "index.html", - }, - (url_test_t){ - .url = "https://host.test", - .basename = "index.html", - }, - (url_test_t){ - .url = "aesgcm://host.test", - .basename = "index.html", - }, - }; - - char* basename; - for (int i = 0; i < num_tests; i++) { - basename = http_basename_from_url(tests[i].url); - assert_string_equal(basename, tests[i].basename); - } -} diff --git a/tests/unittests/test_http_common.h b/tests/unittests/test_http_common.h deleted file mode 100644 index a1c62a7f..00000000 --- a/tests/unittests/test_http_common.h +++ /dev/null @@ -1 +0,0 @@ -void http_basename_from_url_td(void** state); diff --git a/tests/unittests/tools/stub_http_download.c b/tests/unittests/tools/stub_http_download.c index 5fa1c46e..cc7bddc5 100644 --- a/tests/unittests/tools/stub_http_download.c +++ b/tests/unittests/tools/stub_http_download.c @@ -18,4 +18,13 @@ typedef struct http_download_t int cancel; } HTTPDownload; +void* +http_file_get(void* userdata) +{ + return NULL; +} + +void http_download_cancel_processes(){}; +void http_download_add_download(){}; + #endif diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index cab99bf5..c63a3783 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -38,7 +38,6 @@ #include "test_form.h" #include "test_callbacks.h" #include "test_plugins_disco.h" -#include "test_http_common.h" int main(int argc, char* argv[]) @@ -91,6 +90,7 @@ main(int argc, char* argv[]) unit_test(strip_quotes_strips_first), unit_test(strip_quotes_strips_last), unit_test(strip_quotes_strips_both), + unit_test(unique_filename_from_url_td), unit_test(clear_empty), unit_test(reset_after_create), @@ -627,8 +627,6 @@ main(int argc, char* argv[]) unit_test(does_not_add_duplicate_feature), unit_test(removes_plugin_features), unit_test(does_not_remove_feature_when_more_than_one_reference), - - unit_test(http_basename_from_url_td), }; return run_tests(all_tests); From ac030378479cf999fefc0889d5c0932cfcabc4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Sun, 6 Dec 2020 17:02:09 +0100 Subject: [PATCH 25/31] Rework url to filename --- src/command/cmd_funcs.c | 1 - src/common.c | 65 +++++++++++++++------------- src/tools/aesgcm_download.c | 13 +++--- src/tools/http_download.c | 1 + tests/unittests/test_common.c | 80 +++++++++++++++++++++++++++-------- 5 files changed, 105 insertions(+), 55 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 147eca4d..5b1427fe 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9141,7 +9141,6 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) if (0 == g_strcmp0(scheme, "aesgcm")) { char* filename = unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS)); _url_aesgcm_method(window, cmd_template, url, filename); - free(filename); goto out; } diff --git a/src/common.c b/src/common.c index 66e344c5..10be280a 100644 --- a/src/common.c +++ b/src/common.c @@ -602,56 +602,63 @@ _unique_filename(const char* filename) return unique; } -gchar* +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.html"; + const char* default_name = "index"; - GFile* file = g_file_new_for_uri(url); - gchar* filename = g_file_get_basename(file); - g_object_unref(file); + GFile* file = g_file_new_for_commandline_arg(url); + char* basename = g_file_get_basename(file); - if (g_strcmp0(filename, ".") == 0 - || g_strcmp0(filename, "..") == 0 - || g_strcmp0(filename, G_DIR_SEPARATOR_S) == 0) { - g_free(filename); - return strdup(default_name); + if (_has_directory_suffix(basename)) { + g_free(basename); + basename = strdup(default_name); } - return filename; + g_object_unref(file); + + return basename; } gchar* unique_filename_from_url(const char* url, const char* path) { - gchar* directory = NULL; - gchar* basename = NULL; - if (path != NULL) { - directory = g_path_get_dirname(path); - basename = g_path_get_basename(path); + // Default to './' as path when none has been provided. + if (path == NULL) { + path = "./"; } - if (!directory) { - // Explicitly use "./" as directory if no directory has been passed. - directory = "./"; - } + // Resolves paths such as './../.' for path. + GFile* target = g_file_new_for_commandline_arg(path); + gchar* filename = NULL; - if (!g_file_test(directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - return 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); } - if (g_file_test(path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - basename = _basename_from_url(url); - } - - gchar* filename = g_build_filename(directory, basename, NULL); - gchar* unique_filename = _unique_filename(filename); - if (!unique_filename) { + if (unique_filename == NULL) { g_free(filename); return NULL; } + g_object_unref(target); g_free(filename); + return unique_filename; } diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index d75cabe3..f8d2db9f 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -85,11 +85,6 @@ aesgcm_file_get(void* userdata) "(%s).", https_url, g_strerror(errno)); return NULL; - } else { - // TODO(wstrm): Maybe refactor this to use file handles so we do not - // have to open a dummy file descriptor and then close it. - // It's pretty ugly this way... - close(tmpfd); } FILE* outfh = fopen(aesgcm_dl->filename, "wb"); @@ -107,6 +102,7 @@ aesgcm_file_get(void* userdata) 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; @@ -117,7 +113,7 @@ aesgcm_file_get(void* userdata) 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, aesgcm_dl->filename, + aesgcm_dl->url, tmpname, g_strerror(errno)); return NULL; } @@ -130,6 +126,7 @@ aesgcm_file_get(void* userdata) cons_show_error(g_strerror(errno)); } + close(tmpfd); remove(tmpname); g_free(tmpname); @@ -149,7 +146,7 @@ aesgcm_file_get(void* userdata) if (aesgcm_dl->cmd_template != NULL) { gchar** argv = format_call_external_argv(aesgcm_dl->cmd_template, - aesgcm_dl->url, + aesgcm_dl->filename, aesgcm_dl->filename); // TODO(wstrm): Log the error. @@ -164,11 +161,11 @@ aesgcm_file_get(void* userdata) } g_strfreev(argv); + free(aesgcm_dl->cmd_template); } free(aesgcm_dl->filename); free(aesgcm_dl->url); - free(aesgcm_dl->cmd_template); free(aesgcm_dl); return NULL; diff --git a/src/tools/http_download.c b/src/tools/http_download.c index ef7e2906..397ff4b8 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -205,6 +205,7 @@ http_file_get(void* userdata) } g_strfreev(argv); + free(download->cmd_template); } free(download->url); diff --git a/tests/unittests/test_common.c b/tests/unittests/test_common.c index 462676cc..2dee8722 100644 --- a/tests/unittests/test_common.c +++ b/tests/unittests/test_common.c @@ -334,77 +334,123 @@ typedef struct { char* url; char* path; - char* filename; + char* target; + char* basename; } unique_filename_from_url_t; void unique_filename_from_url_td(void** state) { - enum table { num_tests = 11 }; + + 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 = "./", - .filename = "./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 = "./", - .filename = "./image.jpeg", + .target = pwd, + .basename = "image.jpeg", }, (unique_filename_from_url_t){ .url = "https://host.test/image.jpeg?query=param", .path = "./", - .filename = "./image.jpeg", + .target = pwd, + .basename = "image.jpeg", }, (unique_filename_from_url_t){ .url = "https://host.test/image.jpeg?query=param&another=one", .path = "./", - .filename = "./image.jpeg", + .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 = "./", - .filename = "./images", + .target = pwd, + .basename = "images", }, (unique_filename_from_url_t){ .url = "https://host.test/images/../../file", .path = "./", - .filename = "./file", + .target = pwd, + .basename = "file", }, (unique_filename_from_url_t){ .url = "https://host.test/images/../../file/..", .path = "./", - .filename = "./index.html", + .target = pwd, + .basename = "index", }, (unique_filename_from_url_t){ .url = "https://host.test/images/..//", .path = "./", - .filename = "./index.html", + .target = pwd, + .basename = "index", }, (unique_filename_from_url_t){ .url = "https://host.test/", .path = "./", - .filename = "./index.html", + .target = pwd, + .basename = "index", }, (unique_filename_from_url_t){ .url = "https://host.test", .path = "./", - .filename = "./index.html", + .target = pwd, + .basename = "index", }, (unique_filename_from_url_t){ .url = "aesgcm://host.test", .path = "./", - .filename = "./index.html", + .target = pwd, + .basename = "index", }, }; - char* filename; + char* got_filename; + char* exp_filename; for (int i = 0; i < num_tests; i++) { - filename = unique_filename_from_url(tests[i].url, tests[i].path); - assert_string_equal(filename, tests[i].filename); + 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 From 4a1c118b8b575e3fa3c576106bce57efb786f225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 7 Dec 2020 15:15:06 +0100 Subject: [PATCH 26/31] Fix bad order of parameters for url save --- src/command/cmd_funcs.c | 9 ++++++++- src/tools/aesgcm_download.c | 8 +++++++- src/ui/console.c | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 5b1427fe..3cff1ab5 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4809,6 +4809,8 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args) return TRUE; } +// TODO(wstrm): Move this into its own tools such as HTTPUpload or +// AESGCMDownload. #ifdef HAVE_OMEMO char* _add_omemo_stream(int* fd, FILE** fh, char** err) @@ -9180,6 +9182,11 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) } char* filename = unique_filename_from_url(url, path); + if (filename == NULL) { + cons_show("Failed to generate unique filename" + "from URL '%s' for path '%s'", + url, path); + } char* cmd_template = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); if (cmd_template == NULL) { @@ -9188,7 +9195,7 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) _url_http_method(window, url, filename, cmd_template); #ifdef HAVE_OMEMO } else if (g_strcmp0(scheme, "aesgcm") == 0) { - _url_aesgcm_method(window, url, filename, cmd_template); + _url_aesgcm_method(window, cmd_template, url, filename); #endif } else { cons_show_error("No download method defined for the scheme '%s'.", scheme); diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index f8d2db9f..20397b82 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -69,6 +69,8 @@ aesgcm_file_get(void* 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'.", @@ -76,6 +78,8 @@ aesgcm_file_get(void* userdata) 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) { @@ -87,6 +91,7 @@ aesgcm_file_get(void* userdata) 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, @@ -97,13 +102,14 @@ aesgcm_file_get(void* userdata) 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. diff --git a/src/ui/console.c b/src/ui/console.c index dd217105..b24fe976 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2073,7 +2073,7 @@ cons_executable_setting(void) g_free(avatar); //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 char* urlopen = prefs_get_string_with_option(PREF_URL_OPEN_CMD, ""); cons_show("Default '/url open' command (/executable urlopen) : %s", urlopen[1]); g_free(urlopen); From 867d89546931a37fd255cdd5af382e39b66ae09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 7 Dec 2020 16:16:15 +0100 Subject: [PATCH 27/31] Add tests for format_call_external_argv --- src/command/cmd_funcs.c | 43 +++++++++++++++--------- src/tools/aesgcm_download.c | 4 +-- tests/unittests/test_common.c | 61 +++++++++++++++++++++++++++++++++-- tests/unittests/test_common.h | 1 + tests/unittests/unittests.c | 1 + 5 files changed, 91 insertions(+), 19 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 3cff1ab5..024137d2 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -4809,8 +4809,7 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args) return TRUE; } -// TODO(wstrm): Move this into its own tools such as HTTPUpload or -// AESGCMDownload. +// 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) @@ -9126,35 +9125,39 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) return TRUE; } - gchar* scheme = g_uri_parse_scheme(url); + gchar* scheme = NULL; + char* cmd_template = NULL; + char* filename = NULL; + + scheme = g_uri_parse_scheme(url); if (scheme == NULL) { cons_show("URL '%s' is not valid.", args[1]); - return TRUE; + goto out; } - char* cmd_template = prefs_get_string_with_option(PREF_URL_OPEN_CMD, scheme); + cmd_template = prefs_get_string_with_option(PREF_URL_OPEN_CMD, scheme); if (cmd_template == NULL) { cons_show("No default open command found in url open preferences"); - return TRUE; + goto out; } #ifdef HAVE_OMEMO // OMEMO URLs (aesgcm://) must be saved and decrypted before being opened. if (0 == g_strcmp0(scheme, "aesgcm")) { - char* filename = unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS)); + filename = unique_filename_from_url(url, files_get_data_path(DIR_DOWNLOADS)); _url_aesgcm_method(window, cmd_template, url, filename); - free(filename); goto out; } #endif _url_external_method(cmd_template, url, NULL); -#ifdef HAVE_OMEMO out: -#endif + free(cmd_template); - free(scheme); + free(filename); + + g_free(scheme); return TRUE; } @@ -9174,21 +9177,25 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) gchar* url = args[1]; gchar* path = g_strdup(args[2]); + gchar* scheme = NULL; + char* filename = NULL; + char* cmd_template = NULL; - gchar* scheme = g_uri_parse_scheme(url); + scheme = g_uri_parse_scheme(url); if (scheme == NULL) { cons_show("URL '%s' is not valid.", args[1]); - return TRUE; + goto out; } - char* filename = unique_filename_from_url(url, path); + filename = unique_filename_from_url(url, path); if (filename == NULL) { cons_show("Failed to generate unique filename" "from URL '%s' for path '%s'", url, path); + goto out; } - char* cmd_template = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); + cmd_template = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); if (cmd_template == NULL) { if (g_strcmp0(scheme, "http") == 0 || g_strcmp0(scheme, "https") == 0) { @@ -9204,8 +9211,14 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) _url_external_method(cmd_template, url, filename); } +out: + + free(filename); free(cmd_template); + g_free(scheme); + g_free(path); + return TRUE; } diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index 20397b82..96f8d7e8 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -155,7 +155,7 @@ aesgcm_file_get(void* userdata) aesgcm_dl->filename, aesgcm_dl->filename); - // TODO(wstrm): Log the error. + // 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 " @@ -163,7 +163,7 @@ aesgcm_file_get(void* userdata) aesgcm_dl->url, aesgcm_dl->cmd_template, aesgcm_dl->filename, - "TODO(wstrm): Log the error"); + "TODO: Log the error"); } g_strfreev(argv); diff --git a/tests/unittests/test_common.c b/tests/unittests/test_common.c index 2dee8722..fa5dd59e 100644 --- a/tests/unittests/test_common.c +++ b/tests/unittests/test_common.c @@ -330,6 +330,63 @@ strip_quotes_strips_both(void** state) 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; @@ -438,8 +495,8 @@ unique_filename_from_url_td(void** state) }, }; - char* got_filename; - char* exp_filename; + 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); diff --git a/tests/unittests/test_common.h b/tests/unittests/test_common.h index a1ef7c6e..46d990d9 100644 --- a/tests/unittests/test_common.h +++ b/tests/unittests/test_common.h @@ -32,3 +32,4 @@ void strip_quotes_strips_both(void** state); void prof_partial_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); diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index c63a3783..7fd3b192 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -90,6 +90,7 @@ main(int argc, char* argv[]) unit_test(strip_quotes_strips_first), unit_test(strip_quotes_strips_last), unit_test(strip_quotes_strips_both), + unit_test(format_call_external_argv_td), unit_test(unique_filename_from_url_td), unit_test(clear_empty), From 5c5b4d7025546af386f7124924712bb0a6f95e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 7 Dec 2020 16:30:03 +0100 Subject: [PATCH 28/31] Remove cmd_tiny, empty files and link nonce with IV --- src/command/cmd_funcs.h | 1 - src/omemo/omemo.h | 2 +- src/tools/aesgcm_upload.c | 0 src/tools/aesgcm_upload.h | 0 src/tools/http_download.c | 4 ++-- 5 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 src/tools/aesgcm_upload.c delete mode 100644 src/tools/aesgcm_upload.h diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index d5311bba..03c92522 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -128,7 +128,6 @@ gboolean cmd_status_get(ProfWin* window, const char* const command, gchar** args gboolean cmd_status_set(ProfWin* window, const char* const command, gchar** args); gboolean cmd_sub(ProfWin* window, const char* const command, gchar** args); gboolean cmd_theme(ProfWin* window, const char* const command, gchar** args); -gboolean cmd_tiny(ProfWin* window, const char* const command, gchar** args); gboolean cmd_wintitle(ProfWin* window, const char* const command, gchar** args); gboolean cmd_vercheck(ProfWin* window, const char* const command, gchar** args); gboolean cmd_who(ProfWin* window, const char* const command, gchar** args); diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 8c17f48d..7e7c1f14 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -41,7 +41,7 @@ #define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000 #define OMEMO_ERR_GCRYPT -20000 -#define OMEMO_AESGCM_NONCE_LENGTH 12 +#define OMEMO_AESGCM_NONCE_LENGTH AES128_GCM_IV_LENGTH #define OMEMO_AESGCM_KEY_LENGTH 32 #define OMEMO_AESGCM_URL_SCHEME "aesgcm" diff --git a/src/tools/aesgcm_upload.c b/src/tools/aesgcm_upload.c deleted file mode 100644 index e69de29b..00000000 diff --git a/src/tools/aesgcm_upload.h b/src/tools/aesgcm_upload.h deleted file mode 100644 index e69de29b..00000000 diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 397ff4b8..f97fd704 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -193,7 +193,7 @@ http_file_get(void* userdata) download->url, download->filename); - // TODO(wstrm): Log the error. + // TODO: Log the error. if (!call_external(argv, NULL, NULL)) { http_print_transfer_update(download->window, download->url, "Downloading '%s' failed: Unable to call " @@ -201,7 +201,7 @@ http_file_get(void* userdata) download->url, download->cmd_template, download->filename, - "TODO(wstrm): Log the error"); + "TODO: Log the error"); } g_strfreev(argv); From 332dc87ca5948eb908d0e776eb0d33bacf166fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 7 Dec 2020 16:38:05 +0100 Subject: [PATCH 29/31] Fix wrong order of arguments for _url_http_method --- src/command/cmd_funcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 024137d2..d2ec46ee 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9105,7 +9105,7 @@ _url_external_method(const char* cmd_template, const char* url, const char* file 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'.", cmd_template); + cons_show("URL '%s' has been called with '%s'.", url, cmd_template); } g_strfreev(argv); @@ -9199,7 +9199,7 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) if (cmd_template == NULL) { if (g_strcmp0(scheme, "http") == 0 || g_strcmp0(scheme, "https") == 0) { - _url_http_method(window, url, filename, cmd_template); + _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); From 7f0165a91278647e9ddb72890e58a6db6b265a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Mon, 7 Dec 2020 16:46:44 +0100 Subject: [PATCH 30/31] Fix segfault for urlopen[1] --- src/ui/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/console.c b/src/ui/console.c index b24fe976..623556f7 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2075,7 +2075,7 @@ cons_executable_setting(void) //TODO: there needs to be a way to get all the "locales"/schemes so we can //display the default openers for all filetypes char* urlopen = prefs_get_string_with_option(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_free(urlopen); char* urlsave = prefs_get_string(PREF_URL_SAVE_CMD); From d7848e38bc2d916f88e889352f337a5c617fb26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Tue, 8 Dec 2020 20:01:17 +0100 Subject: [PATCH 31/31] Remove scheme and filetype matching for url (save|open) --- src/command/cmd_defs.c | 35 ++++++++----- src/command/cmd_funcs.c | 108 +++++++++++++++++++++------------------ src/command/cmd_funcs.h | 4 +- src/config/preferences.c | 4 +- src/ui/console.c | 9 ++-- 5 files changed, 91 insertions(+), 69 deletions(-) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index d291856c..4a78ba70 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2374,7 +2374,7 @@ static struct cmd_t command_defs[] = { "Settings for consistent color generation for nicks (XEP-0392). Including corrections for Color Vision Deficiencies. " "Your terminal needs to support 256 colors.") 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." }) CMD_EXAMPLES( "/color off", @@ -2493,26 +2493,35 @@ static struct cmd_t command_defs[] = { { "/executable", parse_args, 2, 4, &cons_executable_setting, - CMD_NOSUBFUNCS - CMD_MAINFUNC(cmd_executable) + CMD_SUBFUNCS( + { "avatar", cmd_executable_avatar }, + { "urlopen", cmd_executable_urlopen }, + { "urlsave", cmd_executable_urlsave }) + CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_DISCOVERY) CMD_SYN( "/executable avatar ", - "/executable urlopen (|DEF ", - "/executable urlsave (|DEF) ") + "/executable urlopen set ", + "/executable urlopen default", + "/executable urlsave set ", + "/executable urlsave default") CMD_DESC( - "Configure executable that should be called upon a certain command." - "Default is xdg-open.") + "Configure executable that should be called upon a certain command.") CMD_ARGS( - { "avatar", "Set executable that is run in /avatar open. Use your favourite 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." }, - { "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." }) + { "avatar", "Set executable that is run by /avatar open. Use your favorite image viewer." }, + { "urlopen set", "Set executable that is run by /url open. It may be your favorite browser or a specific viewer." }, + { "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( "/executable avatar xdg-open", - "/executable urlopen DEF false \"xdg-open %u\"", - "/executable urlopen html false \"firefox %u\"", - "/executable urlsave aesgcm \"omut -d -o %p %u\"") + "/executable urlopen set \"xdg-open %u\"", + "/executable urlopen set \"firefox %u\"", + "/executable urlopen default", + "/executable urlsave set \"wget %u -O %p\"", + "/executable urlsave set \"curl %u -o %p\"", + "/executable urlsave default") }, { "/url", diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index d2ec46ee..fd9d2ffd 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9135,9 +9135,9 @@ cmd_url_open(ProfWin* window, const char* const command, gchar** args) goto out; } - cmd_template = prefs_get_string_with_option(PREF_URL_OPEN_CMD, scheme); + cmd_template = prefs_get_string(PREF_URL_OPEN_CMD); if (cmd_template == NULL) { - cons_show("No default open command found in url open preferences"); + cons_show("No default `url open` command found in executables preferences."); goto out; } @@ -9195,20 +9195,17 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args) goto out; } - cmd_template = prefs_get_string_with_option(PREF_URL_SAVE_CMD, scheme); - if (cmd_template == NULL) { - if (g_strcmp0(scheme, "http") == 0 - || g_strcmp0(scheme, "https") == 0) { - _url_http_method(window, cmd_template, url, filename); + 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); + } else if (g_strcmp0(scheme, "aesgcm") == 0) { + _url_aesgcm_method(window, cmd_template, url, filename); #endif - } else { - cons_show_error("No download method defined for the scheme '%s'.", scheme); - } - } else { + } 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: @@ -9223,48 +9220,59 @@ out: } gboolean -cmd_executable(ProfWin* window, const char* const command, gchar** args) +cmd_executable_avatar(ProfWin* window, const char* const command, gchar** args) +{ + prefs_set_string(PREF_AVATAR_CMD, args[1]); + cons_show("`avatar` command set to invoke '%s'", args[1]); + return TRUE; +} + +gboolean +cmd_executable_urlopen(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[0], "avatar") == 0) { - prefs_set_string(PREF_AVATAR_CMD, args[1]); - cons_show("Avatar command set to: %s", args[1]); - - } else if (g_strcmp0(args[0], "urlopen") == 0) { - if (num_args < 4) { - cons_bad_cmd_usage(command); - return TRUE; - } - - gchar* str = g_strjoinv(" ", &args[3]); - const gchar* const list[] = { args[2], str, NULL }; - 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]); + if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) { + gchar* str = g_strjoinv(" ", &args[2]); + prefs_set_string(PREF_URL_OPEN_CMD, str); + cons_show("`url open` command set to invoke '%s'", str); g_free(str); - } else if (g_strcmp0(args[0], "urlsave") == 0) { - - if (num_args < 3) { - cons_bad_cmd_usage(command); - return TRUE; - } - - if (g_strcmp0(args[1], "set") == 0 && num_args >= 4) { - gchar* str = g_strjoinv(" ", &args[3]); - prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[2], str); - cons_show("`url save` command set to: %s for scheme %s", str, args[2]); - g_free(str); - - } else if (g_strcmp0(args[1], "clear") == 0) { - prefs_set_string_with_option(PREF_URL_SAVE_CMD, args[2], NULL); - cons_show("`url save` will use internal download method for scheme %s", args[2]); - - } else { - cons_bad_cmd_usage(command); - return TRUE; - } - + } 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 { cons_bad_cmd_usage(command); } diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 03c92522..4955972c 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -237,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_url_open(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 diff --git a/src/config/preferences.c b/src/config/preferences.c index 7a9b842b..9d7d4f7b 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -170,7 +170,7 @@ _prefs_load(void) value = g_string_append(value, val); 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_string_free(value, TRUE); @@ -529,7 +529,7 @@ prefs_get_string_with_option(preference_t pref, gchar* option) if (result == NULL) { // 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 (def) { // use hardcoded profanity default diff --git a/src/ui/console.c b/src/ui/console.c index 623556f7..306b13d5 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2068,17 +2068,20 @@ cons_correction_setting(void) 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); g_free(avatar); //TODO: there needs to be a way to get all the "locales"/schemes so we can //display the default openers for all filetypes - char* urlopen = prefs_get_string_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); 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); g_free(urlsave); }