iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / dialogs / download.c
blobad65a0e24137318804becbae01b0b075559e5dbb
1 /* Download dialogs */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
11 #include "elinks.h"
13 #include "bfu/dialog.h"
14 #include "bfu/hierbox.h"
15 #include "dialogs/download.h"
16 #include "dialogs/menu.h"
17 #include "dialogs/progress.h"
18 #include "dialogs/status.h"
19 #include "intl/gettext/libintl.h"
20 #include "main/object.h"
21 #include "main/select.h"
22 #include "network/connection.h"
23 #include "network/progress.h"
24 #include "protocol/bittorrent/dialogs.h"
25 #include "protocol/protocol.h"
26 #include "protocol/uri.h"
27 #include "session/download.h"
28 #include "session/session.h"
29 #include "terminal/draw.h"
30 #include "terminal/terminal.h"
31 #include "util/color.h"
32 #include "util/conv.h"
33 #include "util/error.h"
34 #include "util/memlist.h"
35 #include "util/memory.h"
36 #include "util/string.h"
37 #include "util/time.h"
40 static void
41 undisplay_download(struct file_download *file_download)
43 /* We are maybe called from bottom halve so check consistency */
44 if (is_in_downloads_list(file_download) && file_download->dlg_data)
45 cancel_dialog(file_download->dlg_data, NULL);
48 static void
49 do_abort_download(struct file_download *file_download)
51 /* We are maybe called from bottom halve so check consistency */
52 if (is_in_downloads_list(file_download)) {
53 file_download->stop = 1;
54 abort_download(file_download);
58 static widget_handler_status_T
59 dlg_set_notify(struct dialog_data *dlg_data, struct widget_data *widget_data)
61 struct file_download *file_download = dlg_data->dlg->udata;
63 file_download->notify = 1;
64 /* The user of this terminal wants to be notified about the
65 * download. Make this also the terminal where the
66 * notification appears. However, keep the original terminal
67 * for external handlers, because the handler may have been
68 * chosen based on the environment variables (usually TERM or
69 * DISPLAY) of the ELinks process in that terminal. */
70 if (!file_download->external_handler)
71 file_download->term = dlg_data->win->term;
73 #if CONFIG_BITTORRENT
74 if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
75 set_bittorrent_notify_on_completion(&file_download->download,
76 file_download->term);
77 #endif
78 undisplay_download(file_download);
79 return EVENT_PROCESSED;
82 static widget_handler_status_T
83 dlg_abort_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
85 struct file_download *file_download = dlg_data->dlg->udata;
87 object_unlock(file_download);
88 register_bottom_half(do_abort_download, file_download);
89 return EVENT_PROCESSED;
92 static widget_handler_status_T
93 push_delete_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
95 struct file_download *file_download = dlg_data->dlg->udata;
97 file_download->delete = 1;
98 #if CONFIG_BITTORRENT
99 if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
100 set_bittorrent_files_for_deletion(&file_download->download);
101 #endif
102 object_unlock(file_download);
103 register_bottom_half(do_abort_download, file_download);
104 return EVENT_PROCESSED;
107 static widget_handler_status_T
108 dlg_undisplay_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
110 struct file_download *file_download = dlg_data->dlg->udata;
112 object_unlock(file_download);
113 register_bottom_half(undisplay_download, file_download);
114 return EVENT_PROCESSED;
118 static void
119 download_abort_function(struct dialog_data *dlg_data)
121 struct file_download *file_download = dlg_data->dlg->udata;
123 file_download->dlg_data = NULL;
127 static void
128 download_dialog_layouter(struct dialog_data *dlg_data)
130 struct file_download *file_download = dlg_data->dlg->udata;
131 struct terminal *term = dlg_data->win->term;
132 int w = dialog_max_width(term);
133 int rw = w;
134 int x, y = 0;
135 int url_len;
136 unsigned char *url;
137 struct download *download = &file_download->download;
138 struct color_pair *dialog_text_color = get_bfu_color(term, "dialog.text");
139 unsigned char *msg = get_download_msg(download, term, 1, 1, "\n");
140 int show_meter = (download_is_progressing(download)
141 && download->progress->size >= 0);
142 #if CONFIG_BITTORRENT
143 int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT
144 && (show_meter || is_in_state(download->state, S_RESUME)));
145 #endif
147 redraw_windows(REDRAW_BEHIND_WINDOW, dlg_data->win);
148 file_download->dlg_data = dlg_data;
150 if (!msg) return;
152 url = get_uri_string(file_download->uri, URI_PUBLIC);
153 if (!url) {
154 mem_free(msg);
155 return;
157 #ifdef CONFIG_UTF8
158 if (term->utf8_cp)
159 decode_uri(url);
160 else
161 #endif /* CONFIG_UTF8 */
162 decode_uri_for_display(url);
163 url_len = strlen(url);
165 if (show_meter) {
166 int_lower_bound(&w, DOWN_DLG_MIN);
169 dlg_format_text_do(dlg_data, url, 0, &y, w, &rw,
170 dialog_text_color, ALIGN_LEFT, 1);
172 y++;
173 if (show_meter) y += 2;
175 #if CONFIG_BITTORRENT
176 if (bittorrent) y += 2;
177 #endif
178 dlg_format_text_do(dlg_data, msg, 0, &y, w, &rw,
179 dialog_text_color, ALIGN_LEFT, 1);
181 y++;
182 dlg_format_buttons(dlg_data, dlg_data->widgets_data,
183 dlg_data->number_of_widgets, 0, &y, w,
184 &rw, ALIGN_CENTER, 1);
186 draw_dialog(dlg_data, w, y);
188 w = rw;
189 if (url_len > w) {
190 /* Truncate too long urls */
191 url_len = w;
192 url[url_len] = '\0';
193 if (url_len > 4) {
194 url[--url_len] = '.';
195 url[--url_len] = '.';
196 url[--url_len] = '.';
200 y = dlg_data->box.y + DIALOG_TB + 1;
201 x = dlg_data->box.x + DIALOG_LB;
202 dlg_format_text_do(dlg_data, url, x, &y, w, NULL,
203 dialog_text_color, ALIGN_LEFT, 0);
205 if (show_meter) {
206 y++;
207 draw_progress_bar(download->progress, term, x, y, w, NULL, NULL);
208 y++;
211 #if CONFIG_BITTORRENT
212 if (bittorrent) {
213 y++;
214 draw_bittorrent_piece_progress(download, term, x, y, w, NULL, NULL);
215 y++;
217 #endif
218 y++;
219 dlg_format_text_do(dlg_data, msg, x, &y, w, NULL,
220 dialog_text_color, ALIGN_LEFT, 0);
222 y++;
223 dlg_format_buttons(dlg_data, dlg_data->widgets_data,
224 dlg_data->number_of_widgets, x, &y, w,
225 NULL, ALIGN_CENTER, 0);
227 mem_free(url);
228 mem_free(msg);
231 void
232 display_download(struct terminal *term, struct file_download *file_download,
233 struct session *ses)
235 /* [gettext_accelerator_context(display_download)] */
236 struct dialog *dlg;
238 if (!is_in_downloads_list(file_download))
239 return;
241 #if CONFIG_BITTORRENT
242 #define DOWNLOAD_WIDGETS_COUNT 5
243 #else
244 #define DOWNLOAD_WIDGETS_COUNT 4
245 #endif
247 dlg = calloc_dialog(DOWNLOAD_WIDGETS_COUNT, 0);
248 if (!dlg) return;
250 undisplay_download(file_download);
251 file_download->ses = ses;
252 dlg->title = _("Download", term);
253 dlg->layouter = download_dialog_layouter;
254 dlg->abort = download_abort_function;
255 dlg->udata = file_download;
257 object_lock(file_download);
259 add_dlg_button(dlg, _("~Background", term), B_ENTER | B_ESC, dlg_undisplay_download, NULL);
260 add_dlg_button(dlg, _("Background with ~notify", term), B_ENTER | B_ESC, dlg_set_notify, NULL);
262 #if CONFIG_BITTORRENT
263 if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
264 add_dlg_button(dlg, _("~Info", term), B_ENTER | B_ESC, dlg_show_bittorrent_info, NULL);
265 #endif
267 add_dlg_button(dlg, _("~Abort", term), 0, dlg_abort_download, NULL);
269 /* Downloads scheduled to be opened by external handlers are always
270 * deleted. */
271 if (!file_download->external_handler) {
272 add_dlg_button(dlg, _("Abort and ~delete file", term), 0, push_delete_button, NULL);
275 #if CONFIG_BITTORRENT
276 add_dlg_end(dlg, DOWNLOAD_WIDGETS_COUNT - !!file_download->external_handler
277 - (file_download->uri->protocol != PROTOCOL_BITTORRENT));
278 #else
279 add_dlg_end(dlg, DOWNLOAD_WIDGETS_COUNT - !!file_download->external_handler);
280 #endif
282 do_dialog(term, dlg, getml(dlg, (void *) NULL));
286 /* The download manager */
288 static void
289 lock_file_download(struct listbox_item *item)
291 object_lock((struct file_download *) item->udata);
294 static void
295 unlock_file_download(struct listbox_item *item)
297 object_unlock((struct file_download *) item->udata);
300 static int
301 is_file_download_used(struct listbox_item *item)
303 return is_object_used((struct file_download *) item->udata);
306 static unsigned char *
307 get_file_download_text(struct listbox_item *item, struct terminal *term)
309 struct file_download *file_download = item->udata;
310 unsigned char *uristring;
312 uristring = get_uri_string(file_download->uri, URI_PUBLIC);
313 if (uristring) {
314 #ifdef CONFIG_UTF8
315 if (term->utf8_cp)
316 decode_uri(uristring);
317 else
318 #endif /* CONFIG_UTF8 */
319 decode_uri_for_display(uristring);
322 return uristring;
325 static unsigned char *
326 get_file_download_info(struct listbox_item *item, struct terminal *term)
328 return NULL;
331 static struct uri *
332 get_file_download_uri(struct listbox_item *item)
334 struct file_download *file_download = item->udata;
336 return get_uri_reference(file_download->uri);
339 static struct listbox_item *
340 get_file_download_root(struct listbox_item *item)
342 return NULL;
345 static int
346 can_delete_file_download(struct listbox_item *item)
348 return 1;
351 static void
352 delete_file_download(struct listbox_item *item, int last)
354 struct file_download *file_download = item->udata;
356 assert(!is_object_used(file_download));
357 register_bottom_half(do_abort_download, file_download);
360 static enum dlg_refresh_code
361 refresh_file_download(struct dialog_data *dlg_data, void *data)
363 /* Always refresh (until we keep finished downloads) */
364 return are_there_downloads() ? REFRESH_DIALOG : REFRESH_STOP;
367 /* TODO: Make it configurable */
368 #define DOWNLOAD_METER_WIDTH 15
369 #define DOWNLOAD_URI_PERCENTAGE 50
371 static void
372 draw_file_download(struct listbox_item *item, struct listbox_context *context,
373 int x, int y, int width)
375 struct file_download *file_download = item->udata;
376 struct download *download = &file_download->download;
377 unsigned char *stylename;
378 struct color_pair *color;
379 unsigned char *text;
380 int length;
381 int trimmedlen;
382 int meter = DOWNLOAD_METER_WIDTH;
384 /* We have nothing to work with */
385 if (width < 4) return;
387 stylename = (item == context->box->sel) ? "menu.selected"
388 : ((item->marked) ? "menu.marked"
389 : "menu.normal");
391 color = get_bfu_color(context->term, stylename);
393 text = get_file_download_text(item, context->term);
394 if (!text) return;
396 length = strlen(text);
397 /* Show atleast the required percentage of the URI */
398 if (length * DOWNLOAD_URI_PERCENTAGE / 100 < width - meter - 4) {
399 trimmedlen = int_min(length, width - meter - 4);
400 } else {
401 trimmedlen = int_min(length, width - 3);
404 draw_text(context->term, x, y, text, trimmedlen, 0, color);
405 if (trimmedlen < length) {
406 draw_text(context->term, x + trimmedlen, y, "...", 3, 0, color);
407 trimmedlen += 3;
410 mem_free(text);
412 if (!download->progress
413 || download->progress->size < 0
414 || !is_in_state(download->state, S_TRANS)
415 || !has_progress(download->progress)) {
416 /* TODO: Show trimmed error message. */
417 return;
420 if (!dialog_has_refresh(context->dlg_data))
421 refresh_dialog(context->dlg_data, refresh_file_download, NULL);
423 if (trimmedlen + meter >= width) return;
425 x += width - meter;
427 draw_progress_bar(download->progress, context->term, x, y, meter, NULL, NULL);
430 static struct listbox_ops_messages download_messages = {
431 /* cant_delete_item */
432 N_("Sorry, but download \"%s\" cannot be interrupted."),
433 /* cant_delete_used_item */
434 N_("Sorry, but download \"%s\" is being used by something else."),
435 /* cant_delete_folder */
436 NULL,
437 /* cant_delete_used_folder */
438 NULL,
439 /* delete_marked_items_title */
440 N_("Interrupt marked downloads"),
441 /* delete_marked_items */
442 N_("Interrupt marked downloads?"),
443 /* delete_folder_title */
444 NULL,
445 /* delete_folder */
446 NULL,
447 /* delete_item_title */
448 N_("Interrupt download"),
449 /* delete_item; xgettext:c-format */
450 N_("Interrupt this download?"),
451 /* clear_all_items_title */
452 N_("Interrupt all downloads"),
453 /* clear_all_items_title */
454 N_("Do you really want to interrupt all downloads?"),
457 static const struct listbox_ops downloads_listbox_ops = {
458 lock_file_download,
459 unlock_file_download,
460 is_file_download_used,
461 get_file_download_text,
462 get_file_download_info,
463 get_file_download_uri,
464 get_file_download_root,
465 NULL,
466 can_delete_file_download,
467 delete_file_download,
468 draw_file_download,
469 &download_messages,
473 static widget_handler_status_T
474 push_info_button(struct dialog_data *dlg_data, struct widget_data *button)
476 struct listbox_data *box = get_dlg_listbox_data(dlg_data);
477 struct terminal *term = dlg_data->win->term;
478 struct session *ses = dlg_data->dlg->udata;
479 struct file_download *file_download = box->sel ? box->sel->udata : NULL;
481 assert(ses);
483 if (!file_download) return EVENT_PROCESSED;
485 /* Don't layer on top of the download manager */
486 delete_window(dlg_data->win);
488 display_download(term, file_download, ses);
489 return EVENT_PROCESSED;
493 /* TODO: Ideas for buttons .. should be pretty trivial most of it
495 * - Resume or something that will use some goto like handler
496 * - Open button that can be used to set file_download->prog.
497 * - Toggle notify button
499 static const struct hierbox_browser_button download_buttons[] = {
500 /* [gettext_accelerator_context(.download_buttons)] */
501 { N_("~Info"), push_info_button },
502 { N_("~Abort"), push_hierbox_delete_button },
503 #if 0
504 /* This requires more work to make locking work and query the user */
505 { N_("Abort and delete file"), push_delete_button },
506 #endif
507 { N_("C~lear"), push_hierbox_clear_button },
510 static struct_hierbox_browser(
511 download_browser,
512 N_("Download manager"),
513 download_buttons,
514 &downloads_listbox_ops
517 void
518 download_manager(struct session *ses)
520 hierbox_browser(&download_browser, ses);
522 /* FIXME: It's workaround for bug 397. Real fix is needed. */
523 download_browser.do_not_save_state = 1;
526 void
527 init_download_display(struct file_download *file_download)
529 file_download->box_item = add_listbox_leaf(&download_browser, NULL,
530 file_download);
533 void
534 done_download_display(struct file_download *file_download)
536 if (file_download->box_item) {
537 done_listbox_item(&download_browser, file_download->box_item);
538 file_download->box_item = NULL;