/*
 * aesgcm_download.c
 * vim: expandtab:ts=4:sts=4:sw=4
 *
 * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
 * Copyright (C) 2020 William Wennerström <william@wstrm.dev>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <curl/curl.h>
#include <gio/gio.h>
#include <pthread.h>
#include <assert.h>
#include <errno.h>

#include "profanity.h"
#include "event/client_events.h"
#include "tools/http_common.h"
#include "tools/aesgcm_download.h"
#include "omemo/omemo.h"
#include "config/preferences.h"
#include "ui/ui.h"
#include "ui/window.h"
#include "common.h"

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).",
                                   aesgcm_dl->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);
}
