1 /** Downloads managment
12 #ifdef HAVE_SYS_CYGWIN_H
13 #include <sys/cygwin.h>
15 #include <sys/types.h>
17 #include <fcntl.h> /* OS/2 needs this after sys/types.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
);
68 download_is_progressing(struct download
*download
)
71 && is_in_state(download
->state
, S_TRANS
)
72 && has_progress(download
->progress
);
76 are_there_downloads(void)
78 struct file_download
*file_download
;
80 foreach (file_download
, downloads
)
81 if (!file_download
->external_handler
)
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
);
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
;
129 abort_download(struct file_download
*file_download
)
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
));
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
);
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
))
173 file_download
= file_download
->prev
;
174 abort_download(file_download
->next
);
180 abort_all_downloads(void)
182 while (!list_empty(downloads
))
183 abort_download(downloads
.next
);
188 destroy_downloads(struct session
*ses
)
190 struct file_download
*file_download
, *next
;
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
)
201 foreach (file_download
, downloads
) {
202 if (file_download
->ses
!= ses
)
205 file_download
->ses
= s
;
211 foreachsafe (file_download
, next
, downloads
) {
212 if (file_download
->ses
!= ses
)
215 if (!file_download
->external_handler
) {
216 file_download
->ses
= NULL
;
220 abort_download(file_download
);
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
)
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
;
244 abort_download(file_download
);
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
;
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
));
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
);
279 foreach (frag
, cached
->frag
) {
280 off_t remain
= file_download
->seek
- frag
->offset
;
281 int *h
= &file_download
->handle
;
284 if (remain
< 0 || frag
->length
<= remain
)
287 #ifdef USE_OPEN_PREALLOC
288 if (!file_download
->seek
289 && (!file_download
->download
.progress
290 || file_download
->download
.progress
->size
> 0)) {
292 *h
= open_prealloc(file_download
->file
,
293 O_CREAT
|O_WRONLY
|O_TRUNC
,
295 file_download
->download
.progress
296 ? file_download
->download
.progress
->size
299 download_error_dialog(file_download
, errno
);
306 w
= safe_write(*h
, frag
->data
+ remain
, frag
->length
- remain
);
308 download_error_dialog(file_download
, errno
);
312 file_download
->seek
+= w
;
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) {
326 abort_download(file_download
);
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
;
338 /* No term here, so no beep. --Zas */
339 abort_download(file_download
);
343 if (is_in_progress_state(download
->state
)) {
344 if (file_download
->dlg_data
)
345 redraw_dialog(file_download
->dlg_data
, 1);
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
);
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
)));
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
,
371 file_download
->block
? TERM_EXEC_FG
: TERM_EXEC_BG
);
372 file_download
->delete = 0;
373 abort_download_and_beep(file_download
, term
);
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
385 done_download_display(file_download
);
388 info_box(term
, MSGBOX_FREE_TEXT
,
389 N_("Download"), ALIGN_CENTER
,
390 msg_text(term
, N_("Download complete:\n%s"), url
));
395 if (file_download
->remotetime
396 && get_opt_bool("document.download.set_original_time")) {
399 foo
.actime
= foo
.modtime
= file_download
->remotetime
;
400 utime(file_download
->file
, &foo
);
403 abort_download_and_beep(file_download
, term
);
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
);
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);
440 if (!write_cache_entry_to_file(cached
, file_download
)) {
441 detach_connection(download
, file_download
->seek
);
442 abort_download(file_download
);
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().
454 * The terminal on which the callback should display any windows.
455 * Comes directly from the @a term argument of lookup_unique_name().
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).
463 * A pointer to any data that the callback cares about.
464 * Comes directly from the @a data argument of lookup_unique_name().
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(). */
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
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(). */
495 /** This function will be called when the user answers. */
496 lun_callback_T
*callback
;
498 /** A pointer to be passed to #callback. */
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()
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()
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
;
542 /** Data saved by create_download_file() for the create_download_file_do()
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. */
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
565 * @relates lun_hop */
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
,
573 mem_free_if(lun_hop
->ofile
);
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
582 * @relates lun_hop */
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
,
590 mem_free_if(lun_hop
->ofile
);
591 mem_free_if(lun_hop
->file
);
595 /** The use chose "Overwrite the original file" when asked where to
598 * lookup_unique_name() passes this function as a ::done_handler_T to
601 * @relates lun_hop */
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
,
609 mem_free_if(lun_hop
->file
);
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
619 * @relates lun_hop */
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
);
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.
638 * The terminal in which this function should show its UI.
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.
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.
655 * Will be called when the user answers, or right away if the question
656 * need not or cannot be asked.
659 * A pointer to be passed to @a callback.
661 * @relates lun_hop */
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
;
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
);
683 /* !overwrite means always silently overwrite, which may be admitelly
684 * indeed a little confusing ;-) */
685 overwrite
= get_opt_int("document.download.overwrite");
687 /* Nothing special to do... */
688 callback(term
, ofile
, data
, flags
);
692 /* Check if file is a directory, and use a default name if it's the
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."),
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
);
712 /* overwrite == 2 (ask) and file != ofile (=> original file already
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"
729 "The alternative filename is:\n"
731 empty_string_or_(lun_hop
->ofile
),
732 empty_string_or_(file
)),
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")
740 MSG_BOX_BUTTON(N_("~Cancel"), lun_cancel
, B_ESC
));
741 if (!dialog_data
) goto error
;
745 mem_free_if(lun_hop
);
746 if (file
!= ofile
) mem_free_if(file
);
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 */
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
;
769 #ifdef NO_FILE_SECURITY
772 int sf
= !!(flags
& DOWNLOAD_EXTERNAL
);
775 if (!file
) goto finish
;
780 /* Create parent directories if needed. */
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.
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),
790 saved_errno
= errno
; /* Saved in case of ... --Zas */
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
)));
809 if (!(flags
& DOWNLOAD_EXTERNAL
)) {
810 unsigned char *download_dir
= get_opt_str("document.download.directory");
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
]))
819 download_dir
[i
+ 1] = 0;
823 if (cdf_hop
->real_file
)
824 *cdf_hop
->real_file
= file
;
829 cdf_hop
->callback(term
, h
, cdf_hop
->data
, flags
);
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.
838 * If any dialog boxes are needed, show them in this terminal.
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.
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().
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.
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.
864 * A pointer to be passed to @a callback.
866 * @relates cdf_hop */
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
));
877 callback(term
, -1, data
, flags
& ~DOWNLOAD_RESUME_SELECTED
);
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 */
889 /* Also the tilde will be expanded here. */
890 lookup_unique_name(term
, fi
, flags
, create_download_file_do
, cdf_hop
);
899 static unsigned char *
900 get_temp_name(struct uri
*uri
)
903 unsigned char *extension
;
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
)) {
917 add_to_string(&name
, nm
);
920 extension
= get_extension_from_uri(uri
);
922 add_shell_safe_to_string(&name
, extension
, strlen(extension
));
930 static unsigned char *
931 subst_file(unsigned char *prog
, unsigned char *file
)
934 /* When there is no %s in the mailcap entry, the handler program reads
935 * data from stdin instead of a file. */
938 if (!init_string(&name
)) return NULL
;
943 for (p
= 0; prog
[p
] && prog
[p
] != '%'; p
++);
945 add_bytes_to_string(&name
, prog
, p
);
950 #if defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)
952 unsigned char new_path
[MAX_PATH
];
954 unsigned char new_path
[1024];
957 cygwin_conv_to_full_win32_path(file
, new_path
);
958 add_to_string(&name
, new_path
);
960 add_shell_quoted_to_string(&name
, file
, strlen(file
));
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
);
983 /*! common_download() passes this function as a ::cdf_callback_T to
984 * create_download_file().
986 * @relates cmdw_hop */
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
;
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
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
);
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 */
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 */
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 */
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
1087 * @relates codw_hop */
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
));
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 */
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
;
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 */
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
;
1143 if (type_query
->external_handler
) {
1144 file_download
->external_handler
= subst_file(type_query
->external_handler
,
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
);
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
);
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 */
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
));
1187 tp_cancel(type_query
);
1191 if (type_query
->external_handler
) {
1192 /* FIXME: get_temp_name() calls tempnam(). --Zas */
1193 file
= get_temp_name(type_query
->uri
);
1196 tp_cancel(type_query
);
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))
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
);
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 */
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 */
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 */
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
1332 * @relates type_query */
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
;
1355 move_download(old
, new, PRI_MAIN
);
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 */
1371 tp_open(struct type_query
*type_query
)
1373 if (!type_query
->external_handler
|| !*type_query
->external_handler
) {
1374 tp_display(type_query
);
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
;
1384 handler
= subst_file(type_query
->external_handler
, file
);
1389 exec_on_terminal(type_query
->ses
->tab
->term
,
1391 type_query
->block
? TERM_EXEC_FG
: TERM_EXEC_BG
);
1395 done_type_query(type_query
);
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 */
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
;
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
);
1429 type_query
->block
= handler
->block
;
1430 if (!handler
->ask
) {
1431 type_query
->external_handler
= stracpy(handler
->program
);
1432 tp_open(type_query
);
1436 /* Start preparing for the type query dialog. */
1437 description
= handler
->description
;
1438 desc_sep
= *description
? "; " : "";
1439 title
= N_("What to do?");
1442 title
= N_("Unknown type");
1447 dlg
= calloc_dialog(TYPE_QUERY_WIDGETS_COUNT
, MAX_STR_LEN
* 2);
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. */
1457 decode_uri_string(&filename
);
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
);
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
);
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
) {
1519 selected_widget
= 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
);
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
);
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
);
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
);
1559 /* XXX: Assume that the allocated @external_handler will be
1560 * freed when releasing the @type_query. */
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. */
1570 select_widget_by_id(dlg_data
, selected_widget
);
1575 unsigned char *type
;
1576 unsigned int plain
:1;
1577 } static const known_types
[] = {
1579 { "text/plain", 1 },
1580 { "application/xhtml+xml", 0 }, /* RFC 3236 */
1582 { "application/docbook+xml", 1 },
1583 { "application/rss+xml", 0 },
1584 { "application/xbel+xml", 1 },
1585 { "application/xbel", 1 },
1586 { "application/x-xbel", 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
);
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
))
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
);
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
);
1633 do_type_query(type_query
, ctype
, handler
);
1636 mem_free_if(handler
);
1641 vs
= ses_forward(ses
, frame
);
1642 if (vs
) vs
->plain
= plaintext
;