1 /* Download dialogs */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "elinks.h"
12 
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"
38 
39 
40 static void
undisplay_download(struct file_download * file_download)41 undisplay_download(struct file_download *file_download)
42 {
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);
46 }
47 
48 static void
do_abort_download(struct file_download * file_download)49 do_abort_download(struct file_download *file_download)
50 {
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);
55 	}
56 }
57 
58 static widget_handler_status_T
dlg_set_notify(struct dialog_data * dlg_data,struct widget_data * widget_data)59 dlg_set_notify(struct dialog_data *dlg_data, struct widget_data *widget_data)
60 {
61 	struct file_download *file_download = dlg_data->dlg->udata;
62 
63 	file_download->notify = 1;
64 #if CONFIG_BITTORRENT
65 	if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
66 		set_bittorrent_notify_on_completion(&file_download->download,
67 						    file_download->term);
68 #endif
69 	undisplay_download(file_download);
70 	return EVENT_PROCESSED;
71 }
72 
73 static widget_handler_status_T
dlg_abort_download(struct dialog_data * dlg_data,struct widget_data * widget_data)74 dlg_abort_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
75 {
76 	struct file_download *file_download = dlg_data->dlg->udata;
77 
78 	object_unlock(file_download);
79 	register_bottom_half(do_abort_download, file_download);
80 	return EVENT_PROCESSED;
81 }
82 
83 static widget_handler_status_T
push_delete_button(struct dialog_data * dlg_data,struct widget_data * widget_data)84 push_delete_button(struct dialog_data *dlg_data, struct widget_data *widget_data)
85 {
86 	struct file_download *file_download = dlg_data->dlg->udata;
87 
88 	file_download->delete = 1;
89 #if CONFIG_BITTORRENT
90 	if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
91 		set_bittorrent_files_for_deletion(&file_download->download);
92 #endif
93 	object_unlock(file_download);
94 	register_bottom_half(do_abort_download, file_download);
95 	return EVENT_PROCESSED;
96 }
97 
98 static widget_handler_status_T
dlg_undisplay_download(struct dialog_data * dlg_data,struct widget_data * widget_data)99 dlg_undisplay_download(struct dialog_data *dlg_data, struct widget_data *widget_data)
100 {
101 	struct file_download *file_download = dlg_data->dlg->udata;
102 
103 	object_unlock(file_download);
104 	register_bottom_half(undisplay_download, file_download);
105 	return EVENT_PROCESSED;
106 }
107 
108 
109 static void
download_abort_function(struct dialog_data * dlg_data)110 download_abort_function(struct dialog_data *dlg_data)
111 {
112 	struct file_download *file_download = dlg_data->dlg->udata;
113 
114 	file_download->dlg_data = NULL;
115 }
116 
117 
118 static void
download_dialog_layouter(struct dialog_data * dlg_data)119 download_dialog_layouter(struct dialog_data *dlg_data)
120 {
121 	struct file_download *file_download = dlg_data->dlg->udata;
122 	struct terminal *term = dlg_data->win->term;
123 	int w = dialog_max_width(term);
124 	int rw = w;
125 	int x, y = 0;
126 	int url_len;
127 	unsigned char *url;
128 	struct download *download = &file_download->download;
129 	struct color_pair *dialog_text_color = get_bfu_color(term, "dialog.text");
130 	unsigned char *msg = get_download_msg(download, term, 1, 1, "\n");
131 	int show_meter = (download_is_progressing(download)
132 			  && download->progress->size >= 0);
133 #if CONFIG_BITTORRENT
134 	int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT
135 			  && (show_meter || download->state == S_RESUME));
136 #endif
137 
138 	redraw_below_window(dlg_data->win);
139 	file_download->dlg_data = dlg_data;
140 
141 	if (!msg) return;
142 
143 	url = get_uri_string(file_download->uri, URI_PUBLIC);
144 	if (!url) {
145 		mem_free(msg);
146 		return;
147 	}
148 	decode_uri_for_display(url);
149 	url_len = strlen(url);
150 
151 	if (show_meter) {
152 		int_lower_bound(&w, DOWN_DLG_MIN);
153 	}
154 
155 	dlg_format_text_do(NULL, url, 0, &y, w, &rw,
156 			dialog_text_color, ALIGN_LEFT);
157 
158 	y++;
159 	if (show_meter) y += 2;
160 
161 #if CONFIG_BITTORRENT
162 	if (bittorrent) y += 2;
163 #endif
164 	dlg_format_text_do(NULL, msg, 0, &y, w, &rw,
165 			dialog_text_color, ALIGN_LEFT);
166 
167 	y++;
168 	dlg_format_buttons(NULL, dlg_data->widgets_data,
169 			   dlg_data->number_of_widgets, 0, &y, w,
170 			   &rw, ALIGN_CENTER);
171 
172 	draw_dialog(dlg_data, w, y);
173 
174 	w = rw;
175 	if (url_len > w) {
176 		/* Truncate too long urls */
177 		url_len = w;
178 		url[url_len] = '\0';
179 		if (url_len > 4) {
180 			url[--url_len] = '.';
181 			url[--url_len] = '.';
182 			url[--url_len] = '.';
183 		}
184 	}
185 
186 	y = dlg_data->box.y + DIALOG_TB + 1;
187 	x = dlg_data->box.x + DIALOG_LB;
188 	dlg_format_text_do(term, url, x, &y, w, NULL,
189 			dialog_text_color, ALIGN_LEFT);
190 
191 	if (show_meter) {
192 		y++;
193 		draw_progress_bar(download->progress, term, x, y, w, NULL, NULL);
194 		y++;
195 	}
196 
197 #if CONFIG_BITTORRENT
198 	if (bittorrent) {
199 		y++;
200 		draw_bittorrent_piece_progress(download, term, x, y, w, NULL, NULL);
201 		y++;
202 	}
203 #endif
204 	y++;
205 	dlg_format_text_do(term, msg, x, &y, w, NULL,
206 			dialog_text_color, ALIGN_LEFT);
207 
208 	y++;
209 	dlg_format_buttons(term, dlg_data->widgets_data,
210 			   dlg_data->number_of_widgets, x, &y, w,
211 			   NULL, ALIGN_CENTER);
212 
213 	mem_free(url);
214 	mem_free(msg);
215 }
216 
217 void
display_download(struct terminal * term,struct file_download * file_download,struct session * ses)218 display_download(struct terminal *term, struct file_download *file_download,
219 		 struct session *ses)
220 {
221 	struct dialog *dlg;
222 
223 	if (!is_in_downloads_list(file_download))
224 		return;
225 
226 #if CONFIG_BITTORRENT
227 #define DOWNLOAD_WIDGETS_COUNT 5
228 #else
229 #define DOWNLOAD_WIDGETS_COUNT 4
230 #endif
231 
232 	dlg = calloc_dialog(DOWNLOAD_WIDGETS_COUNT, 0);
233 	if (!dlg) return;
234 
235 	undisplay_download(file_download);
236 	file_download->ses = ses;
237 	dlg->title = _("Download", term);
238 	dlg->layouter = download_dialog_layouter;
239 	dlg->abort = download_abort_function;
240 	dlg->udata = file_download;
241 
242 	object_lock(file_download);
243 
244 	add_dlg_button(dlg, _("~Background", term), B_ENTER | B_ESC, dlg_undisplay_download, NULL);
245 	add_dlg_button(dlg, _("Background with ~notify", term), B_ENTER | B_ESC, dlg_set_notify, NULL);
246 
247 #if CONFIG_BITTORRENT
248 	if (file_download->uri->protocol == PROTOCOL_BITTORRENT)
249 		add_dlg_button(dlg, _("~Info", term), B_ENTER | B_ESC, dlg_show_bittorrent_info, NULL);
250 #endif
251 
252 	add_dlg_button(dlg, _("~Abort", term), 0, dlg_abort_download, NULL);
253 
254 	/* Downloads scheduled to be opened by external handlers are always
255 	 * deleted. */
256 	if (!file_download->external_handler) {
257 		add_dlg_button(dlg, _("Abort and ~delete file", term), 0, push_delete_button, NULL);
258 	}
259 
260 #if CONFIG_BITTORRENT
261 	add_dlg_end(dlg, DOWNLOAD_WIDGETS_COUNT - !!file_download->external_handler
262 		         - (file_download->uri->protocol != PROTOCOL_BITTORRENT));
263 #else
264 	add_dlg_end(dlg, DOWNLOAD_WIDGETS_COUNT - !!file_download->external_handler);
265 #endif
266 
267 	do_dialog(term, dlg, getml(dlg, NULL));
268 }
269 
270 
271 /* The download manager */
272 
273 static void
lock_file_download(struct listbox_item * item)274 lock_file_download(struct listbox_item *item)
275 {
276 	object_lock((struct file_download *) item->udata);
277 }
278 
279 static void
unlock_file_download(struct listbox_item * item)280 unlock_file_download(struct listbox_item *item)
281 {
282 	object_unlock((struct file_download *) item->udata);
283 }
284 
285 static int
is_file_download_used(struct listbox_item * item)286 is_file_download_used(struct listbox_item *item)
287 {
288 	return is_object_used((struct file_download *) item->udata);
289 }
290 
291 static unsigned char *
get_file_download_text(struct listbox_item * item,struct terminal * term)292 get_file_download_text(struct listbox_item *item, struct terminal *term)
293 {
294 	struct file_download *file_download = item->udata;
295 	unsigned char *uristring;
296 
297 	uristring = get_uri_string(file_download->uri, URI_PUBLIC);
298 	if (uristring) decode_uri_for_display(uristring);
299 
300 	return uristring;
301 }
302 
303 static unsigned char *
get_file_download_info(struct listbox_item * item,struct terminal * term)304 get_file_download_info(struct listbox_item *item, struct terminal *term)
305 {
306 	return NULL;
307 }
308 
309 static struct uri *
get_file_download_uri(struct listbox_item * item)310 get_file_download_uri(struct listbox_item *item)
311 {
312 	struct file_download *file_download = item->udata;
313 
314 	return get_uri_reference(file_download->uri);
315 }
316 
317 static struct listbox_item *
get_file_download_root(struct listbox_item * item)318 get_file_download_root(struct listbox_item *item)
319 {
320 	return NULL;
321 }
322 
323 static int
can_delete_file_download(struct listbox_item * item)324 can_delete_file_download(struct listbox_item *item)
325 {
326 	return 1;
327 }
328 
329 static void
delete_file_download(struct listbox_item * item,int last)330 delete_file_download(struct listbox_item *item, int last)
331 {
332 	struct file_download *file_download = item->udata;
333 
334 	assert(!is_object_used(file_download));
335 	register_bottom_half(do_abort_download, file_download);
336 }
337 
338 static enum dlg_refresh_code
refresh_file_download(struct dialog_data * dlg_data,void * data)339 refresh_file_download(struct dialog_data *dlg_data, void *data)
340 {
341 	/* Always refresh (until we keep finished downloads) */
342 	return are_there_downloads() ? REFRESH_DIALOG : REFRESH_STOP;
343 }
344 
345 /* TODO: Make it configurable */
346 #define DOWNLOAD_METER_WIDTH 15
347 #define DOWNLOAD_URI_PERCENTAGE 50
348 
349 static void
draw_file_download(struct listbox_item * item,struct listbox_context * context,int x,int y,int width)350 draw_file_download(struct listbox_item *item, struct listbox_context *context,
351 		   int x, int y, int width)
352 {
353 	struct file_download *file_download = item->udata;
354 	struct download *download = &file_download->download;
355 	unsigned char *stylename;
356 	struct color_pair *color;
357 	unsigned char *text;
358 	int length;
359 	int trimmedlen;
360 	int meter = DOWNLOAD_METER_WIDTH;
361 
362 	/* We have nothing to work with */
363 	if (width < 4) return;
364 
365 	stylename = (item == context->box->sel) ? "menu.selected"
366 		  : ((item->marked)	        ? "menu.marked"
367 					        : "menu.normal");
368 
369 	color = get_bfu_color(context->term, stylename);
370 
371 	text = get_file_download_text(item, NULL);
372 	if (!text) return;
373 
374 	length = strlen(text);
375 	/* Show atleast the required percentage of the URI */
376 	if (length * DOWNLOAD_URI_PERCENTAGE / 100 < width - meter - 4) {
377 		trimmedlen = int_min(length, width - meter - 4);
378 	} else {
379 		trimmedlen = int_min(length, width - 3);
380 	}
381 
382 	draw_text(context->term, x, y, text, trimmedlen, 0, color);
383 	if (trimmedlen < length) {
384 		draw_text(context->term, x + trimmedlen, y, "...", 3, 0, color);
385 		trimmedlen += 3;
386 	}
387 
388 	mem_free(text);
389 
390 	if (!download->progress
391 	    || download->progress->size < 0
392 	    || download->state != S_TRANS
393 	    || !has_progress(download->progress)) {
394 		/* TODO: Show trimmed error message. */
395 		return;
396 	}
397 
398 	if (!dialog_has_refresh(context->dlg_data))
399 		refresh_dialog(context->dlg_data, refresh_file_download, NULL);
400 
401 	if (trimmedlen + meter >= width) return;
402 
403 	x += width - meter;
404 
405 	draw_progress_bar(download->progress, context->term, x, y, meter, NULL, NULL);
406 }
407 
408 static struct listbox_ops_messages download_messages = {
409 	/* cant_delete_item */
410 	N_("Sorry, but download \"%s\" cannot be interrupted."),
411 	/* cant_delete_used_item */
412 	N_("Sorry, but download \"%s\" is being used by something else."),
413 	/* cant_delete_folder */
414 	NULL,
415 	/* cant_delete_used_folder */
416 	NULL,
417 	/* delete_marked_items_title */
418 	N_("Interrupt marked downloads"),
419 	/* delete_marked_items */
420 	N_("Interrupt marked downloads?"),
421 	/* delete_folder_title */
422 	NULL,
423 	/* delete_folder */
424 	NULL,
425 	/* delete_item_title */
426 	N_("Interrupt download"),
427 	/* delete_item */
428 	N_("Interrupt this download?"),
429 	/* clear_all_items_title */
430 	N_("Interrupt all downloads"),
431 	/* clear_all_items_title */
432 	N_("Do you really want to interrupt all downloads?"),
433 };
434 
435 static struct listbox_ops downloads_listbox_ops = {
436 	lock_file_download,
437 	unlock_file_download,
438 	is_file_download_used,
439 	get_file_download_text,
440 	get_file_download_info,
441 	get_file_download_uri,
442 	get_file_download_root,
443 	NULL,
444 	can_delete_file_download,
445 	delete_file_download,
446 	draw_file_download,
447 	&download_messages,
448 };
449 
450 
451 static widget_handler_status_T
push_info_button(struct dialog_data * dlg_data,struct widget_data * button)452 push_info_button(struct dialog_data *dlg_data, struct widget_data *button)
453 {
454 	struct listbox_data *box = get_dlg_listbox_data(dlg_data);
455 	struct terminal *term = dlg_data->win->term;
456 	struct session *ses = dlg_data->dlg->udata;
457 	struct file_download *file_download = box->sel ? box->sel->udata : NULL;
458 
459 	assert(ses);
460 
461 	if (!file_download) return EVENT_PROCESSED;
462 
463 	/* Don't layer on top of the download manager */
464 	delete_window(dlg_data->win);
465 
466 	display_download(term, file_download, ses);
467 	return EVENT_PROCESSED;
468 }
469 
470 
471 /* TODO: Ideas for buttons .. should be pretty trivial most of it
472  *
473  * - Resume or something that will use some goto like handler
474  * - Open button that can be used to set file_download->prog.
475  * - Toggle notify button
476  */
477 static struct hierbox_browser_button download_buttons[] = {
478 	{ N_("~Info"),                  push_info_button           },
479 	{ N_("~Abort"),                 push_hierbox_delete_button },
480 #if 0
481 	/* This requires more work to make locking work and query the user */
482 	{ N_("Abort and delete file"),  push_delete_button         },
483 #endif
484 	{ N_("C~lear"),                 push_hierbox_clear_button  },
485 };
486 
487 static struct_hierbox_browser(
488 	download_browser,
489 	N_("Download manager"),
490 	download_buttons,
491 	&downloads_listbox_ops
492 );
493 
494 void
download_manager(struct session * ses)495 download_manager(struct session *ses)
496 {
497 	hierbox_browser(&download_browser, ses);
498 
499 	/* FIXME: It's workaround for bug 397. Real fix is needed. */
500 	download_browser.do_not_save_state = 1;
501 }
502 
503 void
init_download_display(struct file_download * file_download)504 init_download_display(struct file_download *file_download)
505 {
506 	file_download->box_item = add_listbox_leaf(&download_browser, NULL,
507 						   file_download);
508 }
509 
510 void
done_download_display(struct file_download * file_download)511 done_download_display(struct file_download *file_download)
512 {
513 	if (file_download->box_item) {
514 		done_listbox_item(&download_browser, file_download->box_item);
515 		file_download->box_item = NULL;
516 	}
517 }
518 
519