diff options
author | Cameron Paul <cpaul37@gmail.com> | 2025-03-23 13:55:22 -0500 |
---|---|---|
committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2025-05-01 00:56:42 +0200 |
commit | fd401b642694684c517e09d99c0c35ea1a08342c (patch) | |
tree | a2141f7abf9a0deae3f256bfecdba7045fd59254 | |
parent | 19559f536b1e6f4abbfad8e9f29844eaf6544322 (diff) |
Support for Content-Disposition filename
-rw-r--r-- | src/cache.c | 19 | ||||
-rw-r--r-- | src/menu.cc | 2 | ||||
-rw-r--r-- | src/misc.c | 46 | ||||
-rw-r--r-- | src/misc.h | 1 | ||||
-rw-r--r-- | src/uicmd.cc | 10 | ||||
-rw-r--r-- | src/uicmd.hh | 2 |
6 files changed, 72 insertions, 8 deletions
diff --git a/src/cache.c b/src/cache.c index d2827e65..319de1b6 100644 --- a/src/cache.c +++ b/src/cache.c @@ -52,6 +52,7 @@ typedef struct { char *TypeHdr; /**< MIME type string as from the HTTP Header */ char *TypeMeta; /**< MIME type string from META HTTP-EQUIV */ char *TypeNorm; /**< MIME type string normalized */ + char *ContentDisposition; /**< Content-Disposition header */ Dstr *Header; /**< HTTP header */ const DilloUrl *Location; /**< New URI for redirects */ Dlist *Auth; /**< Authentication fields */ @@ -196,6 +197,7 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url) NewEntry->TypeHdr = NULL; NewEntry->TypeMeta = NULL; NewEntry->TypeNorm = NULL; + NewEntry->ContentDisposition = NULL; NewEntry->Header = dStr_new(""); NewEntry->Location = NULL; NewEntry->Auth = NULL; @@ -302,6 +304,7 @@ static void Cache_entry_free(CacheEntry_t *entry) dFree(entry->TypeHdr); dFree(entry->TypeMeta); dFree(entry->TypeNorm); + dFree(entry->ContentDisposition); dStr_free(entry->Header, TRUE); a_Url_free((DilloUrl *)entry->Location); Cache_auth_free(entry->Auth); @@ -813,6 +816,9 @@ static void Cache_parse_header(CacheEntry_t *entry) _MSG("TypeMeta {%s}\n", entry->TypeMeta); dFree(Type); } + + entry->ContentDisposition = Cache_parse_field(header, "Content-Disposition"); + Cache_ref_data(entry); } @@ -1123,6 +1129,7 @@ static void Cache_null_client(int Op, CacheClient_t *Client) typedef struct { BrowserWindow *bw; DilloUrl *url; + char* filename; } Cache_savelink_t; /** @@ -1133,7 +1140,7 @@ static void Cache_savelink_cb(void *vdata) { Cache_savelink_t *data = (Cache_savelink_t*) vdata; - a_UIcmd_save_link(data->bw, data->url); + a_UIcmd_save_link(data->bw, data->url, data->filename); a_Url_free(data->url); dFree(data); } @@ -1184,6 +1191,8 @@ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry) bool_t AbortEntry = FALSE; bool_t OfferDownload = FALSE; bool_t TypeMismatch = FALSE; + char *dtype = NULL; + char *dfilename = NULL; if (Busy) MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n"); @@ -1204,6 +1213,9 @@ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry) } else return entry; /* i.e., wait for more data */ } + if (entry->ContentDisposition) { + a_Misc_parse_content_disposition(entry->ContentDisposition, &dtype, &dfilename); + } Busy = TRUE; for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) { @@ -1268,6 +1280,8 @@ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry) * connection and to keep a failed-resource flag in * the cache entry. */ } + } else if (dtype && dStrnAsciiCasecmp(dtype, "attachment", 10) == 0) { + AbortEntry = OfferDownload = TRUE; } } if (AbortEntry) { @@ -1337,6 +1351,7 @@ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry) Cache_savelink_t *data = dNew(Cache_savelink_t, 1); data->bw = Client_bw; data->url = a_Url_dup(url); + data->filename = dStrdup(dfilename); a_Timeout_add(0.0, Cache_savelink_cb, data); } } @@ -1345,6 +1360,8 @@ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry) Cache_auth_entry(entry, Client_bw); } + dFree(dtype); dFree(dfilename); + /* Trigger cleanup when there are no cache clients */ if (dList_length(ClientQueue) == 0) { a_Dicache_cleanup(); diff --git a/src/menu.cc b/src/menu.cc index 3cdc2f77..0a33e0ce 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -147,7 +147,7 @@ static void Menu_find_text_cb(Fl_Widget*, void*) static void Menu_save_link_cb(Fl_Widget*, void *user_data) { DilloUrl *url = (DilloUrl *)user_data; - a_UIcmd_save_link(popup_bw, url); + a_UIcmd_save_link(popup_bw, url, NULL); } /** @@ -354,6 +354,52 @@ int a_Misc_content_type_check(const char *EntryType, const char *DetectedType) return st; } + +/** + * Parse Content-Disposition string, e.g., "attachment; filename="file name.jpg"". + * Content-Disposition is defined in RFC 6266 + */ +void a_Misc_parse_content_disposition(const char *disposition, char **type, char **filename) +{ + static const char tspecials_space[] = "()<>@,;:\\\"/[]?= "; + const char *str, *s; + + if (type) + *type = NULL; + if (filename) + *filename = NULL; + if (!(str = disposition)) + return; + + for (s = str; *s && d_isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) && + !strchr(tspecials_space, *s); s++) ; + if (type) + *type = dStrndup(str, s - str); + + if (*s == ';') { + const char terminators[] = " ;\t"; + const char key[] = "filename"; + + if ((s = dStriAsciiStr(str, key)) && + (s == str || strchr(terminators, s[-1]))) { + s += sizeof(key) - 1; + for ( ; *s == ' ' || *s == '\t'; ++s); + if (*s == '=') { + size_t len; + for (++s; *s == ' ' || *s == '\t'; ++s); + if ((len = strcspn(s, terminators))) { + if (*s == '"' && s[len-1] == '"' && len > 1) { + /* quoted string */ + s++; + len -= 2; + } + *filename = dStrndup(s, len); + } + } + } + } +} + /** * Parse a geometry string. */ @@ -17,6 +17,7 @@ int a_Misc_content_type_check(const char *EntryType, const char *DetectedType); void a_Misc_parse_content_type(const char *str, char **major, char **minor, char **charset); int a_Misc_content_type_cmp(const char* ct1, const char *ct2); +void a_Misc_parse_content_disposition(const char *disposition, char **type, char **filename); int a_Misc_parse_geometry(char *geom, int *x, int *y, int *w, int *h); int a_Misc_parse_search_url(char *source, char **label, char **urlstr); char *a_Misc_encode_base64(const char *in); diff --git a/src/uicmd.cc b/src/uicmd.cc index 15207408..06333d0c 100644 --- a/src/uicmd.cc +++ b/src/uicmd.cc @@ -1088,10 +1088,10 @@ static int UIcmd_save_file_check(const char *name) /* * Save a URL */ -static void UIcmd_save(BrowserWindow *bw, const DilloUrl *url, +static void UIcmd_save(BrowserWindow *bw, const DilloUrl *url, char *filename, const char *title) { - char *SuggestedName = UIcmd_make_save_filename(url); + char *SuggestedName = filename ? filename : UIcmd_make_save_filename(url); while (1) { const char *name = a_Dialog_save_file(title, NULL, SuggestedName); @@ -1129,7 +1129,7 @@ void a_UIcmd_save(void *vbw) const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw)); if (url) { - UIcmd_save(bw, url, "Save Page as File"); + UIcmd_save(bw, url, NULL, "Save Page as File"); } } @@ -1248,9 +1248,9 @@ const char *a_UIcmd_get_passwd(const char *user) /* * Save link URL */ -void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url) +void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url, char *filename) { - UIcmd_save(bw, url, "Dillo: Save Link as File"); + UIcmd_save(bw, url, filename, "Dillo: Save Link as File"); } /* diff --git a/src/uicmd.hh b/src/uicmd.hh index 1343a4a9..b69b50db 100644 --- a/src/uicmd.hh +++ b/src/uicmd.hh @@ -45,7 +45,7 @@ void a_UIcmd_redirection0(void *vbw, const DilloUrl *url); void a_UIcmd_save(void *vbw); void a_UIcmd_stop(void *vbw); void a_UIcmd_tools(void *vbw, int x, int y); -void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url); +void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url, char *filename); void a_UIcmd_open_file(void *vbw); const char *a_UIcmd_select_file(void); void a_UIcmd_search_dialog(void *vbw); |