/* * aesgcm_download.c * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2012 - 2019 James Booth * Copyright (C) 2020 William Wennerström * * SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "profanity.h" #include "event/client_events.h" #include "tools/http_common.h" #include "tools/aesgcm_download.h" #include "omemo/omemo.h" #include "config/preferences.h" #include "ui/ui.h" #include "ui/window.h" #include "common.h" void* aesgcm_file_get(void* userdata) { AESGCMDownload* aesgcm_dl = (AESGCMDownload*)userdata; auto_char char* https_url = NULL; auto_char 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) { cons_show_error("Download failed: Cannot parse URL '%s'.", aesgcm_dl->url); 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; } // Create a temporary file used for storing the ciphertext that is to be // retrieved from the https:// URL. 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, THEME_ERROR, ENTRY_ERROR, "Downloading '%s' failed: Unable to create " "temporary ciphertext file for writing " "(%s).", https_url, g_strerror(errno)); return NULL; } // Open the target file for storing the cleartext. auto_FILE FILE* outfh = fopen(aesgcm_dl->filename, "wb"); if (outfh == NULL) { 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, g_strerror(errno)); return NULL; } // We wrap the HTTPDownload tool and use it for retrieving the ciphertext // and storing it in the temporary file previously opened. HTTPDownload* http_dl = g_new0(HTTPDownload, 1); http_dl->window = aesgcm_dl->window; 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; ssize_t* p_bytes_received = http_file_get(http_dl); if (!p_bytes_received) { return NULL; } ssize_t bytes_received = *p_bytes_received; free(p_bytes_received); FILE* tmpfh = fopen(tmpname, "rb"); if (tmpfh == NULL) { 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, g_strerror(errno)); return NULL; } gcry_error_t crypt_res; crypt_res = omemo_decrypt_file(tmpfh, outfh, bytes_received, fragment); fclose(tmpfh); remove(tmpname); if (crypt_res != GPG_ERR_NO_ERROR) { 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)); } else { 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) { gchar** argv = format_call_external_argv(aesgcm_dl->cmd_template, aesgcm_dl->filename, aesgcm_dl->filename); // TODO: Log the error. if (!call_external(argv)) { 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, aesgcm_dl->cmd_template, aesgcm_dl->filename, "TODO: Log the error"); } g_strfreev(argv); free(aesgcm_dl->cmd_template); } free(aesgcm_dl->id); free(aesgcm_dl->filename); free(aesgcm_dl->url); free(aesgcm_dl); return NULL; } void aesgcm_download_cancel_processes(ProfWin* window) { http_download_cancel_processes(window); } void aesgcm_download_add_download(AESGCMDownload* aesgcm_dl) { http_download_add_download(aesgcm_dl->http_dl); }