about summary refs log tree commit diff stats
path: root/src/tools/http_download.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/http_download.c')
-rw-r--r--src/tools/http_download.c125
1 files changed, 118 insertions, 7 deletions
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;