diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index dbf92d21..68d6188a 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9209,18 +9209,30 @@ _prepare_filename(ProfWin* window, gchar* url, gchar* path) break; } - // Ensure that the downloads directory exists for saving cleartexts. - auto_gchar gchar* downloads_dir = path ? get_expanded_path(path) : files_get_download_path(jid); - if (g_mkdir_with_parents(downloads_dir, S_IRWXU) != 0) { - cons_show_error("Failed to create download directory " - "at '%s' with error '%s'", - downloads_dir, strerror(errno)); - return NULL; + auto_gchar gchar* expanded_path = path ? get_expanded_path(path) : files_get_download_path(jid); + + // If path is provided and doesn't look like a directory, create its parent. + // Otherwise, create the directory itself. + if (path && !g_str_has_suffix(expanded_path, G_DIR_SEPARATOR_S) && !g_file_test(expanded_path, G_FILE_TEST_IS_DIR)) { + auto_gchar gchar* dirname = g_path_get_dirname(expanded_path); + if (g_mkdir_with_parents(dirname, S_IRWXU) != 0) { + cons_show_error("Failed to create download directory " + "at '%s' with error '%s'", + dirname, strerror(errno)); + return NULL; + } + } else { + if (g_mkdir_with_parents(expanded_path, S_IRWXU) != 0) { + cons_show_error("Failed to create download directory " + "at '%s' with error '%s'", + expanded_path, strerror(errno)); + return NULL; + } } // Generate an unique filename from the URL that should be stored in the // downloads directory. - return unique_filename_from_url(url, downloads_dir); + return unique_filename_from_url(url, expanded_path); } #ifdef HAVE_OMEMO diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c index 1c34074e..b7e7cd85 100644 --- a/src/tools/aesgcm_download.c +++ b/src/tools/aesgcm_download.c @@ -43,7 +43,7 @@ aesgcm_file_get(void* userdata) // and tag stored in the URL fragment. if (omemo_parse_aesgcm_url(aesgcm_dl->url, &https_url, &fragment) != 0) { cons_show_error("Download failed: Cannot parse URL '%s'.", aesgcm_dl->url); - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ERROR, ENTRY_ERROR, "Download failed: Cannot parse URL '%s'.", aesgcm_dl->url); return NULL; @@ -54,7 +54,7 @@ aesgcm_file_get(void* userdata) auto_gchar gchar* tmpname = NULL; auto_gfd gint tmpfd = 0; if ((tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmpname, NULL)) == -1) { - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to create " "temporary ciphertext file for writing " "(%s).", @@ -65,7 +65,7 @@ aesgcm_file_get(void* userdata) // Open the target file for storing the cleartext. auto_FILE FILE* outfh = fopen(aesgcm_dl->filename, "wb"); if (outfh == NULL) { - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to open " "output file at '%s' for writing (%s).", https_url, aesgcm_dl->filename, @@ -80,9 +80,11 @@ aesgcm_file_get(void* userdata) http_dl->worker = aesgcm_dl->worker; http_dl->id = strdup(aesgcm_dl->id); http_dl->url = strdup(https_url); + http_dl->display_url = strdup(aesgcm_dl->url); http_dl->filename = strdup(tmpname); http_dl->cmd_template = NULL; http_dl->silent = FALSE; + http_dl->silent_done = TRUE; http_dl->return_bytes_received = TRUE; aesgcm_dl->http_dl = http_dl; @@ -95,7 +97,7 @@ aesgcm_file_get(void* userdata) FILE* tmpfh = fopen(tmpname, "rb"); if (tmpfh == NULL) { - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to open " "temporary file at '%s' for reading (%s).", aesgcm_dl->url, tmpname, @@ -110,13 +112,15 @@ aesgcm_file_get(void* userdata) remove(tmpname); if (crypt_res != GPG_ERR_NO_ERROR) { - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Failed to decrypt " "file (%s).", - https_url, gcry_strerror(crypt_res)); + aesgcm_dl->url, gcry_strerror(crypt_res)); } else { - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, - "Decrypted file saved to '%s'", aesgcm_dl->filename); + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ONLINE, ENTRY_COMPLETED, + "Downloading '%s': done\nSaved to '%s'", + aesgcm_dl->url, aesgcm_dl->filename); + win_mark_received(aesgcm_dl->window, aesgcm_dl->id); } if (aesgcm_dl->cmd_template != NULL) { @@ -126,7 +130,7 @@ aesgcm_file_get(void* userdata) // TODO: Log the error. if (!call_external(argv)) { - http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, + http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to call " "command '%s' with file at '%s' (%s).", aesgcm_dl->url, diff --git a/src/tools/http_common.c b/src/tools/http_common.c index cfec1bcf..0386e08d 100644 --- a/src/tools/http_common.c +++ b/src/tools/http_common.c @@ -19,7 +19,7 @@ #define FALLBACK_MSG "" void -http_print_transfer_update(ProfWin* window, char* id, const char* fmt, ...) +http_print_transfer_update(ProfWin* window, char* id, theme_item_t theme_item, int flags, const char* fmt, ...) { va_list args; @@ -29,7 +29,7 @@ http_print_transfer_update(ProfWin* window, char* id, const char* fmt, ...) va_end(args); if (window->type != WIN_CONSOLE) { - win_update_entry_message(window, id, msg->str); + win_update_entry(window, id, msg->str, theme_item, flags); } else { cons_show("%s", msg->str); } @@ -38,7 +38,7 @@ http_print_transfer_update(ProfWin* window, char* id, const char* fmt, ...) } void -http_print_transfer(ProfWin* window, char* id, const char* fmt, ...) +http_print_transfer(ProfWin* window, char* id, theme_item_t theme_item, const char* fmt, ...) { va_list args; @@ -47,7 +47,7 @@ http_print_transfer(ProfWin* window, char* id, const char* fmt, ...) g_string_vprintf(msg, fmt, args); va_end(args); - win_print_http_transfer(window, msg->str, id); + win_print_status_with_id(window, msg->str, id, theme_item, 0); g_string_free(msg, TRUE); } diff --git a/src/tools/http_common.h b/src/tools/http_common.h index 529c05c9..f1f9b56a 100644 --- a/src/tools/http_common.h +++ b/src/tools/http_common.h @@ -12,7 +12,7 @@ #include "ui/window.h" -void http_print_transfer(ProfWin* window, char* id, const char* fmt, ...); -void http_print_transfer_update(ProfWin* window, char* id, const char* fmt, ...); +void http_print_transfer(ProfWin* window, char* id, theme_item_t theme_item, const char* fmt, ...); +void http_print_transfer_update(ProfWin* window, char* id, theme_item_t theme_item, int flags, const char* fmt, ...); #endif diff --git a/src/tools/http_download.c b/src/tools/http_download.c index 720cc40c..bc561233 100644 --- a/src/tools/http_download.c +++ b/src/tools/http_download.c @@ -32,7 +32,6 @@ #include "common.h" GSList* download_processes = NULL; -gboolean silent = FALSE; static int _xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) @@ -58,9 +57,18 @@ _xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot dlperc = (100 * dlnow) / dltotal; } - if (!silent) - http_print_transfer_update(download->window, download->url, - "Downloading '%s': %d%%", download->url, dlperc); + if (!download->silent) { + const char* url = download->display_url ? download->display_url : download->url; + if (dlnow == dltotal && dltotal > 0) { + if (!download->silent_done) { + http_print_transfer_update(download->window, download->id, THEME_ONLINE, ENTRY_COMPLETED, + "Downloading '%s': done", url); + } + } else { + http_print_transfer_update(download->window, download->id, THEME_DEFAULT, 0, + "Downloading '%s': %d%%", url, dlperc); + } + } pthread_mutex_unlock(&lock); @@ -85,20 +93,20 @@ http_file_get(void* userdata) CURL* curl; CURLcode res; - silent = download->silent; download->cancel = 0; download->bytes_received = 0; pthread_mutex_lock(&lock); - if (!silent) { - http_print_transfer(download->window, download->id, - "Downloading '%s': 0%%", download->url); + const char* display_url = download->display_url ? download->display_url : download->url; + if (!download->silent) { + http_print_transfer(download->window, download->id, THEME_DEFAULT, + "Downloading '%s': 0%%", display_url); } FILE* outfh = fopen(download->filename, "wb"); if (outfh == NULL) { - http_print_transfer_update(download->window, download->id, + http_print_transfer_update(download->window, download->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to open " "output file at '%s' for writing (%s).", download->url, download->filename, @@ -169,22 +177,24 @@ http_file_get(void* userdata) g_free(cert_path); if (err) { if (download->cancel) { - http_print_transfer_update(download->window, download->id, + http_print_transfer_update(download->window, download->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: " "Download was canceled", - download->url); + display_url); } else { - http_print_transfer_update(download->window, download->id, + http_print_transfer_update(download->window, download->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: %s", - download->url, err); + display_url, err); } free(err); } else { - if (!download->cancel && !silent) { - http_print_transfer_update(download->window, download->id, - "Downloading '%s': done\nSaved to '%s'", - download->url, download->filename); - win_mark_received(download->window, download->id); + if (!download->cancel) { + if (!download->silent && !download->silent_done) { + http_print_transfer_update(download->window, download->id, THEME_ONLINE, ENTRY_COMPLETED, + "Downloading '%s': done\nSaved to '%s'", + display_url, download->filename); + win_mark_received(download->window, download->id); + } if (download->return_bytes_received) { ret = malloc(sizeof(*ret)); if (ret) { @@ -201,10 +211,10 @@ http_file_get(void* userdata) // TODO: Log the error. if (!call_external(argv)) { - http_print_transfer_update(download->window, download->id, + http_print_transfer_update(download->window, download->id, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to call " "command '%s' with file at '%s' (%s).", - download->url, + display_url, download->cmd_template, download->filename, "TODO: Log the error"); @@ -221,6 +231,7 @@ out: free(download->filename); free(download->url); + free(download->display_url); free(download->id); free(download); diff --git a/src/tools/http_download.h b/src/tools/http_download.h index 4b7a40e7..c020e864 100644 --- a/src/tools/http_download.h +++ b/src/tools/http_download.h @@ -25,6 +25,7 @@ typedef struct http_download_t { char* id; char* url; + char* display_url; char* filename; char* cmd_template; curl_off_t bytes_received; @@ -32,6 +33,7 @@ typedef struct http_download_t pthread_t worker; int cancel; gboolean silent; + gboolean silent_done; gboolean return_bytes_received; } HTTPDownload; diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index 6162034f..3cd7cb33 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -65,11 +65,21 @@ _xferinfo(void* userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultot ulperc = (100 * ulnow) / ultotal; } - gchar* msg = g_strdup_printf("Uploading '%s': %d%%", upload->filename, ulperc); + gchar* msg = NULL; + theme_item_t theme = THEME_DEFAULT; + int flags = 0; + if (ulnow == ultotal && ultotal > 0) { + msg = g_strdup_printf("Uploading '%s': done", upload->filename); + theme = THEME_ONLINE; + flags = ENTRY_COMPLETED; + } else { + msg = g_strdup_printf("Uploading '%s': %d%%", upload->filename, ulperc); + } + if (!msg) { msg = g_strdup(FALLBACK_MSG); } - win_update_entry_message(upload->window, upload->put_url, msg); + win_update_entry(upload->window, upload->put_url, msg, theme, flags); g_free(msg); pthread_mutex_unlock(&lock); @@ -150,7 +160,7 @@ http_file_put(void* userdata) if (!msg) { msg = g_strdup(FALLBACK_MSG); } - win_print_http_transfer(upload->window, msg, upload->put_url); + win_print_status_with_id(upload->window, msg, upload->put_url, THEME_DEFAULT, 0); auto_gchar gchar* cert_path = prefs_get_string(PREF_TLS_CERTPATH); auto_gchar gchar* cafile = cafile_get_name(); @@ -278,21 +288,22 @@ http_file_put(void* userdata) if (!err_msg) { err_msg = g_strdup(FALLBACK_MSG); } + win_update_entry(upload->window, upload->put_url, err_msg, THEME_ERROR, ENTRY_ERROR); } else { err_msg = g_strdup_printf("Uploading '%s' failed: %s", upload->filename, err); if (!err_msg) { err_msg = g_strdup(FALLBACK_MSG); } - win_update_entry_message(upload->window, upload->put_url, err_msg); + win_update_entry(upload->window, upload->put_url, err_msg, THEME_ERROR, ENTRY_ERROR); } cons_show_error(err_msg); } else { if (!upload->cancel) { - auto_gchar gchar* status_msg = g_strdup_printf("Uploading '%s': 100%%", upload->filename); + auto_gchar gchar* status_msg = g_strdup_printf("Uploading '%s': done", upload->filename); if (!status_msg) { status_msg = g_strdup(FALLBACK_MSG); } - win_update_entry_message(upload->window, upload->put_url, status_msg); + win_update_entry(upload->window, upload->put_url, status_msg, THEME_ONLINE, ENTRY_COMPLETED); win_mark_received(upload->window, upload->put_url); char* url = NULL; diff --git a/src/ui/ui.h b/src/ui/ui.h index f47bb906..ea0777db 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -25,12 +25,14 @@ #include "otr/otr.h" #endif -#define NO_ME 1 -#define NO_DATE 2 -#define NO_EOL 4 -#define NO_COLOUR_FROM 8 -#define NO_COLOUR_DATE 16 -#define UNTRUSTED 32 +#define NO_ME 1 +#define NO_DATE 2 +#define NO_EOL 4 +#define NO_COLOUR_FROM 8 +#define NO_COLOUR_DATE 16 +#define UNTRUSTED 32 +#define ENTRY_COMPLETED 64 +#define ENTRY_ERROR 128 // core UI void ui_init(void); diff --git a/src/ui/window.c b/src/ui/window.c index 218e252d..2ee55176 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -1659,7 +1659,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* id) { - win_print_outgoing_with_receipt(window, "!", NULL, message, id, NULL); + win_print_status_with_id(window, message, id, THEME_DEFAULT, 0); } void @@ -1708,6 +1708,33 @@ win_update_entry_message(ProfWin* window, const char* const id, const char* cons } } +void +win_update_entry(ProfWin* window, const char* const id, const char* const message, theme_item_t theme_item, int flags) +{ + if (window->type == WIN_CONSOLE) + return; + ProfBuffEntry* entry = buffer_get_entry_by_id(window->layout->buffer, id); + if (entry) { + if (message) { + free(entry->message); + entry->message = strdup(message); + } + entry->theme_item = theme_item; + entry->flags |= flags; + win_redraw(window); + } +} + +void +win_print_status_with_id(ProfWin* window, const char* const message, char* id, theme_item_t theme_item, int flags) +{ + GDateTime* time = g_date_time_new_now_local(); + int y_start_pos = getcury(window->layout->win); + _win_print_internal(window, "!", 0, time, flags, theme_item, NULL, message, NULL); + buffer_append(window->layout->buffer, "!", 0, time, flags, theme_item, NULL, NULL, message, NULL, id, y_start_pos, getcury(window->layout->win)); + g_date_time_unref(time); +} + void win_remove_entry_message(ProfWin* window, const char* const id) { diff --git a/src/ui/window.h b/src/ui/window.h index 08e3e8e3..55665511 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -56,6 +56,8 @@ void win_sub_print(WINDOW* win, char* msg, gboolean newline, gboolean wrap, int void win_sub_newline_lazy(WINDOW* win); void win_mark_received(ProfWin* window, const char* const id); void win_update_entry_message(ProfWin* window, const char* const id, const char* const message); +void win_update_entry(ProfWin* window, const char* const id, const char* const message, theme_item_t theme_item, int flags); +void win_print_status_with_id(ProfWin* window, const char* const message, char* id, theme_item_t theme_item, int flags); gboolean win_has_active_subwin(ProfWin* window);