download: Add DOWNLOAD_EXTERNAL flag
[elinks.git] / src / session / download.c
blob389a3616e1b2f1d0205ec9f1ac4c786fde340b83
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 "mime/mime.h"
36 #include "network/connection.h"
37 #include "network/progress.h"
38 #include "network/state.h"
39 #include "osdep/osdep.h"
40 #include "protocol/bittorrent/dialogs.h"
41 #include "protocol/date.h"
42 #include "protocol/protocol.h"
43 #include "protocol/uri.h"
44 #include "session/download.h"
45 #include "session/history.h"
46 #include "session/location.h"
47 #include "session/session.h"
48 #include "session/task.h"
49 #include "terminal/draw.h"
50 #include "terminal/screen.h"
51 #include "terminal/terminal.h"
52 #include "util/conv.h"
53 #include "util/error.h"
54 #include "util/file.h"
55 #include "util/lists.h"
56 #include "util/memlist.h"
57 #include "util/memory.h"
58 #include "util/string.h"
59 #include "util/time.h"
62 /* TODO: tp_*() should be in separate file, I guess? --pasky */
65 INIT_LIST_OF(struct file_download, downloads);
67 int
68 download_is_progressing(struct download *download)
70 return download
71 && is_in_state(download->state, S_TRANS)
72 && has_progress(download->progress);
75 int
76 are_there_downloads(void)
78 struct file_download *file_download;
80 foreach (file_download, downloads)
81 if (!file_download->external_handler)
82 return 1;
84 return 0;
88 static void download_data(struct download *download, struct file_download *file_download);
90 /*! @note If this fails, the caller is responsible of freeing @a file
91 * and closing @a fd. */
92 struct file_download *
93 init_file_download(struct uri *uri, struct session *ses, unsigned char *file, int fd)
95 struct file_download *file_download;
97 file_download = mem_calloc(1, sizeof(*file_download));
98 if (!file_download) return NULL;
100 /* Actually we could allow fragments in the URI and just change all the
101 * places that compares and shows the URI, but for now it is much
102 * easier this way. */
103 file_download->uri = get_composed_uri(uri, URI_BASE);
104 if (!file_download->uri) {
105 mem_free(file_download);
106 return NULL;
109 init_download_display(file_download);
111 file_download->file = file;
112 file_download->handle = fd;
114 file_download->download.callback = (download_callback_T *) download_data;
115 file_download->download.data = file_download;
116 file_download->ses = ses;
117 /* The tab may be closed, but we will still want to ie. open the
118 * handler on that terminal. */
119 file_download->term = ses->tab->term;
121 object_nolock(file_download, "file_download"); /* Debugging purpose. */
122 add_to_list(downloads, file_download);
124 return file_download;
128 void
129 abort_download(struct file_download *file_download)
131 #if 0
132 /* When hacking to cleanup the download code, remove lots of duplicated
133 * code and implement stuff from bug 435 we should reintroduce this
134 * assertion. Currently it will trigger often and shows that the
135 * download dialog code potentially could access free()d memory. */
136 assert(!is_object_used(file_download));
137 #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->notify >= 2) {
323 beep_terminal(term);
326 abort_download(file_download);
329 static void
330 download_data_store(struct download *download, struct file_download *file_download)
332 struct terminal *term = file_download->term;
334 assert_terminal_ptr_not_dangling(term);
335 if_assert_failed term = file_download->term = NULL;
337 if (!term) {
338 /* No term here, so no beep. --Zas */
339 abort_download(file_download);
340 return;
343 if (is_in_progress_state(download->state)) {
344 if (file_download->dlg_data)
345 redraw_dialog(file_download->dlg_data, 1);
346 return;
349 if (!is_in_state(download->state, S_OK)) {
350 unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);
351 struct connection_state state = download->state;
353 abort_download_and_beep(file_download, term);
355 if (!url) return;
357 info_box(term, MSGBOX_FREE_TEXT,
358 N_("Download error"), ALIGN_CENTER,
359 msg_text(term, N_("Error downloading %s:\n\n%s"),
360 url, get_state_message(state, term)));
361 mem_free(url);
362 return;
365 if (file_download->external_handler) {
366 prealloc_truncate(file_download->handle, file_download->seek);
367 close(file_download->handle);
368 file_download->handle = -1;
369 exec_on_terminal(term, file_download->external_handler,
370 file_download->file,
371 file_download->block ? TERM_EXEC_FG : TERM_EXEC_BG);
372 file_download->delete = 0;
373 abort_download_and_beep(file_download, term);
374 return;
377 if (file_download->notify) {
378 unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);
380 /* This is apparently a little racy. Deleting the box item will
381 * update the download browser _after_ the notification dialog
382 * has been drawn whereby it will be hidden. This should make
383 * the download browser update before launcing any
384 * notification. */
385 done_download_display(file_download);
387 if (url) {
388 info_box(term, MSGBOX_FREE_TEXT,
389 N_("Download"), ALIGN_CENTER,
390 msg_text(term, N_("Download complete:\n%s"), url));
391 mem_free(url);
395 if (file_download->remotetime
396 && get_opt_bool("document.download.set_original_time")) {
397 struct utimbuf foo;
399 foo.actime = foo.modtime = file_download->remotetime;
400 utime(file_download->file, &foo);
403 abort_download_and_beep(file_download, term);
406 static void
407 download_data(struct download *download, struct file_download *file_download)
409 struct cache_entry *cached = download->cached;
411 if (!cached || is_in_queued_state(download->state)) {
412 download_data_store(download, file_download);
413 return;
416 if (cached->last_modified)
417 file_download->remotetime = parse_date(&cached->last_modified, NULL, 0, 1);
419 if (cached->redirect && file_download->redirect_cnt++ < MAX_REDIRECTS) {
420 cancel_download(&file_download->download, 0);
422 assertm(compare_uri(cached->uri, file_download->uri, 0),
423 "Redirecting using bad base URI");
425 done_uri(file_download->uri);
427 file_download->uri = get_uri_reference(cached->redirect);
428 file_download->download.state = connection_state(S_WAIT_REDIR);
430 if (file_download->dlg_data)
431 redraw_dialog(file_download->dlg_data, 1);
433 load_uri(file_download->uri, cached->uri, &file_download->download,
434 PRI_DOWNLOAD, CACHE_MODE_NORMAL,
435 download->progress ? download->progress->start : 0);
437 return;
440 if (!write_cache_entry_to_file(cached, file_download)) {
441 detach_connection(download, file_download->seek);
442 abort_download(file_download);
443 return;
446 detach_connection(download, file_download->seek);
447 download_data_store(download, file_download);
450 /** Type of the callback function that will be called when the user
451 * answers the question posed by lookup_unique_name().
453 * @param term
454 * The terminal on which the callback should display any windows.
455 * Comes directly from the @a term argument of lookup_unique_name().
457 * @param file
458 * The name of the local file to which the data should be downloaded,
459 * or NULL if the download should not begin. The callback is
460 * responsible of doing mem_free(@a file).
462 * @param data
463 * A pointer to any data that the callback cares about.
464 * Comes directly from the @a data argument of lookup_unique_name().
466 * @param flags
467 * The same as the @a flags argument of create_download_file(),
468 * except the ::DOWNLOAD_RESUME_SELECTED bit will be changed to match
469 * what the user chose.
471 * @relates lun_hop */
472 typedef void lun_callback_T(struct terminal *term, unsigned char *file,
473 void *data, enum download_flags flags);
475 /** The user is being asked what to do when the local file for
476 * the download already exists. This structure is allocated by
477 * lookup_unique_name() and freed by each lun_* function:
478 * lun_alternate(), lun_cancel(), lun_overwrite(), and lun_resume(). */
479 struct lun_hop {
480 /** The terminal in which ELinks is asking the question.
481 * This gets passed to #callback. */
482 struct terminal *term;
484 /** The name of the local file into which the data was
485 * originally going to be downloaded, but which already
486 * exists. In this string, "~" has already been expanded
487 * to the home directory. The string must be freed with
488 * mem_free(). */
489 unsigned char *ofile;
491 /** An alternative file name that the user may choose instead
492 * of #ofile. The string must be freed with mem_free(). */
493 unsigned char *file;
495 /** This function will be called when the user answers. */
496 lun_callback_T *callback;
498 /** A pointer to be passed to #callback. */
499 void *data;
501 /** Saved flags to be passed to #callback.
502 * If the user chooses to resume, then lun_resume() sets
503 * ::DOWNLOAD_RESUME_SELECTED when it calls #callback.
505 * @invariant The ::DOWNLOAD_RESUME_SELECTED bit should be
506 * clear here because otherwise there would have been no
507 * reason to ask the user and initialize this structure. */
508 enum download_flags flags;
511 /** Data saved by common_download() for the common_download_do()
512 * callback. */
513 struct cmdw_hop {
514 struct session *ses;
516 /** The URI from which the data will be downloaded. */
517 struct uri *download_uri;
519 /** The name of the local file to which the data will be
520 * downloaded. This is initially NULL, but its address is
521 * given to create_download_file(), which arranges for the
522 * pointer to be set before common_download_do() is called.
523 * The string must be freed with mem_free(). */
524 unsigned char *real_file;
527 /** Data saved by continue_download() for the continue_download_do()
528 * callback. */
529 struct codw_hop {
530 struct type_query *type_query;
532 /** The name of the local file to which the data will be
533 * downloaded. This is initially NULL, but its address is
534 * given to create_download_file(), which arranges for the
535 * pointer to be set before continue_download_do() is called.
536 * The string must be freed with mem_free(). */
537 unsigned char *real_file;
539 unsigned char *file;
542 /** Data saved by create_download_file() for the create_download_file_do()
543 * callback. */
544 struct cdf_hop {
545 /** Where to save the name of the file that was actually
546 * opened. One of the arguments of #callback is a file
547 * descriptor for this file. @c real_file can be NULL if
548 * #callback does not care about the name. */
549 unsigned char **real_file;
551 /** This function will be called when the file has been opened,
552 * or when it is known that the file will not be opened. */
553 cdf_callback_T *callback;
555 /** A pointer to be passed to #callback. */
556 void *data;
559 /** The use chose "Save under the alternative name" when asked where
560 * to download a file.
562 * lookup_unique_name() passes this function as a ::done_handler_T to
563 * msg_box().
565 * @relates lun_hop */
566 static void
567 lun_alternate(void *lun_hop_)
569 struct lun_hop *lun_hop = lun_hop_;
571 lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data,
572 lun_hop->flags);
573 mem_free_if(lun_hop->ofile);
574 mem_free(lun_hop);
577 /** The use chose "Cancel" when asked where to download a file.
579 * lookup_unique_name() passes this function as a ::done_handler_T to
580 * msg_box().
582 * @relates lun_hop */
583 static void
584 lun_cancel(void *lun_hop_)
586 struct lun_hop *lun_hop = lun_hop_;
588 lun_hop->callback(lun_hop->term, NULL, lun_hop->data,
589 lun_hop->flags);
590 mem_free_if(lun_hop->ofile);
591 mem_free_if(lun_hop->file);
592 mem_free(lun_hop);
595 /** The use chose "Overwrite the original file" when asked where to
596 * download a file.
598 * lookup_unique_name() passes this function as a ::done_handler_T to
599 * msg_box().
601 * @relates lun_hop */
602 static void
603 lun_overwrite(void *lun_hop_)
605 struct lun_hop *lun_hop = lun_hop_;
607 lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data,
608 lun_hop->flags);
609 mem_free_if(lun_hop->file);
610 mem_free(lun_hop);
613 /** The user chose "Resume download of the original file" when asked
614 * where to download a file.
616 * lookup_unique_name() passes this function as a ::done_handler_T to
617 * msg_box().
619 * @relates lun_hop */
620 static void
621 lun_resume(void *lun_hop_)
623 struct lun_hop *lun_hop = lun_hop_;
625 lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data,
626 lun_hop->flags | DOWNLOAD_RESUME_SELECTED);
627 mem_free_if(lun_hop->file);
628 mem_free(lun_hop);
632 /** If attempting to download to an existing file, perhaps ask
633 * the user whether to resume, overwrite, or save elsewhere.
634 * This function constructs a struct lun_hop, which will be freed
635 * when the user answers the question.
637 * @param term
638 * The terminal in which this function should show its UI.
640 * @param[in] ofile
641 * A proposed name for the local file to which the data would be
642 * downloaded. "~" here refers to the home directory.
643 * lookup_unique_name() treats this original string as read-only.
645 * @param[in] flags
646 * Flags controlling how to download the file.
647 * ::DOWNLOAD_RESUME_ALLOWED adds a "Resume" button to the dialog.
648 * ::DOWNLOAD_RESUME_SELECTED means the user already chose to resume
649 * downloading (with ::ACT_MAIN_LINK_DOWNLOAD_RESUME), before ELinks
650 * even asked for the file name; thus don't ask whether to overwrite.
651 * Other flags, such as ::DOWNLOAD_EXTERNAL, have no effect at this
652 * level but they get passed to @a callback.
654 * @param callback
655 * Will be called when the user answers, or right away if the question
656 * need not or cannot be asked.
658 * @param data
659 * A pointer to be passed to @a callback.
661 * @relates lun_hop */
662 static void
663 lookup_unique_name(struct terminal *term, unsigned char *ofile,
664 enum download_flags flags,
665 lun_callback_T *callback, void *data)
667 /* [gettext_accelerator_context(.lookup_unique_name)] */
668 struct lun_hop *lun_hop = NULL;
669 unsigned char *file = NULL;
670 struct dialog_data *dialog_data;
671 int overwrite;
673 ofile = expand_tilde(ofile);
674 if (!ofile) goto error;
676 /* Minor code duplication to prevent useless call to get_opt_int()
677 * if possible. --Zas */
678 if (flags & DOWNLOAD_RESUME_SELECTED) {
679 callback(term, ofile, data, flags);
680 return;
683 /* !overwrite means always silently overwrite, which may be admitelly
684 * indeed a little confusing ;-) */
685 overwrite = get_opt_int("document.download.overwrite");
686 if (!overwrite) {
687 /* Nothing special to do... */
688 callback(term, ofile, data, flags);
689 return;
692 /* Check if file is a directory, and use a default name if it's the
693 * case. */
694 if (file_is_dir(ofile)) {
695 info_box(term, MSGBOX_FREE_TEXT,
696 N_("Download error"), ALIGN_CENTER,
697 msg_text(term, N_("'%s' is a directory."),
698 ofile));
699 goto error;
702 /* Check if the file already exists (file != ofile). */
703 file = get_unique_name(ofile);
705 if (!file || overwrite == 1 || file == ofile) {
706 /* Still nothing special to do... */
707 if (file != ofile) mem_free(ofile);
708 callback(term, file, data, flags & ~DOWNLOAD_RESUME_SELECTED);
709 return;
712 /* overwrite == 2 (ask) and file != ofile (=> original file already
713 * exists) */
715 lun_hop = mem_calloc(1, sizeof(*lun_hop));
716 if (!lun_hop) goto error;
717 lun_hop->term = term;
718 lun_hop->ofile = ofile;
719 lun_hop->file = file; /* file != ofile verified above */
720 lun_hop->callback = callback;
721 lun_hop->data = data;
722 lun_hop->flags = flags;
724 dialog_data = msg_box(
725 term, NULL, MSGBOX_FREE_TEXT,
726 N_("File exists"), ALIGN_CENTER,
727 msg_text(term, N_("This file already exists:\n"
728 "%s\n\n"
729 "The alternative filename is:\n"
730 "%s"),
731 empty_string_or_(lun_hop->ofile),
732 empty_string_or_(file)),
733 lun_hop, 4,
734 MSG_BOX_BUTTON(N_("Sa~ve under the alternative name"), lun_alternate, B_ENTER),
735 MSG_BOX_BUTTON(N_("~Overwrite the original file"), lun_overwrite, 0),
736 MSG_BOX_BUTTON((flags & DOWNLOAD_RESUME_ALLOWED
737 ? N_("~Resume download of the original file")
738 : NULL),
739 lun_resume, 0),
740 MSG_BOX_BUTTON(N_("~Cancel"), lun_cancel, B_ESC));
741 if (!dialog_data) goto error;
742 return;
744 error:
745 mem_free_if(lun_hop);
746 if (file != ofile) mem_free_if(file);
747 mem_free_if(ofile);
748 callback(term, NULL, data, flags & ~DOWNLOAD_RESUME_SELECTED);
753 /** Now that the final name of the download file has been chosen,
754 * open the file and call the ::cdf_callback_T that was originally
755 * given to create_download_file().
757 * create_download_file() passes this function as a ::lun_callback_T
758 * to lookup_unique_name().
760 * @relates cdf_hop */
761 static void
762 create_download_file_do(struct terminal *term, unsigned char *file,
763 void *data, enum download_flags flags)
765 struct cdf_hop *cdf_hop = data;
766 unsigned char *wd;
767 int h = -1;
768 int saved_errno;
769 #ifdef NO_FILE_SECURITY
770 int sf = 0;
771 #else
772 int sf = !!(flags & DOWNLOAD_EXTERNAL);
773 #endif
775 if (!file) goto finish;
777 wd = get_cwd();
778 set_cwd(term->cwd);
780 /* Create parent directories if needed. */
781 mkalldirs(file);
783 /* O_APPEND means repositioning at the end of file before each write(),
784 * thus ignoring seek()s and that can hide mysterious bugs. IMHO.
785 * --pasky */
786 h = open(file, O_CREAT | O_WRONLY
787 | (flags & DOWNLOAD_RESUME_SELECTED ? 0 : O_TRUNC)
788 | (sf && !(flags & DOWNLOAD_RESUME_SELECTED) ? O_EXCL : 0),
789 sf ? 0600 : 0666);
790 saved_errno = errno; /* Saved in case of ... --Zas */
792 if (wd) {
793 set_cwd(wd);
794 mem_free(wd);
797 if (h == -1) {
798 info_box(term, MSGBOX_FREE_TEXT,
799 N_("Download error"), ALIGN_CENTER,
800 msg_text(term, N_("Could not create file '%s':\n%s"),
801 file, strerror(saved_errno)));
803 mem_free(file);
804 goto finish;
806 } else {
807 set_bin(h);
809 if (!(flags & DOWNLOAD_EXTERNAL)) {
810 unsigned char *download_dir = get_opt_str("document.download.directory");
811 int i;
813 safe_strncpy(download_dir, file, MAX_STR_LEN);
815 /* Find the used directory so it's available in history */
816 for (i = strlen(download_dir); i >= 0; i--)
817 if (dir_sep(download_dir[i]))
818 break;
819 download_dir[i + 1] = 0;
823 if (cdf_hop->real_file)
824 *cdf_hop->real_file = file;
825 else
826 mem_free(file);
828 finish:
829 cdf_hop->callback(term, h, cdf_hop->data, flags);
830 mem_free(cdf_hop);
833 /** Create a file to which data can be downloaded.
834 * This function constructs a struct cdf_hop that will be freed
835 * when @a callback returns.
837 * @param term
838 * If any dialog boxes are needed, show them in this terminal.
840 * @param fi
841 * A proposed name for the local file to which the data would be
842 * downloaded. "~" here refers to the home directory.
843 * create_download_file() treats this original string as read-only.
845 * @param real_file
846 * If non-NULL, prepare to save in *@a real_file the name of the local
847 * file that was eventually opened. @a callback must then arrange for
848 * this string to be freed with mem_free().
850 * @param flags
851 * Flags controlling how to download the file.
852 * ::DOWNLOAD_RESUME_ALLOWED adds a "Resume" button to the dialog.
853 * ::DOWNLOAD_RESUME_SELECTED skips the dialog entirely.
854 * ::DOWNLOAD_EXTERNAL causes the file to be created with settings
855 * suitable for a temporary file: give only the user herself access to
856 * the file (even if the umask is looser), and create the file with
857 * @c O_EXCL unless resuming.
859 * @param callback
860 * This function will be called when the file has been opened,
861 * or when it is known that the file will not be opened.
863 * @param data
864 * A pointer to be passed to @a callback.
866 * @relates cdf_hop */
867 void
868 create_download_file(struct terminal *term, unsigned char *fi,
869 unsigned char **real_file,
870 enum download_flags flags,
871 cdf_callback_T *callback, void *data)
873 struct cdf_hop *cdf_hop = mem_calloc(1, sizeof(*cdf_hop));
874 unsigned char *wd;
876 if (!cdf_hop) {
877 callback(term, -1, data, flags & ~DOWNLOAD_RESUME_SELECTED);
878 return;
881 cdf_hop->real_file = real_file;
882 cdf_hop->callback = callback;
883 cdf_hop->data = data;
885 /* FIXME: The wd bussiness is probably useless here? --pasky */
886 wd = get_cwd();
887 set_cwd(term->cwd);
889 /* Also the tilde will be expanded here. */
890 lookup_unique_name(term, fi, flags, create_download_file_do, cdf_hop);
892 if (wd) {
893 set_cwd(wd);
894 mem_free(wd);
899 static unsigned char *
900 get_temp_name(struct uri *uri)
902 struct string name;
903 unsigned char *extension;
904 /* FIXME
905 * We use tempnam() here, which is unsafe (race condition), for now.
906 * This should be changed at some time, but it needs an in-depth work
907 * of whole download code. --Zas */
908 unsigned char *nm = tempnam(NULL, ELINKS_TEMPNAME_PREFIX);
910 if (!nm) return NULL;
912 if (!init_string(&name)) {
913 free(nm);
914 return NULL;
917 add_to_string(&name, nm);
918 free(nm);
920 extension = get_extension_from_uri(uri);
921 if (extension) {
922 add_shell_safe_to_string(&name, extension, strlen(extension));
923 mem_free(extension);
926 return name.source;
930 static unsigned char *
931 subst_file(unsigned char *prog, unsigned char *file)
933 struct string name;
934 /* When there is no %s in the mailcap entry, the handler program reads
935 * data from stdin instead of a file. */
936 int input = 1;
938 if (!init_string(&name)) return NULL;
940 while (*prog) {
941 int p;
943 for (p = 0; prog[p] && prog[p] != '%'; p++);
945 add_bytes_to_string(&name, prog, p);
946 prog += p;
948 if (*prog == '%') {
949 input = 0;
950 #if defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)
951 #ifdef MAX_PATH
952 unsigned char new_path[MAX_PATH];
953 #else
954 unsigned char new_path[1024];
955 #endif
957 cygwin_conv_to_full_win32_path(file, new_path);
958 add_to_string(&name, new_path);
959 #else
960 add_shell_quoted_to_string(&name, file, strlen(file));
961 #endif
962 prog++;
966 if (input) {
967 struct string s;
969 if (init_string(&s)) {
970 add_to_string(&s, "/bin/cat ");
971 add_shell_quoted_to_string(&s, file, strlen(file));
972 add_to_string(&s, " | ");
973 add_string_to_string(&s, &name);
974 done_string(&name);
975 return s.source;
978 return name.source;
983 /*! common_download() passes this function as a ::cdf_callback_T to
984 * create_download_file().
986 * @relates cmdw_hop */
987 static void
988 common_download_do(struct terminal *term, int fd, void *data,
989 enum download_flags flags)
991 struct file_download *file_download;
992 struct cmdw_hop *cmdw_hop = data;
993 struct uri *download_uri = cmdw_hop->download_uri;
994 unsigned char *file = cmdw_hop->real_file;
995 struct session *ses = cmdw_hop->ses;
996 struct stat buf;
998 mem_free(cmdw_hop);
1000 if (!file || fstat(fd, &buf)) goto finish;
1002 file_download = init_file_download(download_uri, ses, file, fd);
1003 if (!file_download) goto finish;
1004 /* If init_file_download succeeds, it takes ownership of file
1005 * and fd. */
1006 file = NULL;
1007 fd = -1;
1009 if (flags & DOWNLOAD_RESUME_SELECTED)
1010 file_download->seek = buf.st_size;
1012 display_download(ses->tab->term, file_download, ses);
1014 load_uri(file_download->uri, ses->referrer, &file_download->download,
1015 PRI_DOWNLOAD, CACHE_MODE_NORMAL, file_download->seek);
1017 finish:
1018 mem_free_if(file);
1019 if (fd != -1) close(fd);
1020 done_uri(download_uri);
1023 /** Begin or resume downloading from session.download_uri to the
1024 * @a file specified by the user.
1026 * This function contains the code shared between start_download() and
1027 * resume_download().
1029 * @relates cmdw_hop */
1030 static void
1031 common_download(struct session *ses, unsigned char *file,
1032 enum download_flags flags)
1034 struct cmdw_hop *cmdw_hop;
1036 if (!ses->download_uri) return;
1038 cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
1039 if (!cmdw_hop) return;
1040 cmdw_hop->ses = ses;
1041 cmdw_hop->download_uri = ses->download_uri;
1042 ses->download_uri = NULL;
1044 kill_downloads_to_file(file);
1046 create_download_file(ses->tab->term, file, &cmdw_hop->real_file,
1047 flags, common_download_do, cmdw_hop);
1050 /** Begin downloading from session.download_uri to the @a file
1051 * specified by the user.
1053 * The ::ACT_MAIN_SAVE_AS, ::ACT_MAIN_SAVE_URL_AS,
1054 * ::ACT_MAIN_LINK_DOWNLOAD, and ::ACT_MAIN_LINK_DOWNLOAD_IMAGE
1055 * actions pass this function as the @c std callback to query_file().
1057 * @relates cmdw_hop */
1058 void
1059 start_download(void *ses, unsigned char *file)
1061 common_download(ses, file,
1062 DOWNLOAD_RESUME_ALLOWED);
1066 /** Resume downloading from session.download_uri to the @a file
1067 * specified by the user.
1069 * The ::ACT_MAIN_LINK_DOWNLOAD_RESUME action passes this function as
1070 * the @c std callback to query_file().
1072 * @relates cmdw_hop */
1073 void
1074 resume_download(void *ses, unsigned char *file)
1076 common_download(ses, file,
1077 DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_RESUME_SELECTED);
1080 /** Resume downloading a file, based on information in struct
1081 * codw_hop. This function actually starts a new download from the
1082 * current end of the file, even though a download from the beginning
1083 * is already in progress at codw_hop->type_query->download. The
1084 * caller will cancel the preexisting download after this function
1085 * returns.
1087 * @relates codw_hop */
1088 static void
1089 transform_codw_to_cmdw(struct terminal *term, int fd,
1090 struct codw_hop *codw_hop,
1091 enum download_flags flags)
1093 struct type_query *type_query = codw_hop->type_query;
1094 struct cmdw_hop *cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop));
1096 if (!cmdw_hop) {
1097 close(fd);
1098 return;
1101 cmdw_hop->ses = type_query->ses;
1102 cmdw_hop->download_uri = get_uri_reference(type_query->uri);
1103 cmdw_hop->real_file = codw_hop->real_file;
1104 codw_hop->real_file = NULL;
1106 common_download_do(term, fd, cmdw_hop, flags);
1109 /*! continue_download() passes this function as a ::cdf_callback_T to
1110 * create_download_file().
1112 * @relates codw_hop */
1113 static void
1114 continue_download_do(struct terminal *term, int fd, void *data,
1115 enum download_flags flags)
1117 struct codw_hop *codw_hop = data;
1118 struct file_download *file_download = NULL;
1119 struct type_query *type_query;
1121 assert(codw_hop);
1122 assert(codw_hop->type_query);
1123 assert(codw_hop->type_query->uri);
1124 assert(codw_hop->type_query->ses);
1126 type_query = codw_hop->type_query;
1127 if (!codw_hop->real_file) goto cancel;
1129 if (flags & DOWNLOAD_RESUME_SELECTED) {
1130 transform_codw_to_cmdw(term, fd, codw_hop, flags);
1131 fd = -1; /* ownership transfer */
1132 goto cancel;
1135 file_download = init_file_download(type_query->uri, type_query->ses,
1136 codw_hop->real_file, fd);
1137 if (!file_download) goto cancel;
1138 /* If init_file_download succeeds, it takes ownership of
1139 * codw_hop->real_file and fd. */
1140 codw_hop->real_file = NULL;
1141 fd = -1;
1143 if (type_query->external_handler) {
1144 file_download->external_handler = subst_file(type_query->external_handler,
1145 codw_hop->file);
1146 file_download->delete = 1;
1147 mem_free(codw_hop->file);
1148 mem_free_set(&type_query->external_handler, NULL);
1151 file_download->block = !!type_query->block;
1153 /* Done here and not in init_file_download() so that the external
1154 * handler can become initialized. */
1155 display_download(term, file_download, type_query->ses);
1157 move_download(&type_query->download, &file_download->download, PRI_DOWNLOAD);
1158 done_type_query(type_query);
1160 mem_free(codw_hop);
1161 return;
1163 cancel:
1164 mem_free_if(codw_hop->real_file);
1165 if (fd != -1) close(fd);
1166 if (type_query->external_handler) mem_free_if(codw_hop->file);
1167 tp_cancel(type_query);
1168 mem_free(codw_hop);
1171 /** When asked what to do with a file, the user chose to download it
1172 * to a local file named @a file.
1173 * Or an external handler was selected, in which case
1174 * type_query.external_handler is non-NULL and @a file does not
1175 * matter because this function will generate a name.
1177 * tp_save() passes this function as the @c std callback to query_file().
1179 * @relates codw_hop */
1180 static void
1181 continue_download(void *data, unsigned char *file)
1183 struct type_query *type_query = data;
1184 struct codw_hop *codw_hop = mem_calloc(1, sizeof(*codw_hop));
1186 if (!codw_hop) {
1187 tp_cancel(type_query);
1188 return;
1191 if (type_query->external_handler) {
1192 /* FIXME: get_temp_name() calls tempnam(). --Zas */
1193 file = get_temp_name(type_query->uri);
1194 if (!file) {
1195 mem_free(codw_hop);
1196 tp_cancel(type_query);
1197 return;
1201 codw_hop->type_query = type_query;
1202 codw_hop->file = file;
1204 kill_downloads_to_file(file);
1206 create_download_file(type_query->ses->tab->term, file,
1207 &codw_hop->real_file,
1208 type_query->external_handler
1209 ? DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_EXTERNAL
1210 : DOWNLOAD_RESUME_ALLOWED,
1211 continue_download_do, codw_hop);
1215 /** Prepare to ask the user what to do with a file, but don't display
1216 * the window yet. To display it, do_type_query() must be called
1217 * separately. setup_download_handler() takes care of that.
1219 * @relates type_query */
1220 static struct type_query *
1221 init_type_query(struct session *ses, struct download *download,
1222 struct cache_entry *cached)
1224 struct type_query *type_query;
1226 /* There can be only one ... */
1227 foreach (type_query, ses->type_queries)
1228 if (compare_uri(type_query->uri, ses->loading_uri, 0))
1229 return NULL;
1231 type_query = mem_calloc(1, sizeof(*type_query));
1232 if (!type_query) return NULL;
1234 type_query->uri = get_uri_reference(ses->loading_uri);
1235 type_query->ses = ses;
1236 type_query->target_frame = null_or_stracpy(ses->task.target.frame);
1238 type_query->cached = cached;
1239 type_query->cgi = cached->cgi;
1240 object_lock(type_query->cached);
1242 move_download(download, &type_query->download, PRI_MAIN);
1243 download->state = connection_state(S_OK);
1245 add_to_list(ses->type_queries, type_query);
1247 return type_query;
1250 /** Cancel any download started for @a type_query, remove the structure
1251 * from the session.type_queries list, and free it.
1253 * @relates type_query */
1254 void
1255 done_type_query(struct type_query *type_query)
1257 /* Unregister any active download */
1258 cancel_download(&type_query->download, 0);
1260 object_unlock(type_query->cached);
1261 done_uri(type_query->uri);
1262 mem_free_if(type_query->external_handler);
1263 mem_free_if(type_query->target_frame);
1264 del_from_list(type_query);
1265 mem_free(type_query);
1269 /** The user chose "Cancel" when asked what to do with a file,
1270 * or the type query was cancelled for some other reason.
1272 * do_type_query() and bittorrent_query_callback() pass this function
1273 * as a ::done_handler_T to add_dlg_ok_button(), and tp_save() passes
1274 * this function as a @c cancel callback to query_file().
1276 * @relates type_query */
1277 void
1278 tp_cancel(void *data)
1280 struct type_query *type_query = data;
1282 /* XXX: Should we really abort? (1 vs 0 as the last param) --pasky */
1283 cancel_download(&type_query->download, 1);
1284 done_type_query(type_query);
1288 /** The user chose "Save" when asked what to do with a file.
1289 * Now ask her where to save the file.
1291 * do_type_query() and bittorrent_query_callback() pass this function
1292 * as a ::done_handler_T to add_dlg_ok_button().
1294 * @relates type_query */
1295 void
1296 tp_save(struct type_query *type_query)
1298 mem_free_set(&type_query->external_handler, NULL);
1299 query_file(type_query->ses, type_query->uri, type_query, continue_download, tp_cancel, 1);
1302 /** The user chose "Show header" when asked what to do with a file.
1304 * do_type_query() passes this function as a ::widget_handler_T to
1305 * add_dlg_button(). Unlike with add_dlg_ok_button(), pressing this
1306 * button does not close the dialog box. This way, the user can
1307 * first examine the header and then choose what to do.
1309 * @relates type_query */
1310 static widget_handler_status_T
1311 tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
1313 struct type_query *type_query = widget_data->widget->data;
1315 cached_header_dialog(type_query->ses, type_query->cached);
1317 return EVENT_PROCESSED;
1321 /** The user chose "Display" when asked what to do with a file,
1322 * or she chose "Open" and there is no external handler.
1324 * do_type_query() and bittorrent_query_callback() pass this function
1325 * as a ::done_handler_T to add_dlg_ok_button().
1327 * @bug FIXME: We need to modify this function to take frame data
1328 * instead, as we want to use this function for frames as well (now,
1329 * when frame has content type text/plain, it is ignored and displayed
1330 * as HTML).
1332 * @relates type_query */
1333 void
1334 tp_display(struct type_query *type_query)
1336 struct view_state *vs;
1337 struct session *ses = type_query->ses;
1338 struct uri *loading_uri = ses->loading_uri;
1339 unsigned char *target_frame = ses->task.target.frame;
1341 ses->loading_uri = type_query->uri;
1342 ses->task.target.frame = type_query->target_frame;
1343 vs = ses_forward(ses, /* type_query->frame */ 0);
1344 if (vs) vs->plain = 1;
1345 ses->loading_uri = loading_uri;
1346 ses->task.target.frame = target_frame;
1348 if (/* !type_query->frame */ 1) {
1349 struct download *old = &type_query->download;
1350 struct download *new = &cur_loc(ses)->download;
1352 new->callback = (download_callback_T *) doc_loading_callback;
1353 new->data = ses;
1355 move_download(old, new, PRI_MAIN);
1358 display_timer(ses);
1359 done_type_query(type_query);
1362 /** The user chose "Open" when asked what to do with a file.
1363 * Or an external handler was found and it has been configured
1364 * to run without asking.
1366 * do_type_query() passes this function as a ::done_handler_T to
1367 * add_dlg_ok_button().
1369 * @relates type_query */
1370 static void
1371 tp_open(struct type_query *type_query)
1373 if (!type_query->external_handler || !*type_query->external_handler) {
1374 tp_display(type_query);
1375 return;
1378 if (type_query->uri->protocol == PROTOCOL_FILE && !type_query->cgi) {
1379 unsigned char *file = get_uri_string(type_query->uri, URI_PATH);
1380 unsigned char *handler = NULL;
1382 if (file) {
1383 decode_uri(file);
1384 handler = subst_file(type_query->external_handler, file);
1385 mem_free(file);
1388 if (handler) {
1389 exec_on_terminal(type_query->ses->tab->term,
1390 handler, "",
1391 type_query->block ? TERM_EXEC_FG : TERM_EXEC_BG);
1392 mem_free(handler);
1395 done_type_query(type_query);
1396 return;
1399 continue_download(type_query, "");
1403 /*! Ask the user what to do with a file.
1405 * This function does not support BitTorrent downloads.
1406 * For those, query_bittorrent_dialog() must be called instead.
1407 * setup_download_handler() takes care of this.
1409 * @relates type_query */
1410 static void
1411 do_type_query(struct type_query *type_query, unsigned char *ct, struct mime_handler *handler)
1413 /* [gettext_accelerator_context(.do_type_query)] */
1414 struct string filename;
1415 unsigned char *description;
1416 unsigned char *desc_sep;
1417 unsigned char *format, *text, *title;
1418 struct dialog *dlg;
1419 #define TYPE_QUERY_WIDGETS_COUNT 8
1420 int widgets = TYPE_QUERY_WIDGETS_COUNT;
1421 struct terminal *term = type_query->ses->tab->term;
1422 struct memory_list *ml;
1423 struct dialog_data *dlg_data;
1424 int selected_widget;
1426 mem_free_set(&type_query->external_handler, NULL);
1428 if (handler) {
1429 type_query->block = handler->block;
1430 if (!handler->ask) {
1431 type_query->external_handler = stracpy(handler->program);
1432 tp_open(type_query);
1433 return;
1436 /* Start preparing for the type query dialog. */
1437 description = handler->description;
1438 desc_sep = *description ? "; " : "";
1439 title = N_("What to do?");
1441 } else {
1442 title = N_("Unknown type");
1443 description = "";
1444 desc_sep = "";
1447 dlg = calloc_dialog(TYPE_QUERY_WIDGETS_COUNT, MAX_STR_LEN * 2);
1448 if (!dlg) return;
1450 if (init_string(&filename)) {
1451 add_mime_filename_to_string(&filename, type_query->uri);
1453 /* Let's make the filename pretty for display & save */
1454 /* TODO: The filename can be the empty string here. See bug 396. */
1455 #ifdef CONFIG_UTF8
1456 if (term->utf8_cp)
1457 decode_uri_string(&filename);
1458 else
1459 #endif /* CONFIG_UTF8 */
1460 decode_uri_string_for_display(&filename);
1463 text = get_dialog_offset(dlg, TYPE_QUERY_WIDGETS_COUNT);
1464 /* For "default directory index pages" with wrong content-type
1465 * the filename can be NULL, e.g. http://www.spamhaus.org in bug 396. */
1466 if (filename.length) {
1467 format = _("What would you like to do with the file '%s' (type: %s%s%s)?", term);
1468 snprintf(text, MAX_STR_LEN, format, filename.source, ct, desc_sep, description);
1469 } else {
1470 format = _("What would you like to do with the file (type: %s%s%s)?", term);
1471 snprintf(text, MAX_STR_LEN, format, ct, desc_sep, description);
1474 done_string(&filename);
1476 dlg->title = _(title, term);
1477 dlg->layouter = generic_dialog_layouter;
1478 dlg->layout.padding_top = 1;
1479 dlg->layout.fit_datalen = 1;
1480 dlg->udata2 = type_query;
1482 add_dlg_text(dlg, text, ALIGN_LEFT, 0);
1484 /* Add input field or text widget with info about the program handler. */
1485 if (!get_cmd_opt_bool("anonymous")) {
1486 unsigned char *field = mem_calloc(1, MAX_STR_LEN);
1488 if (!field) {
1489 mem_free(dlg);
1490 return;
1493 if (handler && handler->program) {
1494 safe_strncpy(field, handler->program, MAX_STR_LEN);
1497 /* xgettext:no-c-format */
1498 add_dlg_field(dlg, _("Program ('%' will be replaced by the filename)", term),
1499 0, 0, NULL, MAX_STR_LEN, field, NULL);
1500 type_query->external_handler = field;
1502 add_dlg_checkbox(dlg, _("Block the terminal", term), &type_query->block);
1503 selected_widget = 3;
1505 } else if (handler) {
1506 unsigned char *field = text + MAX_STR_LEN;
1508 format = _("The file will be opened with the program '%s'.", term);
1509 snprintf(field, MAX_STR_LEN, format, handler->program);
1510 add_dlg_text(dlg, field, ALIGN_LEFT, 0);
1512 type_query->external_handler = stracpy(handler->program);
1513 if (!type_query->external_handler) {
1514 mem_free(dlg);
1515 return;
1518 widgets--;
1519 selected_widget = 2;
1521 } else {
1522 widgets -= 2;
1523 selected_widget = 1;
1526 /* Add buttons if they are both usable and allowed. */
1528 if (!get_cmd_opt_bool("anonymous") || handler) {
1529 add_dlg_ok_button(dlg, _("~Open", term), B_ENTER,
1530 (done_handler_T *) tp_open, type_query);
1531 } else {
1532 widgets--;
1535 if (!get_cmd_opt_bool("anonymous")) {
1536 add_dlg_ok_button(dlg, _("Sa~ve", term), B_ENTER,
1537 (done_handler_T *) tp_save, type_query);
1538 } else {
1539 widgets--;
1542 add_dlg_ok_button(dlg, _("~Display", term), B_ENTER,
1543 (done_handler_T *) tp_display, type_query);
1545 if (type_query->cached && type_query->cached->head) {
1546 add_dlg_button(dlg, _("Show ~header", term), B_ENTER,
1547 tp_show_header, type_query);
1548 } else {
1549 widgets--;
1552 add_dlg_ok_button(dlg, _("~Cancel", term), B_ESC,
1553 (done_handler_T *) tp_cancel, type_query);
1555 add_dlg_end(dlg, widgets);
1557 ml = getml(dlg, (void *) NULL);
1558 if (!ml) {
1559 /* XXX: Assume that the allocated @external_handler will be
1560 * freed when releasing the @type_query. */
1561 mem_free(dlg);
1562 return;
1565 dlg_data = do_dialog(term, dlg, ml);
1566 /* Don't focus the text field; we want the user to be able
1567 * to select a button by typing the first letter of its label
1568 * without having to first leave the text field. */
1569 if (dlg_data) {
1570 select_widget_by_id(dlg_data, selected_widget);
1574 struct {
1575 unsigned char *type;
1576 unsigned int plain:1;
1577 } static const known_types[] = {
1578 { "text/html", 0 },
1579 { "text/plain", 1 },
1580 { "application/xhtml+xml", 0 }, /* RFC 3236 */
1581 #if CONFIG_DOM
1582 { "application/docbook+xml", 1 },
1583 { "application/rss+xml", 0 },
1584 { "application/xbel+xml", 1 },
1585 { "application/xbel", 1 },
1586 { "application/x-xbel", 1 },
1587 #endif
1588 { NULL, 1 },
1591 /*! @relates type_query */
1593 setup_download_handler(struct session *ses, struct download *loading,
1594 struct cache_entry *cached, int frame)
1596 struct mime_handler *handler;
1597 struct view_state *vs;
1598 struct type_query *type_query;
1599 unsigned char *ctype = get_content_type(cached);
1600 int plaintext = 1;
1601 int ret = 0;
1602 int xwin, i;
1604 if (!ctype || !*ctype)
1605 goto plaintext_follow;
1607 for (i = 0; known_types[i].type; i++) {
1608 if (c_strcasecmp(ctype, known_types[i].type))
1609 continue;
1611 plaintext = known_types[i].plain;
1612 goto plaintext_follow;
1615 xwin = ses->tab->term->environment & ENV_XWIN;
1616 handler = get_mime_type_handler(ctype, xwin);
1618 if (!handler && strlen(ctype) >= 4 && !c_strncasecmp(ctype, "text", 4))
1619 goto plaintext_follow;
1621 type_query = init_type_query(ses, loading, cached);
1622 if (type_query) {
1623 ret = 1;
1624 #ifdef CONFIG_BITTORRENT
1625 /* A terrible waste of a good MIME handler here, but we want
1626 * to use the type_query this is easier. */
1627 if ((!c_strcasecmp(ctype, "application/x-bittorrent")
1628 || !c_strcasecmp(ctype, "application/x-torrent"))
1629 && !get_cmd_opt_bool("anonymous"))
1630 query_bittorrent_dialog(type_query);
1631 else
1632 #endif
1633 do_type_query(type_query, ctype, handler);
1636 mem_free_if(handler);
1638 return ret;
1640 plaintext_follow:
1641 vs = ses_forward(ses, frame);
1642 if (vs) vs->plain = plaintext;
1643 return 0;