mirror of
https://github.com/profanity-im/profanity.git
synced 2024-12-04 14:46:46 -05:00
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.
This commit is contained in:
parent
9499df6585
commit
eebf54c859
@ -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 = \
|
||||
|
@ -4,6 +4,7 @@
|
||||
*
|
||||
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||
* Copyright (C) 2019 Michael Vetter <jubalh@iodoru.org>
|
||||
* Copyright (C) 2020 William Wennerström <william@wstrm.dev>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
119
tests/unittests/test_http_download.c
Normal file
119
tests/unittests/test_http_download.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
2
tests/unittests/test_http_download.h
Normal file
2
tests/unittests/test_http_download.h
Normal file
@ -0,0 +1,2 @@
|
||||
void http_filename_from_url_td(void **state);
|
||||
void http_filename_from_header_td(void **state);
|
28
tests/unittests/tools/stub_http_download.c
Normal file
28
tests/unittests/tools/stub_http_download.c
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef TOOLS_HTTP_DOWNLOAD_H
|
||||
#define TOOLS_HTTP_DOWNLOAD_H
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct prof_win_t ProfWin;
|
||||
|
||||
typedef struct http_download_t {
|
||||
char *url;
|
||||
char *filename;
|
||||
char *directory;
|
||||
FILE *filehandle;
|
||||
curl_off_t bytes_received;
|
||||
ProfWin *window;
|
||||
pthread_t worker;
|
||||
int cancel;
|
||||
} HTTPDownload;
|
||||
|
||||
|
||||
void* http_file_get(void *userdata);
|
||||
|
||||
void http_download_cancel_processes(ProfWin *window);
|
||||
void http_download_add_download(HTTPDownload *download);
|
||||
|
||||
char *http_filename_from_url(const char *url);
|
||||
|
||||
#endif
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user