Unification of protocol handler names.
[elinks.git] / src / session / download.c
blob8a5e1c4b1c03fd2887ad3f37910ba9680bc2ce40
1 /** Downloads managment
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #ifdef HAVE_SYS_CYGWIN_H
13 #include <sys/cygwin.h>
14 #endif
15 #include <sys/types.h>
16 #ifdef HAVE_FCNTL_H
17 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
18 #endif
19 #include <sys/stat.h>
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23 #include <utime.h>
25 #include "elinks.h"
27 #include "bfu/dialog.h"
28 #include "cache/cache.h"
29 #include "config/options.h"
30 #include "dialogs/document.h"
31 #include "dialogs/download.h"
32 #include "dialogs/menu.h"
33 #include "intl/gettext/libintl.h"
34 #include "main/object.h"
35 #include "main/select.h"
36 #include "mime/mime.h"
37 #include "network/connection.h"
38 #include "network/progress.h"
39 #include "network/state.h"
40 #include "osdep/osdep.h"
41 #include "protocol/bittorrent/dialogs.h"
42 #include "protocol/date.h"
43 #include "protocol/protocol.h"
44 #include "protocol/uri.h"
45 #include "session/download.h"
46 #include "session/history.h"
47 #include "session/location.h"
48 #include "session/session.h"
49 #include "session/task.h"
50 #include "terminal/draw.h"
51 #include "terminal/screen.h"
52 #include "terminal/terminal.h"
53 #include "util/conv.h"
54 #include "util/error.h"
55 #include "util/file.h"
56 #include "util/lists.h"
57 #include "util/memlist.h"
58 #include "util/memory.h"
59 #include "util/string.h"
60 #include "util/time.h"
63 /* TODO: tp_*() should be in separate file, I guess? --pasky */
66 INIT_LIST_OF(struct file_download, downloads);
68 int
69 download_is_progressing(struct download *download)
71 return download
72 && is_in_state(download->state, S_TRANS)
73 && has_progress(download->progress);
76 int
77 are_there_downloads(void)
79 struct file_download *file_download;
81 foreach (file_download, downloads)
82 if (!file_download->external_handler)
83 return 1;
85 return 0;
89 static void download_data(struct download *download, struct file_download *file_download);
91 /*! @note If this fails, the caller is responsible of freeing @a file
92 * and closing @a fd. */
93 struct file_download *
94 init_file_download(struct uri *uri, struct session *ses, unsigned char *file, int fd)
96 struct file_download *file_download;
98 file_download = mem_calloc(1, sizeof(*file_download));
99 if (!file_download) return NULL;
101 /* Actually we could allow fragments in the URI and just change all the
102 * places that compares and shows the URI, but for now it is much
103 * easier this way. */
104 file_download->uri = get_composed_uri(uri, URI_BASE);
105 if (!file_download->uri) {
106 mem_free(file_download);
107 return NULL;
110 init_download_display(file_download);
112 file_download->file = file;
113 file_download->handle = fd;
115 file_download->download.callback = (download_callback_T *) download_data;
116 file_download->download.data = file_download;
117 file_download->ses = ses;
118 /* The tab may be closed, but we will still want to ie. open the
119 * handler on that terminal. */
120 file_download->term = ses->tab->term;
122 object_nolock(file_download, "file_download"); /* Debugging purpose. */
123 add_to_list(downloads, file_download);
125 return file_download;
129 void
130 abort_download(struct file_download *file_download)
132 #if 0
133 /* When hacking to cleanup the download code, remove lots of duplicated
134 * code and implement stuff from bug 435 we should reintroduce this
135 * assertion. Currently it will trigger often and shows that the
136 * download dialog code potentially could access free()d memory. */
137 assert(!is_object_used(file_download));
138 #endif
139 done_download_display(file_download);
141 if (file_download->ses)
142 check_questions_queue(file_download->ses);
144 if (file_download->dlg_data)
145 cancel_dialog(file_download->dlg_data, NULL);
146 cancel_download(&file_download->download, file_download->stop);
147 if (file_download->uri) done_uri(file_download->uri);
149 if (file_download->handle != -1) {
150 prealloc_truncate(file_download->handle, file_download->seek);
151 close(file_download->handle);
154 mem_free_if(file_download->external_handler);
155 if (file_download->file) {
156 if (file_download->delete) unlink(file_download->file);
157 mem_free(file_download->file);
159 del_from_list(file_download);
160 mem_free(file_download);
164 static void
165 kill_downloads_to_file(unsigned char *file)
167 struct file_download *file_download;
169 foreach (file_download, downloads) {
170 if (strcmp(file_download->file, file))
171 continue;
173 file_download = file_download->prev;
174 abort_download(file_download->next);
179 void
180 abort_all_downloads(void)
182 while (!list_empty(downloads))
183 abort_download(downloads.next);
187 void
188 destroy_downloads(struct session *ses)
190 struct file_download *file_download, *next;
191 struct session *s;
193 /* We are supposed to blat all downloads to external handlers belonging
194 * to @ses, but we will refuse to do so if there is another session
195 * bound to this terminal. That looks like the reasonable thing to do,
196 * fulfilling the principle of least astonishment. */
197 foreach (s, sessions) {
198 if (s == ses || s->tab->term != ses->tab->term)
199 continue;
201 foreach (file_download, downloads) {
202 if (file_download->ses != ses)
203 continue;
205 file_download->ses = s;
208 return;
211 foreachsafe (file_download, next, downloads) {
212 if (file_download->ses != ses)
213 continue;
215 if (!file_download->external_handler) {
216 file_download->ses = NULL;
217 continue;
220 abort_download(file_download);
224 void
225 detach_downloads_from_terminal(struct terminal *term)
227 struct file_download *file_download, *next;
229 assert(term != NULL);
230 if_assert_failed return;
232 foreachsafe (file_download, next, downloads) {
233 if (file_download->term != term)
234 continue;
236 if (!file_download->external_handler) {
237 file_download->term = NULL;
238 if (file_download->ses
239 && file_download->ses->tab->term == term)
240 file_download->ses = NULL;
241 continue;
244 abort_download(file_download);
248 static void
249 download_error_dialog(struct file_download *file_download, int saved_errno)
251 unsigned char *emsg = (unsigned char *) strerror(saved_errno);
252 struct session *ses = file_download->ses;
253 struct terminal *term = file_download->term;
255 if (!ses) return;
257 info_box(term, MSGBOX_FREE_TEXT,
258 N_("Download error"), ALIGN_CENTER,
259 msg_text(term, N_("Could not create file '%s':\n%s"),
260 file_download->file, emsg));
263 static int
264 write_cache_entry_to_file(struct cache_entry *cached, struct file_download *file_download)
266 struct fragment *frag;
268 if (file_download->download.progress && file_download->download.progress->seek) {
269 file_download->seek = file_download->download.progress->seek;
270 file_download->download.progress->seek = 0;
271 /* This is exclusive with the prealloc, thus we can perform
272 * this in front of that thing safely. */
273 if (lseek(file_download->handle, file_download->seek, SEEK_SET) < 0) {
274 download_error_dialog(file_download, errno);
275 return 0;
279 foreach (frag, cached->frag) {
280 off_t remain = file_download->seek - frag->offset;
281 int *h = &file_download->handle;
282 ssize_t w;
284 if (remain < 0 || frag->length <= remain)
285 continue;
287 #ifdef USE_OPEN_PREALLOC
288 if (!file_download->seek
289 && (!file_download->download.progress
290 || file_download->download.progress->size > 0)) {
291 close(*h);
292 *h = open_prealloc(file_download->file,
293 O_CREAT|O_WRONLY|O_TRUNC,
294 0666,
295 file_download->download.progress
296 ? file_download->download.progress->size
297 : cached->length);
298 if (*h == -1) {
299 download_error_dialog(file_download, errno);
300 return 0;
302 set_bin(*h);
304 #endif
306 w = safe_write(*h, frag->data + remain, frag->length - remain);
307 if (w == -1) {
308 download_error_dialog(file_download, errno);
309 return 0;
312 file_download->seek += w;
315 return 1;
318 static void
319 abort_download_and_beep(struct file_download *file_download, struct terminal *term)
321 if (term && get_opt_int("document.download.notify_bell",
322 file_download->ses)
323 + file_download->notify >= 2) {
324 beep_terminal(term);
327 abort_download(file_download);
330 struct exec_mailcap {
331 struct session *ses;
332 unsigned char *command;
333 unsigned char *file;
336 static void
337 do_follow_url_mailcap(struct session *ses, struct uri *uri)
339 if (!uri) {
340 print_error_dialog(ses, connection_state(S_BAD_URL), uri, PRI_CANCEL);
341 return;
344 ses->reloadlevel = CACHE_MODE_NORMAL;
346 if (ses->task.type == TASK_FORWARD) {
347 if (compare_uri(ses->loading_uri, uri, 0)) {
348 /* We're already loading the URL. */
349 return;
353 abort_loading(ses, 0);
355 ses_goto(ses, uri, NULL, NULL, CACHE_MODE_NORMAL, TASK_FORWARD, 0);
359 static void
360 exec_mailcap_command(void *data)
362 struct exec_mailcap *exec_mailcap = data;
364 if (exec_mailcap) {
365 if (exec_mailcap->command) {
366 struct string string;
368 if (init_string(&string)) {
369 struct uri *ref = get_uri("mailcap:elmailcap", 0);
370 struct uri *uri;
371 struct session *ses = exec_mailcap->ses;
373 add_to_string(&string, "mailcap:");
374 add_to_string(&string, exec_mailcap->command);
375 if (exec_mailcap->file) {
376 add_to_string(&string, ";/bin/rm -f ");
377 add_to_string(&string, exec_mailcap->file);
380 uri = get_uri(string.source, 0);
381 done_string(&string);
382 set_session_referrer(ses, ref);
383 if (ref) done_uri(ref);
385 do_follow_url_mailcap(ses, uri);
386 if (uri) done_uri(uri);
388 mem_free(exec_mailcap->command);
390 mem_free_if(exec_mailcap->file);
391 mem_free(exec_mailcap);
395 static void
396 exec_later(struct session *ses, unsigned char *handler, unsigned char *file)
398 struct exec_mailcap *exec_mailcap = calloc(1, sizeof(*exec_mailcap));
400 if (exec_mailcap) {
401 exec_mailcap->ses = ses;
402 exec_mailcap->command = null_or_stracpy(handler);
403 exec_mailcap->file = null_or_stracpy(file);
404 register_bottom_half(exec_mailcap_command, exec_mailcap);
408 static void
409 download_data_store(struct download *download, struct file_download *file_download)
411 struct terminal *term = file_download->term;
413 assert_terminal_ptr_not_dangling(term);
414 if_assert_failed term = file_download->term = NULL;
416 if (is_in_progress_state(download->state)) {
417 if (file_download->dlg_data)
418 redraw_dialog(file_download->dlg_data, 1);
419 return;
422 /* If the original terminal of the download has been closed,
423 * display any messages in the default terminal instead. */
424 if (term == NULL)
425 term = get_default_terminal(); /* may be NULL too */
427 if (!is_in_state(download->state, S_OK)) {
428 unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);
429 struct connection_state state = download->state;
431 /* abort_download_and_beep allows term==NULL. */
432 abort_download_and_beep(file_download, term);
434 if (!url) return;
436 if (term) {
437 info_box(term, MSGBOX_FREE_TEXT,
438 N_("Download error"), ALIGN_CENTER,
439 msg_text(term, N_("Error downloading %s:\n\n%s"),
440 url, get_state_message(state, term)));
442 mem_free(url);
443 return;
446 if (file_download->external_handler) {
447 if (term == NULL) {
448 /* There is no terminal in which to run the handler.
449 * Abort the download. file_download->delete should
450 * be 1 here so that the following call also deletes
451 * the temporary file. */
452 abort_download(file_download);
453 return;
456 prealloc_truncate(file_download->handle, file_download->seek);
457 close(file_download->handle);
458 file_download->handle = -1;
459 if (file_download->copiousoutput) {
460 exec_later(file_download->ses,
461 file_download->external_handler, file_download->file);
462 /* Temporary file is deleted by the mailcap_protocol_handler */
463 file_download->delete = 0;
464 } else {
465 exec_on_terminal(term, file_download->external_handler,
466 file_download->file,
467 file_download->block ? TERM_EXEC_FG :
468 TERM_EXEC_BG);
470 /* file_download->delete = 0; */
471 abort_download_and_beep(file_download, term);
472 return;
475 if (file_download->notify && term) {
476 unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);
478 /* This is apparently a little racy. Deleting the box item will
479 * update the download browser _after_ the notification dialog
480 * has been drawn whereby it will be hidden. This should make
481 * the download browser update before launcing any
482 * notification. */
483 done_download_display(file_download);
485 if (url) {
486 info_box(term, MSGBOX_FREE_TEXT,
487 N_("Download"), ALIGN_CENTER,
488 msg_text(term, N_("Download complete:\n%s"), url));
489 mem_free(url);
493 if (file_download->remotetime
494 && get_opt_bool("document.download.set_original_time",
495 file_download->ses)) {
496 struct utimbuf foo;
498 foo.actime = foo.modtime = file_download->remotetime;
499 utime(file_download->file, &foo);
502 /* abort_download_and_beep allows term==NULL. */
503 abort_download_and_beep(file_download, term);
506 static void
507 download_data(struct download *download, struct file_download *file_download)
509 struct cache_entry *cached = download->cached;
511 if (!cached || is_in_queued_state(download->state)) {
512 download_data_store(download, file_download);
513 return;
516 if (cached->last_modified)
517 file_download->remotetime = parse_date(&cached->last_modified, NULL, 0, 1);
519 if (cached->redirect && file_download->redirect_cnt++ < MAX_REDIRECTS) {
520 cancel_download(&file_download->download, 0);
522 assertm(compare_uri(cached->uri, file_download->uri, 0),
523 "Redirecting using bad base URI");
525 done_uri(file_download->uri);
527 file_download->uri = get_uri_reference(cached->redirect);
528 file_download->download.state = connection_state(S_WAIT_REDIR);
530 if (file_download->dlg_data)
531 redraw_dialog(file_download->dlg_data, 1);
533 load_uri(file_download->uri, cached->uri, &file_download->download,
534 PRI_DOWNLOAD, CACHE_MODE_NORMAL,
535 download->progress ? download->progress->start : 0);
537 return;
540 if (!write_cache_entry_to_file(cached, file_download)) {
541 detach_connection(download, file_download->seek);
542 abort_download(file_download);
543 return;
546 detach_connection(download, file_download->seek);
547 download_data_store(download, file_download);
550 /** Type of the callback function that will be called when the user
551 * answers the question posed by lookup_unique_name().
553 * @param term
554 * The terminal on which the callback should display any windows.
555 * Comes directly from the @a term argument of lookup_unique_name().
557 * @param file
558 * The name of the local file to which the data should be downloaded,
559 * or NULL if the download should not begin. The callback is
560 * responsible of doing mem_free(@a file).
562 * @param data
563 * A pointer to any data that the callback cares about.
564 * Comes directly from the @a data argument of lookup_unique_name().
566 * @param flags
567 * The same as the @a flags argument of create_download_file(),
568 * except the ::DOWNLOAD_RESUME_SELECTED bit will be changed to match
569 * what the user chose.
571 * @relates lun_hop */
572 typedef void lun_callback_T(struct terminal *term, unsigned char *file,
573 void *data, enum download_flags flags);
575 /** The user is being asked what to do when the local file for
576 * the download already exists. This structure is allocated by
577 * lookup_unique_name() and freed by each lun_* function:
578 * lun_alternate(), lun_cancel(), lun_overwrite(), and lun_resume(). */
579 struct lun_hop {
580 /** The terminal in which ELinks is asking the question.
581 * This gets passed to #callback. */
582 struct terminal *term;
584 /** The name of the local file into which the data was
585 * originally going to be downloaded, but which already
586 * exists. In this string, "~" has already been expanded
587 * to the home directory. The string must be freed with
588 * mem_free(). */
589 unsigned char *ofile;
591 /** An alternative file name that the user may choose instead
592 * of #ofile. The string must be freed with mem_free(). */
593 unsigned char *file;
595 /** This function will be called when the user answers. */
596 lun_callback_T *callback;
598 /** A pointer to be passed to #callback. */
599 void *data;
601 /** Saved flags to be passed to #callback.
602 * If the user chooses to resume, then lun_resume() sets
603 * ::DOWNLOAD_RESUME_SELECTED when it calls #callback.
605 * @invariant The ::DOWNLOAD_RESUME_SELECTED bit should be
606 * clear here because otherwise there would have been no
607 * reason to ask the user and initialize this structure. */
608 enum download_flags flags;
611 /** Data saved by common_download() for the common_download_do()
612 * callback. */
613 struct cmdw_hop {
614 struct session *ses;
616 /** The URI from which the data will be downloaded. */
617 struct uri *download_uri;
619 /** The name of the local file to which the data will be
620 * downloaded. This is initially NULL, but its address is
621 * given to create_download_file(), which arranges for the
622 * pointer to be set before common_download_do() is called.
623 * The string must be freed with mem_free(). */
624 unsigned char *real_file;
627 /** Data saved by continue_download() for the continue_download_do()
628 * callback. */
629 struct codw_hop {
630 struct type_query *type_query;
632 /** The name of the local file to which the data will be
633 * downloaded. This is initially NULL, but its address is
634 * given to create_download_file(), which arranges for the
635 * pointer to be set before continue_download_do() is called.
636 * The string must be freed with mem_free(). */
637 unsigned char *real_file;
639 unsigned char *file;
642 /** Data saved by create_download_file() for the create_download_file_do()
643 * callback. */
644 struct cdf_hop {
645 /** Where to save the name of the file that was actually
646 * opened. One of the arguments of #callback is a file
647 * descriptor for this file. @c real_file can be NULL if
648 * #callback does not care about the name. */
649 unsigned char **real_file;
651 /** This function will be called when the file has been opened,
652 * or when it is known that the file will not be opened. */
653 cdf_callback_T *callback;
655 /** A pointer to be passed to #callback. */
656 void *data;
659 /** The use chose "Save under the alternative name" when asked where
660 * to download a file.
662 * lookup_unique_name() passes this function as a ::done_handler_T to
663 * msg_box().
665 * @relates lun_hop */
666 static void
667 lun_alternate(void *lun_hop_)
669 struct lun_hop *lun_hop = lun_hop_;
671 lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data,
672 lun_hop->flags);
673 mem_free_if(lun_hop->ofile);
674 mem_free(lun_hop);
677 /** The use chose "Cancel" when asked where to download a file.
679 * lookup_unique_name() passes this function as a ::done_handler_T to
680 * msg_box().
682 * @relates lun_hop */
683 static void
684 lun_cancel(void *lun_hop_)
686 struct lun_hop *lun_hop = lun_hop_;
688 lun_hop->callback(lun_hop->term, NULL, lun_hop->data,
689 lun_hop->flags);
690 mem_free_if(lun_hop->ofile);
691 mem_free_if(lun_hop->file);
692 mem_free(lun_hop);
695 /** The use chose "Overwrite the original file" when asked where to
696 * download a file.
698 * lookup_unique_name() passes this function as a ::done_handler_T to
699 * msg_box().
701 * @relates lun_hop */
702 static void
703 lun_overwrite(void *lun_hop_)
705 struct lun_hop *lun_hop = lun_hop_;
707 lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data,
708 lun_hop->flags);
709 mem_free_if(lun_hop->file);
710 mem_free(lun_hop);
713 /** The user chose "Resume download of the original file" when asked
714 * where to download a file.
716 * lookup_unique_name() passes this function as a ::done_handler_T to
717 * msg_box().
719 * @relates lun_hop */
720 static void
721 lun_resume(void *lun_hop_)
723 struct lun_hop *lun_hop = lun_hop_;
725 lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data,
726 lun_hop->flags | DOWNLOAD_RESUME_SELECTED);
727 mem_free_if(lun_hop->file);
728 mem_free(lun_hop);
732 /** If attempting to download to an existing file, perhaps ask
733 * the user whether to resume, overwrite, or save elsewhere.
734 * This function constructs a struct lun_hop, which will be freed
735 * when the user answers the question.
737 * @param term
738 * The terminal in which this function should show its UI.
740 * @param[in] ofile
741 * A proposed name for the local file to which the data would be
742 * downloaded. "~" here refers to the home directory.
743 * lookup_unique_name() treats this original string as read-only.
745 * @param[in] flags
746 * Flags controlling how to download the file.
747 * ::DOWNLOAD_RESUME_ALLOWED adds a "Resume" button to the dialog.
748 * ::DOWNLOAD_RESUME_SELECTED means the user already chose to resume
749 * downloading (with ::ACT_MAIN_LINK_DOWNLOAD_RESUME), before ELinks
750 * even asked for the file name; thus don't ask whether to overwrite.
751 * Other flags, such as ::DOWNLOAD_EXTERNAL, have no effect at this
752 * level but they get passed to @a callback.
754 * @param callback
755 * Will be called when the user answers, or right away if the question
756 * need not or cannot be asked.
758 * @param data
759 * A pointer to be passed to @a callback.
761 * @relates lun_hop */
762 static void
763 lookup_unique_name(struct terminal *term, unsigned char *ofile,
764 enum download_flags flags,
765 lun_callback_T *callback, void *data)
767 /* [gettext_accelerator_context(.lookup_unique_name)] */
768 struct lun_hop *lun_hop = NULL;
769 unsigned char *file = NULL;
770 struct dialog_data *dialog_data;
771 int overwrite;
773 ofile = expand_tilde(ofile);
774 if (!ofile) goto error;
776 /* Minor code duplication to prevent useless call to get_opt_int()
777 * if possible. --Zas */
778 if (flags & DOWNLOAD_RESUME_SELECTED) {
779 callback(term, ofile, data, flags);
780 return;
783 /* !overwrite means always silently overwrite, which may be admitelly
784 * indeed a little confusing ;-) */
785 overwrite = get_opt_int("document.download.overwrite", NULL);
786 if (!overwrite) {
787 /* Nothing special to do... */
788 callback(term, ofile, data, flags);
789 return;
792 /* Check if file is a directory, and use a default name if it's the
793 * case. */
794 if (file_is_dir(ofile)) {
795 info_box(term, MSGBOX_FREE_TEXT,
796 N_("Download error"), ALIGN_CENTER,
797 msg_text(term, N_("'%s' is a directory."),
798 ofile));
799 goto error;
802 /* Check if the file already exists (file != ofile). */
803 file = get_unique_name(ofile);
805 if (!file || overwrite == 1 || file == ofile) {
806 /* Still nothing special to do... */
807 if (file != ofile) mem_free(ofile);
808 callback(term, file, data, flags & ~DOWNLOAD_RESUME_SELECTED);
809 return;
812 /* overwrite == 2 (ask) and file != ofile (=> original file already
813 * exists) */
815 lun_hop = mem_calloc(1, sizeof(*lun_hop));
816 if (!lun_hop) goto error;
817 lun_hop->term = term;
818 lun_hop->ofile = ofile;
819 lun_hop->file = file; /* file != ofile verified above */
820 lun_hop->callback = callback;
821 lun_hop->data = data;
822 lun_hop->flags = flags;
824 dialog_data = msg_box(
825 term, NULL, MSGBOX_FREE_TEXT,
826 N_("File exists"), ALIGN_CENTER,
827 msg_text(term, N_("This file already exists:\n"
828 "%s\n\n"
829 "The alternative filename is:\n"
830 "%s"),
831 empty_string_or_(lun_hop->ofile),
832 empty_string_or_(file)),
833 lun_hop, 4,
834 MSG_BOX_BUTTON(N_("Sa~ve under the alternative name"), lun_alternate, B_ENTER),
835 MSG_BOX_BUTTON(N_("~Overwrite the original file"), lun_overwrite, 0),
836 MSG_BOX_BUTTON((flags & DOWNLOAD_RESUME_ALLOWED
837 ? N_("~Resume download of the original file")
838 : NULL),
839 lun_resume, 0),
840 MSG_BOX_BUTTON(N_("~Cancel"), lun_cancel, B_ESC));
841 if (!dialog_data) goto error;
842 return;
844 error:
845 mem_free_if(lun_hop);
846 if (file != ofile) mem_free_if(file);
847 mem_free_if(ofile);
848 callback(term, NULL, data, flags & ~DOWNLOAD_RESUME_SELECTED);
853 /** Now that the final name of the download file has been chosen,
854 * open the file and call the ::cdf_callback_T that was originally
855 * given to create_download_file().
857 * create_download_file() passes this function as a ::lun_callback_T
858 * to lookup_unique_name().
860 * @relates cdf_hop */
861 static void
862 create_download_file_do(struct terminal *term, unsigned char *file,
863 void *data, enum download_flags flags)
865 struct cdf_hop *cdf_hop = data;
866 unsigned char *wd;
867 int h = -1;
868 int saved_errno;
869 #ifdef NO_FILE_SECURITY
870 int sf = 0;
871 #else
872 int sf = !!(flags & DOWNLOAD_EXTERNAL);
873 #endif
875 if (!file) goto finish;
877 wd = get_cwd();
878 set_cwd(term->cwd);
880 /* Create parent directories if needed. */
881 mkalldirs(file);
883 /* O_APPEND means repositioning at the end of file before each write(),
884 * thus ignoring seek()s and that can hide mysterious bugs. IMHO.
885 * --pasky */
886 h = open(file, O_CREAT | O_WRONLY
887 | (flags & DOWNLOAD_RESUME_SELECTED ? 0 : O_TRUNC)
888 | (sf && !(flags & DOWNLOAD_RESUME_SELECTED) ? O_EXCL : 0),
889 sf ? 0600 : 0666);
890 saved_errno = errno; /* Saved in case of ... --Zas */
892 if (wd) {
893 set_cwd(wd);
894 mem_free(wd);
897 if (h == -1) {
898 info_box(term, MSGBOX_FREE_TEXT,
899 N_("Download error"), ALIGN_CENTER,
900 msg_text(term, N_("Could not create file '%s':\n%s"),
901 file, strerror(saved_errno)));
903 mem_free(file);
904 goto finish;
906 } else {
907 set_bin(h);
909 if (!(flags & DOWNLOAD_EXTERNAL)) {
910 unsigned char *download_dir = get_opt_str("document.download.directory", NULL);
911 int i;
913 safe_strncpy(download_dir, file, MAX_STR_LEN);
915 /* Find the used directory so it's available in history */
916 for (i = strlen(download_dir); i >= 0; i--)
917 if (dir_sep(download_dir[i]))
918 break;
919 download_dir[i + 1] = 0;
923 if (cdf_hop->real_file)
924 *cdf_hop->real_file = file;
925 else
926 mem_free(file);
928 finish:
929 cdf_hop->callback(term, h, cdf_hop->data, flags);
930 mem_free(cdf_hop);
933 /** Create a file to which data can be downloaded.
934 * This function constructs a struct cdf_hop that will be freed
935 * when @a callback returns.
937 * @param term
938 * If any dialog boxes are needed, show them in this terminal.
940 * @param fi
941 * A proposed name for the local file to which the data would be
942 * downloaded. "~" here refers to the home directory.
943 * create_download_file() treats this original string as read-only.
945 * @param real_file
946 * If non-NULL, prepare to save in *@a real_file the name of the local
947 * file that was eventually opened. @a callback must then arrange for
948 * this string to be freed with mem_free().
950 * @param flags
951 * Flags controlling how to download the file.
952 * ::DOWNLOAD_RESUME_ALLOWED adds a "Resume" button to the dialog.
953 * ::DOWNLOAD_RESUME_SELECTED skips the dialog entirely.
954 * ::DOWNLOAD_EXTERNAL causes the file to be created with settings
955 * suitable for a temporary file: give only the user herself access to
956 * the file (even if the umask is looser), and create the file with
957 * @c O_EXCL unless resuming.
959 * @param callback
960 * This function will be called when the file has been opened,
961 * or when it is known that the file will not be opened.
963 * @param data
964 * A pointer to be passed to @a callback.
966 * @relates cdf_hop */
967 void
968 create_download_file(struct terminal *term, unsigned char *fi,
969 unsigned char **real_file,
970 enum download_flags flags,
971 cdf_callback_T *callback, void *data)
973 struct cdf_hop *cdf_hop = mem_calloc(1, sizeof(*cdf_hop));
974 unsigned char *wd;
976 if (!cdf_hop) {
977 callback(term, -1, data, flags & ~DOWNLOAD_RESUME_SELECTED);
978 return;
981 cdf_hop->real_file = real_file;
982 cdf_hop->callback = callback;
983 cdf_hop->data = data;
985 /* FIXME: The wd bussiness is probably useless here? --pasky */
986 wd = get_cwd();
987 set_cwd(term->cwd);
989 /* Also the tilde will be expanded here. */
990 lookup_unique_name(term, fi, flags, create_download_file_do, cdf_hop);
992 if (wd) {
993 set_cwd(wd);
994 mem_free(wd);
999 static unsigned char *
1000 get_temp_name(struct uri *uri)
1002 struct string name;
1003 unsigned char *extension;
1004 /* FIXME
1005 * We use tempnam() here, which is unsafe (race condition), for now.
1006 * This should be changed at some time, but it needs an in-depth work
1007 * of whole download code. --Zas */
1008 unsigned char *nm = tempnam(NULL, ELINKS_TEMPNAME_PREFIX);
1010 if (!nm) return NULL;
1012 if (!init_string(&name)) {
1013 free(nm);
1014 return NULL;
1017 add_to_string(&name, nm);
1018 free(nm);
1020 extension = get_extension_from_uri(uri);
1021 if (extension) {
1022 add_shell_safe_to_string(&name, extension, strlen(extension));
1023 mem_free(extension);
1026 return name.source;
1030 static unsigned char *
1031 subst_file(unsigned char *prog, unsigned char *file)
1033 struct string name;
1034 /* When there is no %s in the mailcap entry, the handler program reads
1035 * data from stdin instead of a file. */
1036 int input = 1;
1038 if (!init_string(&name)) return NULL;
1040 while (*prog) {
1041 int p;
1043 for (p = 0; prog[p] && prog[p] != '%'; p++);
1045 add_bytes_to_string(&name, prog, p);
1046 prog += p;
1048 if (*prog == '%') {
1049 input = 0;
1050 #if defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)
1051 #ifdef MAX_PATH
1052 unsigned char new_path[MAX_PATH];
1053 #else
1054 unsigned char new_path[1024];
1055 #endif
1057 cygwin_conv_to_full_win32_path(file, new_path);
1058 add_to_string(&name, new_path);
1059 #else
1060 add_shell_quoted_to_string(&name, file, strlen(file));
1061 #endif
1062 prog++;
1066 if (input) {
1067 struct string s;
1069 if (init_string(&s)) {
1070 add_to_string(&s, "/bin/cat ");
1071 add_shell_quoted_to_string(&s, file, strlen(file));
1072 add_to_string(&s, " | ");
1073 add_string_to_string(&s, &name);
1074 done_string(&name);
1075 return s.source;
1078 return name.source;
1083 /*! common_download() passes this function as a ::cdf_callback_T to
1084 * create_download_file().
1086 * @relates cmdw_hop */
1087 static void
1088 common_download_do(struct terminal *term, int fd, void *data,
1089 enum download_flags flags)
1091 struct file_download *file_download;
1092 struct cmdw_hop *cmdw_hop = data;
1093 struct uri *download_uri = cmdw_hop->download_uri;
1094 unsigned char *file = cmdw_hop->real_file;
1095 struct session *ses = cmdw_hop->ses;
1096 struct stat buf;
1098 mem_free(cmdw_hop);
1100 if (!file || fstat(fd, &buf)) goto finish;
1102 file_download = init_file_download(download_uri, ses, file, fd);
1103 if (!file_download) goto finish;
1104 /* If init_file_download succeeds, it takes ownership of file
1105 * and fd. */
1106 file = NULL;
1107 fd = -1;
1109 if (flags & DOWNLOAD_RESUME_SELECTED)
1110 file_download->seek = buf.st_size;
1112 display_download(ses->tab->term, file_download, ses);
1114 load_uri(file_download->uri, ses->referrer, &file_download->download,
1115 PRI_DOWNLOAD, CACHE_MODE_NORMAL, file_download->seek);
1117 finish:
1118 mem_free_if(file);
1119 if (fd != -1) close(fd);
1120 done_uri(download_uri);
1123 /** Begin or resume downloading from session.download_uri to the
1124 * @a file specified by the user.
1126 * This function contains the code shared between start_download() and
1127 * resume_download().
1129 * @relates cmdw_hop */
1130 static void
1131 common_download(struct session *ses, unsigned char *file,
1132 enum download_flags flags)
1134 struct cmdw_hop *cmdw_hop;
1136 if (!ses->download_uri) return;
1138 cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
1139 if (!cmdw_hop) return;
1140 cmdw_hop->ses = ses;
1141 cmdw_hop->download_uri = ses->download_uri;
1142 ses->download_uri = NULL;
1144 kill_downloads_to_file(file);
1146 create_download_file(ses->tab->term, file, &cmdw_hop->real_file,
1147 flags, common_download_do, cmdw_hop);
1150 /** Begin downloading from session.download_uri to the @a file
1151 * specified by the user.
1153 * The ::ACT_MAIN_SAVE_AS, ::ACT_MAIN_SAVE_URL_AS,
1154 * ::ACT_MAIN_LINK_DOWNLOAD, and ::ACT_MAIN_LINK_DOWNLOAD_IMAGE
1155 * actions pass this function as the @c std callback to query_file().
1157 * @relates cmdw_hop */
1158 void
1159 start_download(void *ses, unsigned char *file)
1161 common_download(ses, file,
1162 DOWNLOAD_RESUME_ALLOWED);
1166 /** Resume downloading from session.download_uri to the @a file
1167 * specified by the user.
1169 * The ::ACT_MAIN_LINK_DOWNLOAD_RESUME action passes this function as
1170 * the @c std callback to query_file().
1172 * @relates cmdw_hop */
1173 void
1174 resume_download(void *ses, unsigned char *file)
1176 common_download(ses, file,
1177 DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_RESUME_SELECTED);
1180 /** Resume downloading a file, based on information in struct
1181 * codw_hop. This function actually starts a new download from the
1182 * current end of the file, even though a download from the beginning
1183 * is already in progress at codw_hop->type_query->download. The
1184 * caller will cancel the preexisting download after this function
1185 * returns.
1187 * @relates codw_hop */
1188 static void
1189 transform_codw_to_cmdw(struct terminal *term, int fd,
1190 struct codw_hop *codw_hop,
1191 enum download_flags flags)
1193 struct type_query *type_query = codw_hop->type_query;
1194 struct cmdw_hop *cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
1196 if (!cmdw_hop) {
1197 close(fd);
1198 return;
1201 cmdw_hop->ses = type_query->ses;
1202 cmdw_hop->download_uri = get_uri_reference(type_query->uri);
1203 cmdw_hop->real_file = codw_hop->real_file;
1204 codw_hop->real_file = NULL;
1206 common_download_do(term, fd, cmdw_hop, flags);
1209 /*! continue_download() passes this function as a ::cdf_callback_T to
1210 * create_download_file().
1212 * @relates codw_hop */
1213 static void
1214 continue_download_do(struct terminal *term, int fd, void *data,
1215 enum download_flags flags)
1217 struct codw_hop *codw_hop = data;
1218 struct file_download *file_download = NULL;
1219 struct type_query *type_query;
1221 assert(codw_hop);
1222 assert(codw_hop->type_query);
1223 assert(codw_hop->type_query->uri);
1224 assert(codw_hop->type_query->ses);
1226 type_query = codw_hop->type_query;
1227 if (!codw_hop->real_file) goto cancel;
1229 if (flags & DOWNLOAD_RESUME_SELECTED) {
1230 transform_codw_to_cmdw(term, fd, codw_hop, flags);
1231 fd = -1; /* ownership transfer */
1232 goto cancel;
1235 file_download = init_file_download(type_query->uri, type_query->ses,
1236 codw_hop->real_file, fd);
1237 if (!file_download) goto cancel;
1238 /* If init_file_download succeeds, it takes ownership of
1239 * codw_hop->real_file and fd. */
1240 codw_hop->real_file = NULL;
1241 fd = -1;
1243 if (type_query->external_handler) {
1244 file_download->external_handler = subst_file(type_query->external_handler,
1245 codw_hop->file);
1246 file_download->delete = 1;
1247 file_download->copiousoutput = type_query->copiousoutput;
1248 mem_free(codw_hop->file);
1249 mem_free_set(&type_query->external_handler, NULL);
1252 file_download->block = !!type_query->block;
1254 /* Done here and not in init_file_download() so that the external
1255 * handler can become initialized. */
1256 display_download(term, file_download, type_query->ses);
1258 move_download(&type_query->download, &file_download->download, PRI_DOWNLOAD);
1259 done_type_query(type_query);
1261 mem_free(codw_hop);
1262 return;
1264 cancel:
1265 mem_free_if(codw_hop->real_file);
1266 if (fd != -1) close(fd);
1267 if (type_query->external_handler) mem_free_if(codw_hop->file);
1268 tp_cancel(type_query);
1269 mem_free(codw_hop);
1272 /** When asked what to do with a file, the user chose to download it
1273 * to a local file named @a file.
1274 * Or an external handler was selected, in which case
1275 * type_query.external_handler is non-NULL and @a file does not
1276 * matter because this function will generate a name.
1278 * tp_save() passes this function as the @c std callback to query_file().
1280 * @relates codw_hop */
1281 static void
1282 continue_download(void *data, unsigned char *file)
1284 struct type_query *type_query = data;
1285 struct codw_hop *codw_hop = mem_calloc(1, sizeof(*codw_hop));
1287 if (!codw_hop) {
1288 tp_cancel(type_query);
1289 return;
1292 if (type_query->external_handler) {
1293 /* FIXME: get_temp_name() calls tempnam(). --Zas */
1294 file = get_temp_name(type_query->uri);
1295 if (!file) {
1296 mem_free(codw_hop);
1297 tp_cancel(type_query);
1298 return;
1302 codw_hop->type_query = type_query;
1303 codw_hop->file = file;
1305 kill_downloads_to_file(file);
1307 create_download_file(type_query->ses->tab->term, file,
1308 &codw_hop->real_file,
1309 type_query->external_handler
1310 ? DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_EXTERNAL
1311 : DOWNLOAD_RESUME_ALLOWED,
1312 continue_download_do, codw_hop);
1316 /*! @relates type_query */
1317 static struct type_query *
1318 find_type_query(struct session *ses)
1320 struct type_query *type_query;
1322 foreach (type_query, ses->type_queries)
1323 if (compare_uri(type_query->uri, ses->loading_uri, 0))
1324 return type_query;
1326 return NULL;
1329 /** Prepare to ask the user what to do with a file, but don't display
1330 * the window yet. To display it, do_type_query() must be called
1331 * separately. setup_download_handler() takes care of that.
1333 * @relates type_query */
1334 static struct type_query *
1335 init_type_query(struct session *ses, struct download *download,
1336 struct cache_entry *cached)
1338 struct type_query *type_query;
1340 type_query = mem_calloc(1, sizeof(*type_query));
1341 if (!type_query) return NULL;
1343 type_query->uri = get_uri_reference(ses->loading_uri);
1344 type_query->ses = ses;
1345 type_query->target_frame = null_or_stracpy(ses->task.target.frame);
1347 type_query->cached = cached;
1348 type_query->cgi = cached->cgi;
1349 object_lock(type_query->cached);
1351 move_download(download, &type_query->download, PRI_MAIN);
1352 download->state = connection_state(S_OK);
1354 add_to_list(ses->type_queries, type_query);
1356 return type_query;
1359 /** Cancel any download started for @a type_query, remove the structure
1360 * from the session.type_queries list, and free it.
1362 * @relates type_query */
1363 void
1364 done_type_query(struct type_query *type_query)
1366 /* Unregister any active download */
1367 cancel_download(&type_query->download, 0);
1369 object_unlock(type_query->cached);
1370 done_uri(type_query->uri);
1371 mem_free_if(type_query->external_handler);
1372 mem_free_if(type_query->target_frame);
1373 del_from_list(type_query);
1374 mem_free(type_query);
1378 /** The user chose "Cancel" when asked what to do with a file,
1379 * or the type query was cancelled for some other reason.
1381 * do_type_query() and bittorrent_query_callback() pass this function
1382 * as a ::done_handler_T to add_dlg_ok_button(), and tp_save() passes
1383 * this function as a @c cancel callback to query_file().
1385 * @relates type_query */
1386 void
1387 tp_cancel(void *data)
1389 struct type_query *type_query = data;
1391 /* XXX: Should we really abort? (1 vs 0 as the last param) --pasky */
1392 cancel_download(&type_query->download, 1);
1393 done_type_query(type_query);
1397 /** The user chose "Save" when asked what to do with a file.
1398 * Now ask her where to save the file.
1400 * do_type_query() and bittorrent_query_callback() pass this function
1401 * as a ::done_handler_T to add_dlg_ok_button().
1403 * @relates type_query */
1404 void
1405 tp_save(struct type_query *type_query)
1407 mem_free_set(&type_query->external_handler, NULL);
1408 query_file(type_query->ses, type_query->uri, type_query, continue_download, tp_cancel, 1);
1411 /** The user chose "Show header" when asked what to do with a file.
1413 * do_type_query() passes this function as a ::widget_handler_T to
1414 * add_dlg_button(). Unlike with add_dlg_ok_button(), pressing this
1415 * button does not close the dialog box. This way, the user can
1416 * first examine the header and then choose what to do.
1418 * @relates type_query */
1419 static widget_handler_status_T
1420 tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
1422 struct type_query *type_query = widget_data->widget->data;
1424 cached_header_dialog(type_query->ses, type_query->cached);
1426 return EVENT_PROCESSED;
1430 /** The user chose "Display" when asked what to do with a file,
1431 * or she chose "Open" and there is no external handler.
1433 * do_type_query() and bittorrent_query_callback() pass this function
1434 * as a ::done_handler_T to add_dlg_ok_button().
1436 * @bug FIXME: We need to modify this function to take frame data
1437 * instead, as we want to use this function for frames as well (now,
1438 * when frame has content type text/plain, it is ignored and displayed
1439 * as HTML).
1441 * @relates type_query */
1442 void
1443 tp_display(struct type_query *type_query)
1445 struct view_state *vs;
1446 struct session *ses = type_query->ses;
1447 struct uri *loading_uri = ses->loading_uri;
1448 unsigned char *target_frame = null_or_stracpy(ses->task.target.frame);
1450 ses->loading_uri = type_query->uri;
1451 mem_free_set(&ses->task.target.frame, null_or_stracpy(type_query->target_frame));
1452 vs = ses_forward(ses, /* type_query->frame */ 0);
1453 if (vs) vs->plain = 1;
1454 ses->loading_uri = loading_uri;
1455 mem_free_set(&ses->task.target.frame, target_frame);
1457 if (/* !type_query->frame */ 1) {
1458 struct download *old = &type_query->download;
1459 struct download *new = &cur_loc(ses)->download;
1461 new->callback = (download_callback_T *) doc_loading_callback;
1462 new->data = ses;
1464 move_download(old, new, PRI_MAIN);
1467 display_timer(ses);
1468 done_type_query(type_query);
1471 /** The user chose "Open" when asked what to do with a file.
1472 * Or an external handler was found and it has been configured
1473 * to run without asking.
1475 * do_type_query() passes this function as a ::done_handler_T to
1476 * add_dlg_ok_button().
1478 * @relates type_query */
1479 static void
1480 tp_open(struct type_query *type_query)
1482 if (!type_query->external_handler || !*type_query->external_handler) {
1483 tp_display(type_query);
1484 return;
1487 if (type_query->uri->protocol == PROTOCOL_FILE && !type_query->cgi) {
1488 unsigned char *file = get_uri_string(type_query->uri, URI_PATH);
1489 unsigned char *handler = NULL;
1491 if (file) {
1492 decode_uri(file);
1493 handler = subst_file(type_query->external_handler, file);
1494 mem_free(file);
1497 if (handler) {
1498 if (type_query->copiousoutput) {
1499 exec_later(type_query->ses, handler, NULL);
1500 } else {
1501 exec_on_terminal(type_query->ses->tab->term,
1502 handler, "", type_query->block ?
1503 TERM_EXEC_FG : TERM_EXEC_BG);
1505 mem_free(handler);
1508 done_type_query(type_query);
1509 return;
1512 continue_download(type_query, "");
1516 /*! Ask the user what to do with a file.
1518 * This function does not support BitTorrent downloads.
1519 * For those, query_bittorrent_dialog() must be called instead.
1520 * setup_download_handler() takes care of this.
1522 * @relates type_query */
1523 static void
1524 do_type_query(struct type_query *type_query, unsigned char *ct, struct mime_handler *handler)
1526 /* [gettext_accelerator_context(.do_type_query)] */
1527 struct string filename;
1528 unsigned char *description;
1529 unsigned char *desc_sep;
1530 unsigned char *format, *text, *title;
1531 struct dialog *dlg;
1532 #define TYPE_QUERY_WIDGETS_COUNT 8
1533 int widgets = TYPE_QUERY_WIDGETS_COUNT;
1534 struct terminal *term = type_query->ses->tab->term;
1535 struct memory_list *ml;
1536 struct dialog_data *dlg_data;
1537 int selected_widget;
1539 mem_free_set(&type_query->external_handler, NULL);
1541 if (handler) {
1542 type_query->block = handler->block;
1543 type_query->copiousoutput = handler->copiousoutput;
1544 if (!handler->ask) {
1545 type_query->external_handler = stracpy(handler->program);
1546 tp_open(type_query);
1547 return;
1550 /* Start preparing for the type query dialog. */
1551 description = handler->description;
1552 desc_sep = *description ? "; " : "";
1553 title = N_("What to do?");
1555 } else {
1556 title = N_("Unknown type");
1557 description = "";
1558 desc_sep = "";
1561 dlg = calloc_dialog(TYPE_QUERY_WIDGETS_COUNT, MAX_STR_LEN * 2);
1562 if (!dlg) return;
1564 if (init_string(&filename)) {
1565 add_mime_filename_to_string(&filename, type_query->uri);
1567 /* Let's make the filename pretty for display & save */
1568 /* TODO: The filename can be the empty string here. See bug 396. */
1569 #ifdef CONFIG_UTF8
1570 if (term->utf8_cp)
1571 decode_uri_string(&filename);
1572 else
1573 #endif /* CONFIG_UTF8 */
1574 decode_uri_string_for_display(&filename);
1577 text = get_dialog_offset(dlg, TYPE_QUERY_WIDGETS_COUNT);
1578 /* For "default directory index pages" with wrong content-type
1579 * the filename can be NULL, e.g. http://www.spamhaus.org in bug 396. */
1580 if (filename.length) {
1581 format = _("What would you like to do with the file '%s' (type: %s%s%s)?", term);
1582 snprintf(text, MAX_STR_LEN, format, filename.source, ct, desc_sep, description);
1583 } else {
1584 format = _("What would you like to do with the file (type: %s%s%s)?", term);
1585 snprintf(text, MAX_STR_LEN, format, ct, desc_sep, description);
1588 done_string(&filename);
1590 dlg->title = _(title, term);
1591 dlg->layouter = generic_dialog_layouter;
1592 dlg->layout.padding_top = 1;
1593 dlg->layout.fit_datalen = 1;
1594 dlg->udata2 = type_query;
1596 add_dlg_text(dlg, text, ALIGN_LEFT, 0);
1598 /* Add input field or text widget with info about the program handler. */
1599 if (!get_cmd_opt_bool("anonymous")) {
1600 unsigned char *field = mem_calloc(1, MAX_STR_LEN);
1602 if (!field) {
1603 mem_free(dlg);
1604 return;
1607 if (handler && handler->program) {
1608 safe_strncpy(field, handler->program, MAX_STR_LEN);
1611 /* xgettext:no-c-format */
1612 add_dlg_field(dlg, _("Program ('%' will be replaced by the filename)", term),
1613 0, 0, NULL, MAX_STR_LEN, field, NULL);
1614 type_query->external_handler = field;
1616 add_dlg_checkbox(dlg, _("Block the terminal", term), &type_query->block);
1617 selected_widget = 3;
1619 } else if (handler) {
1620 unsigned char *field = text + MAX_STR_LEN;
1622 format = _("The file will be opened with the program '%s'.", term);
1623 snprintf(field, MAX_STR_LEN, format, handler->program);
1624 add_dlg_text(dlg, field, ALIGN_LEFT, 0);
1626 type_query->external_handler = stracpy(handler->program);
1627 if (!type_query->external_handler) {
1628 mem_free(dlg);
1629 return;
1632 widgets--;
1633 selected_widget = 2;
1635 } else {
1636 widgets -= 2;
1637 selected_widget = 1;
1640 /* Add buttons if they are both usable and allowed. */
1642 if (!get_cmd_opt_bool("anonymous") || handler) {
1643 add_dlg_ok_button(dlg, _("~Open", term), B_ENTER,
1644 (done_handler_T *) tp_open, type_query);
1645 } else {
1646 widgets--;
1649 if (!get_cmd_opt_bool("anonymous")) {
1650 add_dlg_ok_button(dlg, _("Sa~ve", term), B_ENTER,
1651 (done_handler_T *) tp_save, type_query);
1652 } else {
1653 widgets--;
1656 add_dlg_ok_button(dlg, _("~Display", term), B_ENTER,
1657 (done_handler_T *) tp_display, type_query);
1659 if (type_query->cached && type_query->cached->head) {
1660 add_dlg_button(dlg, _("Show ~header", term), B_ENTER,
1661 tp_show_header, type_query);
1662 } else {
1663 widgets--;
1666 add_dlg_ok_button(dlg, _("~Cancel", term), B_ESC,
1667 (done_handler_T *) tp_cancel, type_query);
1669 add_dlg_end(dlg, widgets);
1671 ml = getml(dlg, (void *) NULL);
1672 if (!ml) {
1673 /* XXX: Assume that the allocated @external_handler will be
1674 * freed when releasing the @type_query. */
1675 mem_free(dlg);
1676 return;
1679 dlg_data = do_dialog(term, dlg, ml);
1680 /* Don't focus the text field; we want the user to be able
1681 * to select a button by typing the first letter of its label
1682 * without having to first leave the text field. */
1683 if (dlg_data) {
1684 select_widget_by_id(dlg_data, selected_widget);
1688 struct {
1689 unsigned char *type;
1690 unsigned int plain:1;
1691 } static const known_types[] = {
1692 { "text/html", 0 },
1693 { "text/plain", 1 },
1694 { "application/xhtml+xml", 0 }, /* RFC 3236 */
1695 #if CONFIG_DOM
1696 { "application/docbook+xml", 1 },
1697 { "application/rss+xml", 0 },
1698 { "application/xbel+xml", 1 },
1699 { "application/xbel", 1 },
1700 { "application/x-xbel", 1 },
1701 #endif
1702 { NULL, 1 },
1705 /*! @relates type_query */
1707 setup_download_handler(struct session *ses, struct download *loading,
1708 struct cache_entry *cached, int frame)
1710 struct mime_handler *handler;
1711 struct view_state *vs;
1712 struct type_query *type_query;
1713 unsigned char *ctype = get_content_type(cached);
1714 int plaintext = 1;
1715 int ret = 0;
1716 int xwin, i;
1718 if (!ctype || !*ctype)
1719 goto plaintext_follow;
1721 for (i = 0; known_types[i].type; i++) {
1722 if (c_strcasecmp(ctype, known_types[i].type))
1723 continue;
1725 plaintext = known_types[i].plain;
1726 goto plaintext_follow;
1729 xwin = ses->tab->term->environment & ENV_XWIN;
1730 handler = get_mime_type_handler(ctype, xwin);
1732 if (!handler && strlen(ctype) >= 4 && !c_strncasecmp(ctype, "text", 4))
1733 goto plaintext_follow;
1735 type_query = find_type_query(ses);
1736 if (type_query) {
1737 ret = 1;
1738 } else {
1739 type_query = init_type_query(ses, loading, cached);
1740 if (type_query) {
1741 ret = 1;
1742 #ifdef CONFIG_BITTORRENT
1743 /* A terrible waste of a good MIME handler here, but we want
1744 * to use the type_query this is easier. */
1745 if ((!c_strcasecmp(ctype, "application/x-bittorrent")
1746 || !c_strcasecmp(ctype, "application/x-torrent"))
1747 && !get_cmd_opt_bool("anonymous"))
1748 query_bittorrent_dialog(type_query);
1749 else
1750 #endif
1751 do_type_query(type_query, ctype, handler);
1755 mem_free_if(handler);
1757 return ret;
1759 plaintext_follow:
1760 vs = ses_forward(ses, frame);
1761 if (vs) vs->plain = plaintext;
1762 return 0;