links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

session.c (91226B)


      1 /* session.c
      2  * (c) 2002 Mikulas Patocka
      3  * This file is a part of the Links program, released under GPL.
      4  */
      5 
      6 #include <limits.h>
      7 
      8 #include "links.h"
      9 
     10 struct list_head downloads = { &downloads, &downloads };
     11 
     12 /* prototypes */
     13 static void abort_and_delete_download(void *);
     14 static void undisplay_download(void *);
     15 static void increase_download_file(unsigned char **f);
     16 static void copy_additional_files(struct additional_files **a);
     17 static struct location *new_location(void);
     18 static void destroy_location(struct location *loc);
     19 
     20 int
     21 are_there_downloads(void)
     22 {
     23 	struct download *down = NULL;
     24 	struct list_head *ldown;
     25 	foreach (struct download, down, ldown, downloads)
     26 		if (!down->prog)
     27 			return 1;
     28 	return 0;
     29 }
     30 
     31 struct list_head sessions = { &sessions, &sessions };
     32 unsigned char
     33 get_session_attribute(struct session *ses, int reverse)
     34 {
     35 	if (!ses->term->spec->col) {
     36 		if (!reverse)
     37 			return COLOR_TITLE;
     38 		else
     39 			return COLOR_STATUS;
     40 	} else {
     41 		if (!reverse)
     42 			return get_attribute(ses->ds.t_text_color,
     43 			                     ses->ds.t_background_color);
     44 		else
     45 			return get_attribute(ses->ds.t_background_color,
     46 			                     ses->ds.t_text_color);
     47 	}
     48 }
     49 
     50 struct s_msg_dsc {
     51 	int n;
     52 	unsigned char *msg;
     53 };
     54 
     55 static const struct s_msg_dsc msg_dsc[] = {
     56 	{S_WAIT,                 TEXT_(T_WAITING_IN_QUEUE)                    },
     57 	{ S_DNS,		 TEXT_(T_LOOKING_UP_HOST)                     },
     58 	{ S_CONN,                TEXT_(T_MAKING_CONNECTION)                   },
     59 	{ S_CONN_ANOTHER,        TEXT_(T_MAKING_CONNECTION_TO_ANOTHER_ADDRESS)},
     60 	{ S_SOCKS_NEG,           TEXT_(T_SOCKS_NEGOTIATION)                   },
     61 	{ S_SSL_NEG,             TEXT_(T_SSL_NEGOTIATION)                     },
     62 	{ S_SENT,                TEXT_(T_REQUEST_SENT)                        },
     63 	{ S_LOGIN,               TEXT_(T_LOGGING_IN)                          },
     64 	{ S_GETH,                TEXT_(T_GETTING_HEADERS)                     },
     65 	{ S_PROC,                TEXT_(T_SERVER_IS_PROCESSING_REQUEST)        },
     66 	{ S_TRANS,               TEXT_(T_TRANSFERRING)                        },
     67 
     68 	{ S__OK,		 TEXT_(T_OK)				  },
     69 	{ S_INTERRUPTED,         TEXT_(T_INTERRUPTED)                         },
     70 	{ S_INTERNAL,            TEXT_(T_INTERNAL_ERROR)                      },
     71 	{ S_OUT_OF_MEM,          TEXT_(T_OUT_OF_MEMORY)                       },
     72 	{ S_NO_DNS,              TEXT_(T_HOST_NOT_FOUND)                      },
     73 	{ S_NO_PROXY_DNS,        TEXT_(T_PROXY_NOT_FOUND)                     },
     74 	{ S_CANT_WRITE,          TEXT_(T_ERROR_WRITING_TO_SOCKET)             },
     75 	{ S_CANT_READ,           TEXT_(T_ERROR_READING_FROM_SOCKET)           },
     76 	{ S_MODIFIED,            TEXT_(T_DATA_MODIFIED)                       },
     77 	{ S_BAD_URL,             TEXT_(T_BAD_URL_SYNTAX)                      },
     78 	{ S_BAD_PROXY,           TEXT_(T_BAD_PROXY_SYNTAX)                    },
     79 	{ S_TIMEOUT,             TEXT_(T_RECEIVE_TIMEOUT)                     },
     80 	{ S_RESTART,             TEXT_(T_REQUEST_MUST_BE_RESTARTED)           },
     81 	{ S_STATE,               TEXT_(T_CANT_GET_SOCKET_STATE)               },
     82 	{ S_CYCLIC_REDIRECT,     TEXT_(T_CYCLIC_REDIRECT)                     },
     83 	{ S_LARGE_FILE,          TEXT_(T_TOO_LARGE_FILE)                      },
     84 
     85 	{ S_HTTP_ERROR,          TEXT_(T_BAD_HTTP_RESPONSE)                   },
     86 	{ S_HTTP_204,            TEXT_(T_NO_CONTENT)                          },
     87 	{ S_HTTPS_FWD_ERROR,     TEXT_(T_HTTPS_FWD_ERROR)                     },
     88 	{ S_INVALID_CERTIFICATE, TEXT_(T_INVALID_CERTIFICATE)                 },
     89 	{ S_DOWNGRADED_METHOD,   TEXT_(T_DOWNGRADED_METHOD)                   },
     90 	{ S_INSECURE_CIPHER,     TEXT_(T_INSECURE_CIPHER)                     },
     91 
     92 	{ S_FILE_TYPE,           TEXT_(T_UNKNOWN_FILE_TYPE)                   },
     93 	{ S_FILE_ERROR,          TEXT_(T_ERROR_OPENING_FILE)                  },
     94 
     95 	{ S_SSL_ERROR,           TEXT_(T_SSL_ERROR)                           },
     96 	{ S_NO_SSL,              TEXT_(T_NO_SSL)                              },
     97 	{ S_BAD_SOCKS_VERSION,   TEXT_(T_BAD_SOCKS_VERSION)                   },
     98 	{ S_SOCKS_REJECTED,      TEXT_(T_SOCKS_REJECTED_OR_FAILED)            },
     99 	{ S_SOCKS_NO_IDENTD,     TEXT_(T_SOCKS_NO_IDENTD)                     },
    100 	{ S_SOCKS_BAD_USERID,    TEXT_(T_SOCKS_BAD_USERID)                    },
    101 	{ S_SOCKS_UNKNOWN_ERROR, TEXT_(T_SOCKS_UNKNOWN_ERROR)                 },
    102 
    103 	{ S_NO_PROXY,            TEXT_(T_NO_PROXY)                            },
    104 	{ S_SMB_NOT_ALLOWED,     TEXT_(T_SMB_NOT_ALLOWED)                     },
    105 	{ S_FILE_NOT_ALLOWED,    TEXT_(T_FILE_NOT_ALLOWED)                    },
    106 
    107 	{ S_WAIT_REDIR,          TEXT_(T_WAITING_FOR_REDIRECT_CONFIRMATION)   },
    108 	{ 0,		     NULL					 }
    109 };
    110 
    111 struct strerror_val {
    112 	list_entry_1st;
    113 #ifdef REORDER_LIST_ENTRIES
    114 	unsigned char pad;
    115 #endif
    116 	unsigned char msg[1];
    117 };
    118 
    119 static struct list_head strerror_buf = { &strerror_buf, &strerror_buf };
    120 
    121 void
    122 free_strerror_buf(void)
    123 {
    124 	free_list(struct strerror_val, strerror_buf);
    125 }
    126 
    127 int
    128 get_error_from_errno(int errn)
    129 {
    130 	if (errn > 0 && (errn < -S__OK || errn > -S_MAX))
    131 		return -errn;
    132 	return S_UNKNOWN_ERROR;
    133 }
    134 
    135 unsigned char *
    136 get_err_msg(int state)
    137 {
    138 	unsigned char *e;
    139 	size_t sl;
    140 	struct strerror_val *s = NULL;
    141 	struct list_head *ls;
    142 	if ((state >= S_MAX && state <= S__OK) || state >= S_WAIT) {
    143 		int i;
    144 		for (i = 0; msg_dsc[i].msg; i++)
    145 			if (msg_dsc[i].n == state)
    146 				return msg_dsc[i].msg;
    147 unk:
    148 		return TEXT_(T_UNKNOWN_ERROR);
    149 	}
    150 	if ((e = cast_uchar strerror(-state)) && *e)
    151 		goto have_error;
    152 	goto unk;
    153 have_error:
    154 	foreach (struct strerror_val, s, ls, strerror_buf)
    155 		if (!strcmp(cast_const_char s->msg, cast_const_char e)) {
    156 			return s->msg;
    157 		}
    158 	sl = strlen(cast_const_char e);
    159 	if (sl > INT_MAX - sizeof(struct strerror_val))
    160 		overalloc();
    161 	s = xmalloc(sizeof(struct strerror_val) + sl);
    162 	strcpy(cast_char s->msg, cast_const_char e);
    163 	add_to_list(strerror_buf, s);
    164 	return s->msg;
    165 }
    166 
    167 static size_t
    168 add_xnum_to_str(unsigned char **s, size_t l, off_t n)
    169 {
    170 	unsigned char suff = 0;
    171 	int d = -1;
    172 	if (n >= 1000000000) {
    173 		suff = 'G';
    174 		d = (int)((n / 100000000) % 10);
    175 		n /= 1000000000;
    176 	} else if (n >= 1000000) {
    177 		suff = 'M';
    178 		d = (int)((n / 100000) % 10);
    179 		n /= 1000000;
    180 	} else if (n >= 1000) {
    181 		suff = 'k';
    182 		d = (int)((n / 100) % 10);
    183 		n /= 1000;
    184 	}
    185 	l = add_num_to_str(s, l, n);
    186 	if (n < 10 && d != -1) {
    187 		l = add_chr_to_str(s, l, '.');
    188 		add_num_to_str(s, l, d);
    189 	}
    190 	l = add_chr_to_str(s, l, ' ');
    191 	if (suff)
    192 		l = add_chr_to_str(s, l, suff);
    193 	return add_chr_to_str(s, l, 'B');
    194 }
    195 
    196 static size_t
    197 add_time_to_str(unsigned char **s, size_t l, uttime t)
    198 {
    199 	unsigned char q[64];
    200 	if (t >= 86400) {
    201 		sprintf(cast_char q, "%lud ", (unsigned long)(t / 86400));
    202 		l = add_to_str(s, l, q);
    203 	}
    204 	if (t >= 3600) {
    205 		t %= 86400;
    206 		sprintf(cast_char q, "%d:%02d", (int)(t / 3600),
    207 		        (int)(t / 60 % 60));
    208 		l = add_to_str(s, l, q);
    209 	} else {
    210 		sprintf(cast_char q, "%d", (int)(t / 60));
    211 		l = add_to_str(s, l, q);
    212 	}
    213 	sprintf(cast_char q, ":%02d", (int)(t % 60));
    214 	return add_to_str(s, l, q);
    215 }
    216 
    217 static unsigned char *
    218 get_stat_msg(struct status *stat, struct terminal *term)
    219 {
    220 	if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
    221 		unsigned char *m = NULL;
    222 		size_t l;
    223 		l = add_to_str(&m, 0,
    224 		               get_text_translation(TEXT_(T_RECEIVED), term));
    225 		l = add_chr_to_str(&m, l, ' ');
    226 		l = add_xnum_to_str(&m, l, stat->prg->pos);
    227 		if (stat->prg->size >= 0) {
    228 			l = add_chr_to_str(&m, l, ' ');
    229 			l = add_to_str(&m, l,
    230 			               get_text_translation(TEXT_(T_OF), term));
    231 			l = add_chr_to_str(&m, l, ' ');
    232 			l = add_xnum_to_str(&m, l, stat->prg->size);
    233 		}
    234 		l = add_to_str(&m, l, cast_uchar ", ");
    235 		if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME) {
    236 			l = add_to_str(
    237 			    &m, l, get_text_translation(TEXT_(T_AVG), term));
    238 			l = add_chr_to_str(&m, l, ' ');
    239 		}
    240 		l = add_xnum_to_str(
    241 		    &m, l, stat->prg->loaded * 10 / (stat->prg->elapsed / 100));
    242 		l = add_to_str(&m, l, cast_uchar "/s");
    243 		if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME) {
    244 			l = add_to_str(&m, l, cast_uchar ", ");
    245 			l = add_to_str(
    246 			    &m, l, get_text_translation(TEXT_(T_CUR), term));
    247 			l = add_chr_to_str(&m, l, ' ');
    248 			l = add_xnum_to_str(
    249 			    &m, l,
    250 			    stat->prg->cur_loaded
    251 				/ (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000));
    252 			l = add_to_str(&m, l, cast_uchar "/s");
    253 		}
    254 		return m;
    255 	}
    256 	return stracpy(get_text_translation(get_err_msg(stat->state), term));
    257 }
    258 
    259 void
    260 change_screen_status(struct session *ses)
    261 {
    262 	struct status *stat = NULL;
    263 	if (ses->rq) {
    264 		stat = &ses->rq->stat;
    265 	} else {
    266 		struct f_data_c *fd = current_frame(ses);
    267 		if (fd->rq)
    268 			stat = &fd->rq->stat;
    269 		if (stat && stat->state == S__OK && fd->af) {
    270 			unsigned count = 0;
    271 			struct additional_file *af = NULL;
    272 			struct list_head *laf;
    273 			foreachback (struct additional_file, af, laf,
    274 			             fd->af->af) {
    275 				if (af->rq && af->rq->stat.state >= 0) {
    276 					if (af->rq->stat.state > stat->state
    277 					    || (af->rq->stat.state == S_TRANS
    278 					        && stat->state == S_TRANS
    279 					        && af->rq->stat.prg->pos
    280 					               > stat->prg->pos))
    281 						stat = &af->rq->stat;
    282 				}
    283 				count++;
    284 				/* avoid too high cpu consumption */
    285 				if (count >= 100 && stat->state >= 0
    286 				    && stat->state != S_WAIT)
    287 					break;
    288 			}
    289 		}
    290 	}
    291 	free(ses->st);
    292 
    293 	/* default status se ukazuje, kdyz
    294 	 *			a) by se jinak ukazovalo prazdno
    295 	 *			b) neni NULL a ukazovalo by se OK
    296 	 */
    297 	ses->st = NULL;
    298 	if (stat) {
    299 		if (stat->state == S__OK)
    300 			ses->st = print_current_link(ses);
    301 		if (!ses->st)
    302 			ses->st = ses->default_status
    303 			              ? stracpy(ses->default_status)
    304 			              : get_stat_msg(stat, ses->term);
    305 	} else {
    306 		ses->st = stracpy(ses->default_status);
    307 	}
    308 }
    309 
    310 static void
    311 x_print_screen_status(struct terminal *term, void *ses_)
    312 {
    313 	struct session *ses = (struct session *)ses_;
    314 	unsigned char color = get_session_attribute(ses, proxies.only_proxies);
    315 	fill_area(term, 0, term->y - 1, term->x, 1, ' ', color);
    316 	if (ses->st)
    317 		print_text(term, 0, term->y - 1,
    318 		           (int)strlen(cast_const_char ses->st), ses->st,
    319 		           COLOR_STATUS);
    320 }
    321 
    322 static void
    323 x_print_screen_title(struct terminal *term, void *ses_)
    324 {
    325 	struct session *ses = (struct session *)ses_;
    326 	unsigned char *m;
    327 	unsigned char color = get_session_attribute(ses, proxies.only_proxies);
    328 	if (!term->spec->col)
    329 		color = COLOR_TITLE;
    330 	fill_area(term, 0, 0, term->x, 1, ' ', color);
    331 	if ((m = print_current_title(ses))) {
    332 		int p = term->x - 1 - strlen((char *)m);
    333 		if (p < 0)
    334 			p = 0;
    335 		print_text(term, p, 0, strlen((char *)m), m, color);
    336 		free(m);
    337 	}
    338 }
    339 
    340 static void
    341 print_only_screen_status(struct session *ses)
    342 {
    343 	draw_to_window(ses->win, x_print_screen_status, ses);
    344 }
    345 
    346 void
    347 print_screen_status(struct session *ses)
    348 {
    349 	unsigned char *m;
    350 
    351 	print_only_screen_status(ses);
    352 	draw_to_window(ses->win, x_print_screen_title, ses);
    353 
    354 	m = stracpy(cast_uchar "Links");
    355 	if (ses->screen && ses->screen->f_data && ses->screen->f_data->title
    356 	    && ses->screen->f_data->title[0]) {
    357 		add_to_strn(&m, cast_uchar " - ");
    358 		add_to_strn(&m, ses->screen->f_data->title);
    359 	}
    360 	set_terminal_title(ses->term, m);
    361 
    362 	if (ses->brl_cursor_mode) {
    363 		if (ses->brl_cursor_mode == 1)
    364 			set_cursor(ses->term, 0, 0, 0, 0);
    365 		if (ses->brl_cursor_mode == 2)
    366 			set_cursor(ses->term, 0, ses->term->y - 1, 0,
    367 			           ses->term->y - 1);
    368 	}
    369 }
    370 
    371 void
    372 print_progress(struct session *ses, unsigned char *msg)
    373 {
    374 	free(ses->st);
    375 	ses->st = stracpy(get_text_translation(msg, ses->term));
    376 	print_only_screen_status(ses);
    377 	flush_terminal(ses->term);
    378 }
    379 
    380 void
    381 print_error_dialog(struct session *ses, struct status *stat, unsigned char *url)
    382 {
    383 	unsigned char *t = get_err_msg(stat->state);
    384 	unsigned char *u = display_url(ses->term, url, 1);
    385 	msg_box(ses->term, getml(u, NULL), TEXT_(T_ERROR), AL_CENTER,
    386 	        TEXT_(T_ERROR_LOADING), cast_uchar " ", u, cast_uchar ":\n\n",
    387 	        t, MSG_BOX_END,
    388 	        (void *)ses, 1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC /*, get_text_translation("Retry"), NULL, 0 !!! FIXME: retry */);
    389 }
    390 
    391 static inline unsigned char
    392 hx(int a)
    393 {
    394 	return a >= 10 ? a + 'A' - 10 : a + '0';
    395 }
    396 
    397 static inline int
    398 unhx(unsigned char a)
    399 {
    400 	if (a >= '0' && a <= '9')
    401 		return a - '0';
    402 	if (a >= 'A' && a <= 'F')
    403 		return a - 'A' + 10;
    404 	if (a >= 'a' && a <= 'f')
    405 		return a - 'a' + 10;
    406 	return -1;
    407 }
    408 
    409 unsigned char *
    410 encode_url(unsigned char *url)
    411 {
    412 	unsigned char *u = NULL;
    413 	size_t l;
    414 	l = add_to_str(&u, 0, cast_uchar "+++");
    415 	for (; *url; url++) {
    416 		if (is_safe_in_shell(*url) && *url != '+')
    417 			l = add_chr_to_str(&u, l, *url);
    418 		else {
    419 			l = add_chr_to_str(&u, l, '+');
    420 			l = add_chr_to_str(&u, l, hx(*url >> 4));
    421 			l = add_chr_to_str(&u, l, hx(*url & 0xf));
    422 		}
    423 	}
    424 	return u;
    425 }
    426 
    427 unsigned char *
    428 decode_url(unsigned char *url)
    429 {
    430 	unsigned char *u;
    431 	int l;
    432 	if (casecmp(url, cast_uchar "+++", 3))
    433 		return stracpy(url);
    434 	url += 3;
    435 	u = NULL;
    436 	l = 0;
    437 	for (; *url; url++) {
    438 		if (*url != '+' || unhx(url[1]) == -1 || unhx(url[2]) == -1)
    439 			l = add_chr_to_str(&u, l, *url);
    440 		else {
    441 			l = add_chr_to_str(&u, l,
    442 			                   (unhx(url[1]) << 4) + unhx(url[2]));
    443 			url += 2;
    444 		}
    445 	}
    446 	return u;
    447 }
    448 
    449 struct session *
    450 get_download_ses(struct download *down)
    451 {
    452 	struct session *ses = NULL;
    453 	struct list_head *lses;
    454 	if (down)
    455 		foreach (struct session, ses, lses, sessions)
    456 			if (ses == down->ses)
    457 				return ses;
    458 	if (!list_empty(sessions))
    459 		return list_struct(sessions.next, struct session);
    460 	return NULL;
    461 }
    462 
    463 static int
    464 close_download_file(struct download *down)
    465 {
    466 	int rs;
    467 	if (down->handle != -1) {
    468 		EINTRLOOP(rs, ftruncate(down->handle,
    469 		                        down->last_pos - down->file_shift));
    470 		EINTRLOOP(rs, close(down->handle));
    471 		down->handle = -1;
    472 		if (rs)
    473 			return -1;
    474 	}
    475 	return 0;
    476 }
    477 
    478 static void
    479 delete_download_file(struct download *down)
    480 {
    481 	int rs;
    482 	unsigned char *file = stracpy(down->orig_file);
    483 	unsigned char *wd = get_cwd();
    484 	set_cwd(down->cwd);
    485 	while (1) {
    486 		unsigned char *f = translate_download_file(file);
    487 		EINTRLOOP(rs, unlink(cast_const_char f));
    488 		free(f);
    489 		if (!strcmp(cast_const_char file, cast_const_char down->file))
    490 			break;
    491 		increase_download_file(&file);
    492 	}
    493 	free(file);
    494 	if (wd) {
    495 		set_cwd(wd);
    496 		free(wd);
    497 	}
    498 }
    499 
    500 static void
    501 abort_download(void *down_)
    502 {
    503 	struct download *down = (struct download *)down_;
    504 	unregister_bottom_half(abort_download, down);
    505 	unregister_bottom_half(abort_and_delete_download, down);
    506 	unregister_bottom_half(undisplay_download, down);
    507 
    508 	if (down->win)
    509 		delete_window(down->win);
    510 	if (down->ask)
    511 		delete_window(down->ask);
    512 	if (down->stat.state >= 0)
    513 		change_connection(&down->stat, NULL, PRI_CANCEL);
    514 	free(down->url);
    515 	close_download_file(down);
    516 	if (down->prog) {
    517 		delete_download_file(down);
    518 		free(down->prog);
    519 	}
    520 	free(down->cwd);
    521 	free(down->orig_file);
    522 	free(down->file);
    523 	del_from_list(down);
    524 	free(down);
    525 }
    526 
    527 static void
    528 abort_and_delete_download(void *down_)
    529 {
    530 	struct download *down = (struct download *)down_;
    531 	abort_download(down);
    532 }
    533 
    534 int
    535 test_abort_downloads_to_file(unsigned char *file, unsigned char *cwd,
    536                              int abort_downloads)
    537 {
    538 	int ret = 0;
    539 	struct download *down = NULL;
    540 	struct list_head *ldown;
    541 	foreach (struct download, down, ldown, downloads) {
    542 		if (strcmp(cast_const_char down->cwd, cast_const_char cwd)) {
    543 			if (file[0] == '/')
    544 				goto abs;
    545 			continue;
    546 		}
    547 abs:
    548 		if (!strcmp(cast_const_char down->file, cast_const_char file)
    549 		    || !strcmp(cast_const_char down->orig_file,
    550 		               cast_const_char file)) {
    551 			ret = 1;
    552 			if (!abort_downloads)
    553 				break;
    554 			ldown = ldown->prev;
    555 			abort_download(down);
    556 		}
    557 	}
    558 	return ret;
    559 }
    560 
    561 static void
    562 undisplay_download(void *down_)
    563 {
    564 	struct download *down = (struct download *)down_;
    565 	if (down->win)
    566 		delete_window(down->win);
    567 }
    568 
    569 static int
    570 dlg_abort_download(struct dialog_data *dlg, struct dialog_item_data *di)
    571 {
    572 	register_bottom_half(abort_download, dlg->dlg->udata);
    573 	return 0;
    574 }
    575 
    576 static int
    577 dlg_abort_and_delete_download(struct dialog_data *dlg,
    578                               struct dialog_item_data *di)
    579 {
    580 	register_bottom_half(abort_and_delete_download, dlg->dlg->udata);
    581 	return 0;
    582 }
    583 
    584 static int
    585 dlg_undisplay_download(struct dialog_data *dlg, struct dialog_item_data *di)
    586 {
    587 	register_bottom_half(undisplay_download, dlg->dlg->udata);
    588 	return 0;
    589 }
    590 
    591 static void
    592 download_abort_function(struct dialog_data *dlg)
    593 {
    594 	struct download *down = dlg->dlg->udata;
    595 	down->win = NULL;
    596 }
    597 
    598 static int
    599 test_percentage(struct status *stat)
    600 {
    601 	return stat->prg->size > 0;
    602 }
    603 
    604 static int
    605 download_meter(int size, struct status *stat)
    606 {
    607 	int m;
    608 	if (!stat->prg->size)
    609 		return 0;
    610 	m = (int)((double)size * (double)stat->prg->pos
    611 	          / (double)stat->prg->size);
    612 	if (m < 0)
    613 		m = 0;
    614 	if (m > size)
    615 		m = size;
    616 	return m;
    617 }
    618 
    619 unsigned char *
    620 download_percentage(struct download *down, int pad)
    621 {
    622 	unsigned char *s;
    623 	size_t l;
    624 	int perc;
    625 	struct status *stat = &down->stat;
    626 	if (stat->state != S_TRANS || !test_percentage(stat))
    627 		return stracpy(cast_uchar "");
    628 	s = NULL;
    629 	l = 0;
    630 	perc = download_meter(100, stat);
    631 	if (pad) {
    632 		if (perc < 10)
    633 			l = add_chr_to_str(&s, l, ' ');
    634 		if (perc < 100)
    635 			l = add_chr_to_str(&s, l, ' ');
    636 	}
    637 	l = add_num_to_str(&s, l, perc);
    638 	l = add_chr_to_str(&s, l, '%');
    639 	return s;
    640 }
    641 
    642 void
    643 download_window_function(struct dialog_data *dlg)
    644 {
    645 	struct download *down = dlg->dlg->udata;
    646 	struct terminal *term = dlg->win->term;
    647 	int max = 0, min = 0;
    648 	int w, x, y;
    649 	int t = 0;
    650 	int show_percentage = 0;
    651 	unsigned char *m, *u;
    652 	struct status *stat = &down->stat;
    653 	redraw_below_window(dlg->win);
    654 	down->win = dlg->win;
    655 	if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
    656 		size_t l;
    657 		m = NULL;
    658 		t = 1;
    659 		l = add_to_str(&m, 0,
    660 		               get_text_translation(TEXT_(T_RECEIVED), term));
    661 		l = add_chr_to_str(&m, l, ' ');
    662 		l = add_xnum_to_str(&m, l, stat->prg->pos);
    663 		if (stat->prg->size >= 0) {
    664 			l = add_chr_to_str(&m, l, ' ');
    665 			l = add_to_str(&m, l,
    666 			               get_text_translation(TEXT_(T_OF), term));
    667 			l = add_chr_to_str(&m, l, ' ');
    668 			l = add_xnum_to_str(&m, l, stat->prg->size);
    669 			l = add_chr_to_str(&m, l, ' ');
    670 		}
    671 		l = add_chr_to_str(&m, l, '\n');
    672 		if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
    673 			l = add_to_str(
    674 			    &m, l,
    675 			    get_text_translation(TEXT_(T_AVERAGE_SPEED), term));
    676 		else
    677 			l = add_to_str(
    678 			    &m, l, get_text_translation(TEXT_(T_SPEED), term));
    679 		l = add_chr_to_str(&m, l, ' ');
    680 		l = add_xnum_to_str(&m, l,
    681 		                    (long long)stat->prg->loaded * 10
    682 		                        / (stat->prg->elapsed / 100));
    683 		l = add_to_str(&m, l, cast_uchar "/s");
    684 		if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME) {
    685 			l = add_to_str(&m, l, cast_uchar ", ");
    686 			l = add_to_str(
    687 			    &m, l,
    688 			    get_text_translation(TEXT_(T_CURRENT_SPEED), term));
    689 			l = add_chr_to_str(&m, l, ' ');
    690 			l = add_xnum_to_str(
    691 			    &m, l,
    692 			    stat->prg->cur_loaded
    693 				/ (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000));
    694 			l = add_to_str(&m, l, cast_uchar "/s");
    695 		}
    696 		l = add_chr_to_str(&m, l, '\n');
    697 		l = add_to_str(
    698 		    &m, l, get_text_translation(TEXT_(T_ELAPSED_TIME), term));
    699 		l = add_chr_to_str(&m, l, ' ');
    700 		l = add_time_to_str(&m, l, stat->prg->elapsed / 1000);
    701 		if (stat->prg->size >= 0 && stat->prg->loaded > 0) {
    702 			l = add_to_str(&m, l, cast_uchar ", ");
    703 			l = add_to_str(&m, l,
    704 			               get_text_translation(
    705 					   TEXT_(T_ESTIMATED_TIME), term));
    706 			l = add_chr_to_str(&m, l, ' ');
    707 			/*add_time_to_str(&m, &l, stat->prg->elapsed / 1000 *
    708 			 * stat->prg->size / stat->prg->loaded * 1000 -
    709 			 * stat->prg->elapsed);*/
    710 			/*add_time_to_str(&m, &l, (stat->prg->size -
    711 			 * stat->prg->pos) / ((longlong)stat->prg->loaded * 10 /
    712 			 * (stat->prg->elapsed / 100)));*/
    713 			l = add_time_to_str(
    714 			    &m, l,
    715 			    (uttime)((stat->prg->size - stat->prg->pos)
    716 			             / ((double)stat->prg->loaded * 1000
    717 			                / stat->prg->elapsed)));
    718 		}
    719 	} else
    720 		m = stracpy(
    721 		    get_text_translation(get_err_msg(stat->state), term));
    722 	show_percentage = t && test_percentage(stat);
    723 	u = display_url(term, down->url, 1);
    724 	max_text_width(term, u, &max, AL_LEFT);
    725 	min_text_width(term, u, &min, AL_LEFT);
    726 	max_text_width(term, m, &max, AL_LEFT);
    727 	min_text_width(term, m, &min, AL_LEFT);
    728 	max_buttons_width(term, dlg->items, dlg->n, &max);
    729 	min_buttons_width(term, dlg->items, dlg->n, &min);
    730 	w = dlg->win->term->x * 9 / 10 - 2 * DIALOG_LB;
    731 	if (w < min)
    732 		w = min;
    733 	if (w > dlg->win->term->x - 2 * DIALOG_LB)
    734 		w = dlg->win->term->x - 2 * DIALOG_LB;
    735 	if (show_percentage) {
    736 		if (w < DOWN_DLG_MIN)
    737 			w = DOWN_DLG_MIN;
    738 	} else {
    739 		if (w > max)
    740 			w = max;
    741 	}
    742 	if (w < 1)
    743 		w = 1;
    744 	y = 0;
    745 	dlg_format_text(dlg, NULL, u, 0, &y, w, NULL, COLOR_DIALOG_TEXT,
    746 	                AL_LEFT);
    747 	y++;
    748 	if (show_percentage)
    749 		y += 2;
    750 	dlg_format_text(dlg, NULL, m, 0, &y, w, NULL, COLOR_DIALOG_TEXT,
    751 	                AL_LEFT);
    752 	y++;
    753 	dlg_format_buttons(dlg, NULL, dlg->items, dlg->n, 0, &y, w, NULL,
    754 	                   AL_CENTER);
    755 	dlg->xw = w + 2 * DIALOG_LB;
    756 	dlg->yw = y + 2 * DIALOG_TB;
    757 	center_dlg(dlg);
    758 	draw_dlg(dlg);
    759 	y = dlg->y + DIALOG_TB + 1;
    760 	x = dlg->x + DIALOG_LB;
    761 	dlg_format_text(dlg, term, u, x, &y, w, NULL, COLOR_DIALOG_TEXT,
    762 	                AL_LEFT);
    763 	if (show_percentage) {
    764 		unsigned char *q;
    765 		int p = w - 6;
    766 		y++;
    767 		set_only_char(term, x, y, '[', 0);
    768 		set_only_char(term, x + p + 1, y, ']', 0);
    769 		fill_area(term, x + 1, y, download_meter(p, stat), 1,
    770 		          CHAR_DIALOG_METER, COLOR_DIALOG_METER);
    771 		q = download_percentage(down, 1);
    772 		print_text(term, x + p + 2, y, (int)strlen(cast_const_char q),
    773 		           q, COLOR_DIALOG_TEXT);
    774 		free(q);
    775 		y++;
    776 	}
    777 	y++;
    778 	dlg_format_text(dlg, term, m, x, &y, w, NULL, COLOR_DIALOG_TEXT,
    779 	                AL_LEFT);
    780 	y++;
    781 	dlg_format_buttons(dlg, term, dlg->items, dlg->n, x, &y, w, NULL,
    782 	                   AL_CENTER);
    783 	free(u);
    784 	free(m);
    785 }
    786 
    787 void
    788 display_download(struct terminal *term, void *down_, void *ses_)
    789 {
    790 	struct download *down = (struct download *)down_;
    791 	struct session *ses = (struct session *)ses_;
    792 	struct dialog *dlg;
    793 	struct download *dd = NULL;
    794 	struct list_head *ldd;
    795 	foreach (struct download, dd, ldd, downloads)
    796 		if (dd == down)
    797 			goto found;
    798 	return;
    799 found:
    800 	dlg =
    801 	    mem_calloc(sizeof(struct dialog) + 4 * sizeof(struct dialog_item));
    802 	undisplay_download(down);
    803 	down->ses = ses;
    804 	dlg->title = TEXT_(T_DOWNLOAD);
    805 	dlg->fn = download_window_function;
    806 	dlg->abort = download_abort_function;
    807 	dlg->udata = down;
    808 	dlg->align = AL_CENTER;
    809 	dlg->items[0].type = D_BUTTON;
    810 	dlg->items[0].gid = B_ENTER | B_ESC;
    811 	dlg->items[0].fn = dlg_undisplay_download;
    812 	dlg->items[0].text = TEXT_(T_BACKGROUND);
    813 	dlg->items[1].type = D_BUTTON;
    814 	dlg->items[1].gid = 0;
    815 	dlg->items[1].fn = dlg_abort_download;
    816 	dlg->items[1].text = TEXT_(T_ABORT);
    817 	if (!down->prog) {
    818 		dlg->items[2].type = D_BUTTON;
    819 		dlg->items[2].gid = 0;
    820 		dlg->items[2].fn = dlg_abort_and_delete_download;
    821 		dlg->items[2].text = TEXT_(T_ABORT_AND_DELETE_FILE);
    822 		dlg->items[3].type = D_END;
    823 	} else {
    824 		dlg->items[2].type = D_END;
    825 	}
    826 	do_dialog(term, dlg, getml(dlg, NULL));
    827 }
    828 
    829 time_t
    830 parse_http_date(unsigned char *date) /* this functions is bad !!! */
    831 {
    832 	static unsigned char *months[12] = {
    833 		cast_uchar "Jan", cast_uchar "Feb", cast_uchar "Mar",
    834 		cast_uchar "Apr", cast_uchar "May", cast_uchar "Jun",
    835 		cast_uchar "Jul", cast_uchar "Aug", cast_uchar "Sep",
    836 		cast_uchar "Oct", cast_uchar "Nov", cast_uchar "Dec"
    837 	};
    838 
    839 	time_t t = 0;
    840 	/* Mon, 03 Jan 2000 21:29:33 GMT */
    841 	int y;
    842 	struct tm tm;
    843 	memset(&tm, 0, sizeof(struct tm));
    844 
    845 	date = cast_uchar strchr(cast_const_char date, ' ');
    846 	if (!date)
    847 		return 0;
    848 	date++;
    849 	if (*date >= '0' && *date <= '9') {
    850 		/* Sun, 06 Nov 1994 08:49:37 GMT */
    851 		/* Sunday, 06-Nov-94 08:49:37 GMT */
    852 		y = 0;
    853 		if (date[0] < '0' || date[0] > '9')
    854 			return 0;
    855 		if (date[1] < '0' || date[1] > '9')
    856 			return 0;
    857 		tm.tm_mday = (date[0] - '0') * 10 + date[1] - '0';
    858 		date += 2;
    859 		if (*date != ' ' && *date != '-')
    860 			return 0;
    861 		date += 1;
    862 		for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++)
    863 			if (!casecmp(date, months[tm.tm_mon], 3))
    864 				goto f1;
    865 		return 0;
    866 f1:
    867 		date += 3;
    868 		if (*date == ' ') {
    869 			/* Sun, 06 Nov 1994 08:49:37 GMT */
    870 			date++;
    871 			if (date[0] < '0' || date[0] > '9')
    872 				return 0;
    873 			if (date[1] < '0' || date[1] > '9')
    874 				return 0;
    875 			if (date[2] < '0' || date[2] > '9')
    876 				return 0;
    877 			if (date[3] < '0' || date[3] > '9')
    878 				return 0;
    879 			tm.tm_year =
    880 			    (date[0] - '0') * 1000 + (date[1] - '0') * 100
    881 			    + (date[2] - '0') * 10 + date[3] - '0' - 1900;
    882 			date += 4;
    883 		} else if (*date == '-') {
    884 			/* Sunday, 06-Nov-94 08:49:37 GMT */
    885 			date++;
    886 			if (date[0] < '0' || date[0] > '9')
    887 				return 0;
    888 			if (date[1] < '0' || date[1] > '9')
    889 				return 0;
    890 			tm.tm_year = (date[0] >= '7' ? 1900 : 2000)
    891 			             + (date[0] - '0') * 10 + date[1] - '0'
    892 			             - 1900;
    893 			date += 2;
    894 		} else
    895 			return 0;
    896 		if (*date != ' ')
    897 			return 0;
    898 		date++;
    899 	} else {
    900 		/* Sun Nov  6 08:49:37 1994 */
    901 		y = 1;
    902 		for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++)
    903 			if (!casecmp(date, months[tm.tm_mon], 3))
    904 				goto f2;
    905 		return 0;
    906 f2:
    907 		date += 3;
    908 		while (*date == ' ')
    909 			date++;
    910 		if (date[0] < '0' || date[0] > '9')
    911 			return 0;
    912 		tm.tm_mday = date[0] - '0';
    913 		date++;
    914 		if (*date != ' ') {
    915 			if (date[0] < '0' || date[0] > '9')
    916 				return 0;
    917 			tm.tm_mday = tm.tm_mday * 10 + date[0] - '0';
    918 			date++;
    919 		}
    920 		if (*date != ' ')
    921 			return 0;
    922 		date++;
    923 	}
    924 
    925 	if (date[0] < '0' || date[0] > '9')
    926 		return 0;
    927 	if (date[1] < '0' || date[1] > '9')
    928 		return 0;
    929 	tm.tm_hour = (date[0] - '0') * 10 + date[1] - '0';
    930 	date += 2;
    931 	if (*date != ':')
    932 		return 0;
    933 	date++;
    934 	if (date[0] < '0' || date[0] > '9')
    935 		return 0;
    936 	if (date[1] < '0' || date[1] > '9')
    937 		return 0;
    938 	tm.tm_min = (date[0] - '0') * 10 + date[1] - '0';
    939 	date += 2;
    940 	if (*date != ':')
    941 		return 0;
    942 	date++;
    943 	if (date[0] < '0' || date[0] > '9')
    944 		return 0;
    945 	if (date[1] < '0' || date[1] > '9')
    946 		return 0;
    947 	tm.tm_sec = (date[0] - '0') * 10 + date[1] - '0';
    948 	date += 2;
    949 	if (y) {
    950 		if (*date != ' ')
    951 			return 0;
    952 		date++;
    953 		if (date[0] < '0' || date[0] > '9')
    954 			return 0;
    955 		if (date[1] < '0' || date[1] > '9')
    956 			return 0;
    957 		if (date[2] < '0' || date[2] > '9')
    958 			return 0;
    959 		if (date[3] < '0' || date[3] > '9')
    960 			return 0;
    961 		tm.tm_year = (date[0] - '0') * 1000 + (date[1] - '0') * 100
    962 		             + (date[2] - '0') * 10 + date[3] - '0' - 1900;
    963 		date += 4;
    964 	}
    965 	if (*date != ' ' && *date)
    966 		return 0;
    967 
    968 	t = mktime(&tm);
    969 	if (t == (time_t)-1)
    970 		return 0;
    971 	return t;
    972 }
    973 
    974 static void
    975 download_file_error(struct download *down, int err)
    976 {
    977 	struct session *ses = get_download_ses(down);
    978 	if (ses) {
    979 		unsigned char *emsg = stracpy(err ? cast_uchar strerror(err)
    980 		                                  : cast_uchar "Zero returned");
    981 		unsigned char *msg = stracpy(down->file);
    982 		msg_box(ses->term, getml(msg, emsg, NULL),
    983 		        TEXT_(T_DOWNLOAD_ERROR), AL_CENTER,
    984 		        TEXT_(T_COULD_NOT_WRITE_TO_FILE), cast_uchar " ", msg,
    985 		        cast_uchar ": ", emsg, MSG_BOX_END, NULL, 1,
    986 		        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
    987 	}
    988 }
    989 
    990 static int
    991 download_write(struct download *down, void *ptr, off_t to_write)
    992 {
    993 	int w;
    994 	int err;
    995 	if (to_write != (int)to_write || (int)to_write < 0)
    996 		to_write = INT_MAX;
    997 try_write_again:
    998 	w = hard_write(down->handle, ptr, (int)to_write);
    999 	if (w >= 0)
   1000 		err = 0;
   1001 	else
   1002 		err = errno;
   1003 	if (w <= -!to_write) {
   1004 #ifdef EFBIG
   1005 		if (err == EFBIG && !down->prog) {
   1006 			if (to_write > 1) {
   1007 				to_write >>= 1;
   1008 				goto try_write_again;
   1009 			}
   1010 			if (down->last_pos == down->file_shift)
   1011 				goto no_e2big;
   1012 			if (close_download_file(down)) {
   1013 				download_file_error(down, errno);
   1014 				return -1;
   1015 			}
   1016 			increase_download_file(&down->file);
   1017 			if ((down->handle = create_download_file(
   1018 				 get_download_ses(down), down->cwd, down->file,
   1019 				 0, down->last_pos - down->file_shift))
   1020 			    < 0)
   1021 				return -1;
   1022 			down->file_shift = down->last_pos;
   1023 			goto try_write_again;
   1024 no_e2big:;
   1025 		}
   1026 #endif
   1027 		download_file_error(down, err);
   1028 		return -1;
   1029 	}
   1030 	down->last_pos += w;
   1031 	down->downloaded_something = 1;
   1032 	return 0;
   1033 }
   1034 
   1035 static void
   1036 download_data(struct status *stat, void *down_)
   1037 {
   1038 	struct download *down = (struct download *)down_;
   1039 	struct cache_entry *ce;
   1040 	struct fragment *frag = NULL;
   1041 	struct list_head *lfrag;
   1042 	int rs;
   1043 	if (!(ce = stat->ce))
   1044 		goto end_store;
   1045 	if (stat->state >= S_WAIT && stat->state < S_TRANS)
   1046 		goto end_store;
   1047 	if (!down->remotetime && ce->last_modified)
   1048 		down->remotetime = parse_http_date(ce->last_modified);
   1049 	if (!down->downloaded_something) {
   1050 		unsigned char *enc;
   1051 		if (ce->redirect) {
   1052 			if (down->redirect_cnt++ < MAX_REDIRECTS) {
   1053 				unsigned char *u;
   1054 				unsigned char *prev_down_url;
   1055 				int cache, allow_flags;
   1056 				if (stat->state >= 0)
   1057 					change_connection(&down->stat, NULL,
   1058 					                  PRI_CANCEL);
   1059 				u = join_urls(down->url, ce->redirect);
   1060 				extract_position(u);
   1061 				prev_down_url = down->url;
   1062 				down->url = u;
   1063 				down->stat.state = S_WAIT_REDIR;
   1064 				if (down->win) {
   1065 					struct links_event ev = { EV_REDRAW, 0,
   1066 						                  0, 0 };
   1067 					ev.x = down->win->term->x;
   1068 					ev.y = down->win->term->y;
   1069 					down->win->handler(down->win, &ev, 0);
   1070 				}
   1071 				cache = NC_CACHE;
   1072 				if (!strcmp(cast_const_char down->url,
   1073 				            cast_const_char prev_down_url)
   1074 				    || down->redirect_cnt
   1075 				           >= MAX_CACHED_REDIRECTS)
   1076 					cache = NC_RELOAD;
   1077 				allow_flags = get_allow_flags(prev_down_url);
   1078 				free(prev_down_url);
   1079 				load_url(down->url, NULL, &down->stat,
   1080 				         PRI_DOWNLOAD, cache, 1, allow_flags,
   1081 				         down->last_pos);
   1082 				return;
   1083 			} else {
   1084 				if (stat->state >= 0)
   1085 					change_connection(&down->stat, NULL,
   1086 					                  PRI_CANCEL);
   1087 				stat->state = S_CYCLIC_REDIRECT;
   1088 				goto end_store;
   1089 			}
   1090 		}
   1091 		enc = get_content_encoding(ce->head, ce->url, !down->prog);
   1092 		if (enc) {
   1093 			down->decompress = 1;
   1094 			free(enc);
   1095 			enc = get_content_encoding(ce->head, ce->url, 1);
   1096 			if (enc) {
   1097 				free(enc);
   1098 				detach_connection(stat, down->last_pos, 1, 0);
   1099 			}
   1100 		} else {
   1101 			down->decompress = 0;
   1102 		}
   1103 	}
   1104 	if (!down->decompress) {
   1105 		foreachback (struct fragment, frag, lfrag, ce->frag)
   1106 			if (frag->offset <= down->last_pos)
   1107 				goto have_frag;
   1108 		foreach (struct fragment, frag, lfrag, ce->frag) {
   1109 have_frag:
   1110 			while (frag->offset <= down->last_pos
   1111 			       && frag->offset + frag->length
   1112 			              > down->last_pos) {
   1113 				if (download_write(
   1114 					down,
   1115 					frag->data
   1116 					    + (down->last_pos - frag->offset),
   1117 					frag->length
   1118 					    - (down->last_pos
   1119 				               - frag->offset))) {
   1120 det_abt:
   1121 					detach_connection(stat, down->last_pos,
   1122 					                  0, 0);
   1123 					abort_download(down);
   1124 					return;
   1125 				}
   1126 			}
   1127 		}
   1128 	}
   1129 	if (!down->decompress)
   1130 		detach_connection(stat, down->last_pos, 0, 0);
   1131 end_store:
   1132 	if (stat->state < 0) {
   1133 		if (down->decompress) {
   1134 			struct session *ses = get_download_ses(down);
   1135 			unsigned char *start;
   1136 			size_t len;
   1137 			int err;
   1138 			get_file_by_term(ses ? ses->term : NULL, ce, &start,
   1139 			                 &len, &err);
   1140 			if (err)
   1141 				goto det_abt;
   1142 			while (down->last_pos < len) {
   1143 				if (download_write(down, start + down->last_pos,
   1144 				                   len - down->last_pos))
   1145 					goto det_abt;
   1146 			}
   1147 		}
   1148 		if (stat->state != S__OK) {
   1149 			unsigned char *t = get_err_msg(stat->state);
   1150 			unsigned char *tt = display_url(
   1151 			    get_download_ses(down)->term, down->url, 1);
   1152 			msg_box(
   1153 			    get_download_ses(down)->term, getml(tt, NULL),
   1154 			    TEXT_(T_DOWNLOAD_ERROR), AL_CENTER,
   1155 			    TEXT_(T_ERROR_DOWNLOADING), cast_uchar " ", tt,
   1156 			    cast_uchar ":\n\n", t, MSG_BOX_END,
   1157 			    (void *)get_download_ses(down),
   1158 			    1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC /*, TEXT_(T_RETRY), NULL, 0 !!! FIXME: retry */);
   1159 		} else {
   1160 			if (close_download_file(down)) {
   1161 				download_file_error(down, errno);
   1162 			} else if (down->prog) {
   1163 				exec_on_terminal(get_download_ses(down)->term,
   1164 				                 down->prog, down->orig_file,
   1165 				                 !!down->prog_flag_block);
   1166 				free(down->prog);
   1167 				down->prog = NULL;
   1168 			} else if (down->remotetime && download_utime) {
   1169 				struct timeval utv[2];
   1170 				unsigned char *file = stracpy(down->orig_file);
   1171 				unsigned char *wd = get_cwd();
   1172 				set_cwd(down->cwd);
   1173 				utv[0].tv_usec = utv[1].tv_usec = 0;
   1174 				utv[0].tv_sec = utv[1].tv_sec =
   1175 				    down->remotetime;
   1176 				while (1) {
   1177 					unsigned char *f =
   1178 					    translate_download_file(file);
   1179 					EINTRLOOP(rs, utimes(cast_char f, utv));
   1180 					free(f);
   1181 					if (!strcmp(cast_const_char file,
   1182 					            cast_const_char down->file))
   1183 						break;
   1184 					increase_download_file(&file);
   1185 				}
   1186 				free(file);
   1187 				if (wd) {
   1188 					set_cwd(wd);
   1189 					free(wd);
   1190 				}
   1191 			}
   1192 		}
   1193 		abort_download(down);
   1194 		return;
   1195 	}
   1196 	if (down->win) {
   1197 		struct links_event ev = { EV_REDRAW, 0, 0, 0 };
   1198 		ev.x = down->win->term->x;
   1199 		ev.y = down->win->term->y;
   1200 		down->win->handler(down->win, &ev, 0);
   1201 	}
   1202 }
   1203 
   1204 unsigned char *
   1205 translate_download_file(unsigned char *fi)
   1206 {
   1207 	unsigned char *file = stracpy(cast_uchar "");
   1208 	unsigned char *h;
   1209 	if (fi[0] == '~' && dir_sep(fi[1]) && (h = cast_uchar getenv("HOME"))) {
   1210 		add_to_strn(&file, h);
   1211 		fi++;
   1212 	}
   1213 	add_to_strn(&file, fi);
   1214 	return file;
   1215 }
   1216 
   1217 int
   1218 create_download_file(struct session *ses, unsigned char *cwd, unsigned char *fi,
   1219                      int mode, off_t siz)
   1220 {
   1221 	unsigned char *wd;
   1222 	unsigned char *file;
   1223 	int h;
   1224 #ifdef NO_FILE_SECURITY
   1225 	int perm = 0666;
   1226 #else
   1227 	int perm = mode & CDF_RESTRICT_PERMISSION ? 0600 : 0666;
   1228 #endif
   1229 	wd = get_cwd();
   1230 	set_cwd(cwd);
   1231 	file = translate_download_file(fi);
   1232 	h = c_open3(file,
   1233 	            O_CREAT | O_NOCTTY | O_WRONLY
   1234 	                | (mode & CDF_NOTRUNC ? 0 : O_TRUNC)
   1235 	                | (mode & CDF_EXCL ? O_EXCL : 0),
   1236 	            perm);
   1237 	if (h == -1) {
   1238 		unsigned char *msg, *msge;
   1239 		int errn = errno;
   1240 		if (errn == EEXIST && mode & CDF_NO_POPUP_ON_EEXIST) {
   1241 			h = -2;
   1242 			goto x;
   1243 		}
   1244 		if (!ses)
   1245 			goto x;
   1246 		msg = stracpy(file);
   1247 		msge = stracpy(cast_uchar strerror(errn));
   1248 		msg_box(ses->term, getml(msg, msge, NULL),
   1249 		        TEXT_(T_DOWNLOAD_ERROR), AL_CENTER,
   1250 		        TEXT_(T_COULD_NOT_CREATE_FILE), cast_uchar " ", msg,
   1251 		        cast_uchar ": ", msge, MSG_BOX_END, NULL, 1,
   1252 		        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   1253 		goto x;
   1254 	}
   1255 x:
   1256 	free(file);
   1257 	if (wd) {
   1258 		set_cwd(wd);
   1259 		free(wd);
   1260 	}
   1261 	return h;
   1262 }
   1263 
   1264 static int
   1265 create_or_append_download_file(struct session *ses, unsigned char *cwd,
   1266                                unsigned char *fi, int mode, int *hp,
   1267                                unsigned char **xl_file, off_t *last_pos,
   1268                                off_t *file_shift)
   1269 {
   1270 	int rs;
   1271 	int down_flags = mode == DOWNLOAD_CONTINUE    ? CDF_NOTRUNC
   1272 	                 : mode == DOWNLOAD_OVERWRITE ? 0
   1273 	                                              : CDF_EXCL;
   1274 
   1275 	*xl_file = stracpy(fi);
   1276 	*last_pos = 0;
   1277 	*file_shift = 0;
   1278 
   1279 retry_next_file:
   1280 	test_abort_downloads_to_file(*xl_file, ses->term->cwd, 1);
   1281 
   1282 	if ((*hp = create_download_file(ses, ses->term->cwd, *xl_file,
   1283 	                                down_flags, 0))
   1284 	    < 0)
   1285 		goto err_free;
   1286 
   1287 	if (mode == DOWNLOAD_CONTINUE) {
   1288 		off_t ls;
   1289 		unsigned char *f;
   1290 		struct stat st;
   1291 
   1292 		EINTRLOOP(rs, fstat(*hp, &st));
   1293 		if (rs || !S_ISREG(st.st_mode))
   1294 			goto ret_0;
   1295 
   1296 		EINTRLOOP(ls, lseek(*hp, 0, SEEK_END));
   1297 		if (ls == (off_t)-1) {
   1298 			unsigned char *emsg =
   1299 			    stracpy(cast_uchar strerror(errno));
   1300 			unsigned char *msg = stracpy(*xl_file);
   1301 			msg_box(ses->term, getml(msg, emsg, NULL),
   1302 			        TEXT_(T_DOWNLOAD_ERROR), AL_CENTER,
   1303 			        TEXT_(T_ERROR_CALLING_LSEEK_ON_FILE),
   1304 			        cast_uchar " ", msg, cast_uchar ": ", emsg,
   1305 			        MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL),
   1306 			        msg_box_null, B_ENTER | B_ESC);
   1307 			goto err_close;
   1308 		}
   1309 		if ((off_t)(0UL + *last_pos + ls) < 0
   1310 		    || (off_t)(0UL + *last_pos + ls) < *last_pos) {
   1311 			unsigned char *msg1 = stracpy(fi);
   1312 			unsigned char *msg2 = stracpy(*xl_file);
   1313 			msg_box(ses->term, getml(msg1, msg2, NULL),
   1314 			        TEXT_(T_DOWNLOAD_ERROR), AL_CENTER,
   1315 			        TEXT_(T_TOO_LARGE_FILE_SEQUENCE),
   1316 			        cast_uchar " ", msg1, cast_uchar " - ", msg2,
   1317 			        MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL),
   1318 			        msg_box_null, B_ENTER | B_ESC);
   1319 			goto err_close;
   1320 		}
   1321 		*last_pos += ls;
   1322 
   1323 		f = stracpy(*xl_file);
   1324 		increase_download_file(&f);
   1325 		EINTRLOOP(rs, stat(cast_const_char f, &st));
   1326 		if (rs || !S_ISREG(st.st_mode)) {
   1327 			free(f);
   1328 			goto ret_0;
   1329 		}
   1330 		EINTRLOOP(rs, close(*hp));
   1331 		free(*xl_file);
   1332 		*xl_file = f;
   1333 		*file_shift = *last_pos;
   1334 		goto retry_next_file;
   1335 	}
   1336 
   1337 ret_0:
   1338 	return 0;
   1339 
   1340 err_close:
   1341 	EINTRLOOP(rs, close(*hp));
   1342 err_free:
   1343 	free(*xl_file);
   1344 	return -1;
   1345 }
   1346 
   1347 static void
   1348 increase_download_file(unsigned char **f)
   1349 {
   1350 	unsigned char *p = NULL, *pp = *f;
   1351 	unsigned char *q;
   1352 	while ((pp = cast_uchar strstr(cast_const_char pp, ".part-")))
   1353 		p = pp += 6;
   1354 	if (!p || !*p) {
   1355 no_suffix:
   1356 		add_to_strn(f, cast_uchar ".part-2");
   1357 		return;
   1358 	}
   1359 	for (q = p; *q; q++)
   1360 		if (*q < '0' || *q > '9')
   1361 			goto no_suffix;
   1362 	for (q--; q >= p; q--) {
   1363 		if (*q < '9') {
   1364 			(*q)++;
   1365 			return;
   1366 		}
   1367 		*q = '0';
   1368 	}
   1369 	*p = '1';
   1370 	add_to_strn(f, cast_uchar "0");
   1371 }
   1372 
   1373 static unsigned char *
   1374 get_temp_name(unsigned char *url, unsigned char *head)
   1375 {
   1376 	size_t nl;
   1377 	unsigned char *name, *fn, *fnx;
   1378 	unsigned char *nm;
   1379 	unsigned char *directory = NULL;
   1380 	nm = cast_uchar tempnam(cast_const_char directory, "links");
   1381 	if (!nm)
   1382 		return NULL;
   1383 	name = NULL;
   1384 	nl = add_to_str(&name, 0, nm);
   1385 	free(nm);
   1386 	fn = get_filename_from_url(url, head, 1);
   1387 	fnx = cast_uchar strchr(cast_const_char fn, '.');
   1388 	if (fnx) {
   1389 		unsigned char *s;
   1390 		s = stracpy(fnx);
   1391 		check_shell_security(&s);
   1392 		nl = add_to_str(&name, nl, s);
   1393 		free(s);
   1394 	}
   1395 	free(fn);
   1396 	return name;
   1397 }
   1398 
   1399 unsigned char *
   1400 subst_file(unsigned char *prog, unsigned char *file, int cyg_subst)
   1401 {
   1402 	unsigned char *orig_prog = prog;
   1403 	unsigned char *nn;
   1404 	unsigned char *n = NULL;
   1405 	size_t l = 0;
   1406 	while (*prog) {
   1407 		int p;
   1408 		for (p = 0; prog[p] && prog[p] != '%'; p++)
   1409 			;
   1410 		l = add_bytes_to_str(&n, l, prog, p);
   1411 		prog += p;
   1412 		if (*prog == '%') {
   1413 			if (cyg_subst) {
   1414 				unsigned char *conv =
   1415 				    os_conv_to_external_path(file, orig_prog);
   1416 				l = add_to_str(&n, l, conv);
   1417 				free(conv);
   1418 			} else
   1419 				l = add_to_str(&n, l, file);
   1420 			prog++;
   1421 		}
   1422 	}
   1423 	nn = os_fixup_external_program(n);
   1424 	free(n);
   1425 	return nn;
   1426 }
   1427 
   1428 void
   1429 start_download(struct session *ses, unsigned char *file, int mode)
   1430 {
   1431 	struct download *down;
   1432 	int h;
   1433 	unsigned char *url = ses->dn_url;
   1434 	unsigned char *xl_file;
   1435 	off_t last_pos = 0, file_shift = 0;
   1436 
   1437 	if (!url)
   1438 		return;
   1439 	extract_position(url);
   1440 
   1441 	if (create_or_append_download_file(ses, ses->term->cwd, file, mode, &h,
   1442 	                                   &xl_file, &last_pos, &file_shift)
   1443 	    < 0)
   1444 		return;
   1445 
   1446 	down = mem_calloc(sizeof(struct download));
   1447 	down->url = stracpy(url);
   1448 	down->stat.end = download_data;
   1449 	down->stat.data = down;
   1450 	down->decompress = 0;
   1451 	down->last_pos = last_pos;
   1452 	down->file_shift = file_shift;
   1453 	down->cwd = stracpy(ses->term->cwd);
   1454 	down->orig_file = stracpy(file);
   1455 	down->file = xl_file;
   1456 	down->handle = h;
   1457 	down->ses = ses;
   1458 	down->remotetime = 0;
   1459 	add_to_list(downloads, down);
   1460 	load_url(url, NULL, &down->stat, PRI_DOWNLOAD, NC_CACHE, 1,
   1461 	         ses->dn_allow_flags, down->last_pos);
   1462 	display_download(ses->term, down, ses);
   1463 }
   1464 
   1465 void
   1466 abort_all_downloads(void)
   1467 {
   1468 	while (!list_empty(downloads)) {
   1469 		struct download *down =
   1470 		    list_struct(downloads.next, struct download);
   1471 		abort_download(down);
   1472 	}
   1473 }
   1474 
   1475 int
   1476 f_is_finished(struct f_data *f)
   1477 {
   1478 	struct additional_file *af = NULL;
   1479 	struct list_head *laf;
   1480 	if (!f || f->rq->state >= 0)
   1481 		return 0;
   1482 	if (f->fd && f->fd->rq && f->fd->rq->state >= 0)
   1483 		return 0;
   1484 	if (f->af)
   1485 		foreach (struct additional_file, af, laf, f->af->af)
   1486 			if (!af->rq || af->rq->state >= 0)
   1487 				return 0;
   1488 	return 1;
   1489 }
   1490 
   1491 static int
   1492 f_is_cacheable(struct f_data *f)
   1493 {
   1494 	if (!f || f->rq->state >= 0)
   1495 		return 0;
   1496 	if (f->fd && f->fd->rq && f->fd->rq->state >= 0)
   1497 		return 0;
   1498 	return 1;
   1499 }
   1500 
   1501 static int
   1502 f_need_reparse(struct f_data *f)
   1503 {
   1504 	struct additional_file *af = NULL;
   1505 	struct list_head *laf;
   1506 	if (!f || f->rq->state >= 0)
   1507 		return 1;
   1508 	if (f->af)
   1509 		foreach (struct additional_file, af, laf, f->af->af)
   1510 			if (af->need_reparse > 0)
   1511 				return 1;
   1512 	return 0;
   1513 }
   1514 
   1515 static struct f_data *
   1516 format_html(struct f_data_c *fd, struct object_request *rq, unsigned char *url,
   1517             struct document_options *opt, int *cch)
   1518 {
   1519 	struct f_data *f;
   1520 	pr(if (cch) *cch = 0;
   1521 	   if (!rq->ce || !(f = init_formatted(opt))) goto nul; f->fd = fd;
   1522 	   f->ses = fd->ses; f->time_to_get = -get_time();
   1523 	   clone_object(rq, &f->rq); if (f->rq->ce) {
   1524 		   unsigned char *start;
   1525 		   size_t len;
   1526 		   int stl = -1;
   1527 		   struct additional_file *af = NULL;
   1528 		   struct list_head *laf;
   1529 
   1530 		   if (fd->af)
   1531 			   foreach (struct additional_file, af, laf, fd->af->af)
   1532 				   if (af->need_reparse > 0)
   1533 					   af->need_reparse = 0;
   1534 
   1535 		   get_file(rq, &start, &len);
   1536 		   if (len > INT_MAX)
   1537 			   len = INT_MAX;
   1538 		   f->uncacheable = 1;
   1539 		   if (opt->plain == 2) {
   1540 			   start = NULL;
   1541 			   stl =
   1542 			       add_to_str(&start, 0, cast_uchar "<img src=\"");
   1543 			   stl = add_to_str(&start, stl, f->rq->ce->url);
   1544 			   stl = add_to_str(&start, stl, cast_uchar "\">");
   1545 			   len = stl;
   1546 		   }
   1547 		   really_format_html(f->rq->ce, start, start + len, f,
   1548 		                      fd->ses ? fd != fd->ses->screen : 0);
   1549 		   if (stl != -1)
   1550 			   free(start);
   1551 		   f->use_tag = f->rq->ce->count;
   1552 		   if (f->af)
   1553 			   foreach (struct additional_file, af, laf,
   1554 			            f->af->af) {
   1555 				   if (af->rq && af->rq->ce) {
   1556 					   af->use_tag = af->rq->ce->count;
   1557 					   af->use_tag2 = af->rq->ce->count2;
   1558 				   } else {
   1559 					   af->use_tag = 0;
   1560 					   af->use_tag2 = 0;
   1561 				   }
   1562 			   }
   1563 	   } else f->use_tag = 0;
   1564 	   f->time_to_get += get_time();) nul : return NULL;
   1565 	return f;
   1566 }
   1567 
   1568 static void
   1569 count_frames(struct f_data_c *fd, unsigned long *i)
   1570 {
   1571 	struct f_data_c *sub = NULL;
   1572 	struct list_head *lsub;
   1573 	if (!fd)
   1574 		return;
   1575 	if (fd->f_data)
   1576 		(*i)++;
   1577 	foreach (struct f_data_c, sub, lsub, fd->subframes)
   1578 		count_frames(sub, i);
   1579 }
   1580 
   1581 unsigned long
   1582 formatted_info(int type)
   1583 {
   1584 	unsigned long i = 0;
   1585 	struct session *ses = NULL;
   1586 	struct list_head *lses;
   1587 	switch (type) {
   1588 	case CI_FILES:
   1589 		foreach (struct session, ses, lses, sessions)
   1590 			i += list_size(&ses->format_cache);
   1591 		/*-fallthrough*/
   1592 	case CI_LOCKED:
   1593 		foreach (struct session, ses, lses, sessions)
   1594 			count_frames(ses->screen, &i);
   1595 		return i;
   1596 	default:
   1597 		internal("formatted_info: bad request");
   1598 	}
   1599 	return 0;
   1600 }
   1601 
   1602 static void
   1603 f_data_attach(struct f_data_c *fd, struct f_data *f)
   1604 {
   1605 	struct additional_file *af = NULL;
   1606 	struct list_head *laf;
   1607 	f->rq->upcall = fd_loaded;
   1608 	f->rq->data = fd;
   1609 	free_additional_files(&fd->af);
   1610 	fd->af = f->af;
   1611 	if (f->af) {
   1612 		f->af->refcount++;
   1613 		foreachback (struct additional_file, af, laf, f->af->af) {
   1614 			if (af->rq) {
   1615 				af->rq->upcall = fd_loaded;
   1616 				af->rq->data = fd;
   1617 			} else {
   1618 				request_object(
   1619 				    fd->ses->term, af->url, f->rq->url, PRI_IMG,
   1620 				    NC_CACHE, get_allow_flags(f->rq->url),
   1621 				    f->rq->upcall, f->rq->data, &af->rq);
   1622 			}
   1623 		}
   1624 	}
   1625 }
   1626 
   1627 static inline int
   1628 is_format_cache_entry_uptodate(struct f_data *f)
   1629 {
   1630 	struct cache_entry *ce = f->rq->ce;
   1631 	struct additional_file *af = NULL;
   1632 	struct list_head *laf;
   1633 	if (!ce || ce->count != f->use_tag)
   1634 		return 0;
   1635 	if (f->af)
   1636 		foreach (struct additional_file, af, laf, f->af->af) {
   1637 			struct cache_entry *ce = af->rq ? af->rq->ce : NULL;
   1638 			tcount tag = ce ? ce->count : 0;
   1639 			tcount tag2 = ce ? ce->count2 : 0;
   1640 			if (af->need_reparse > 0)
   1641 				if (tag != af->use_tag)
   1642 					return 0;
   1643 			if (af->unknown_image_size)
   1644 				if (tag2 != af->use_tag2)
   1645 					return 0;
   1646 		}
   1647 	return 1;
   1648 }
   1649 
   1650 static void
   1651 detach_f_data(struct f_data **ff)
   1652 {
   1653 	struct f_data *f = *ff;
   1654 	struct f_data_c *fd;
   1655 	if (!f)
   1656 		return;
   1657 	fd = f->fd;
   1658 	*ff = NULL;
   1659 
   1660 	f->fd = NULL;
   1661 	if (f->frame_desc_link || f->uncacheable || !f_is_cacheable(f)
   1662 	    || !is_format_cache_entry_uptodate(f) || !f->ses) {
   1663 		destroy_formatted(f);
   1664 	} else {
   1665 		add_to_list(f->ses->format_cache, f);
   1666 		copy_additional_files(&fd->af); /* break structure sharing */
   1667 	}
   1668 }
   1669 
   1670 int
   1671 shrink_format_cache(int u)
   1672 {
   1673 	static int sc = 0;
   1674 	int scc;
   1675 	int r = 0;
   1676 	int c = 0;
   1677 	struct session *ses = NULL;
   1678 	struct list_head *lses;
   1679 	foreach (struct session, ses, lses, sessions) {
   1680 		struct f_data *f = NULL;
   1681 		struct list_head *lf;
   1682 		foreach (struct f_data, f, lf, ses->format_cache) {
   1683 			if (u == SH_FREE_ALL
   1684 			    || !is_format_cache_entry_uptodate(f)) {
   1685 				lf = lf->prev;
   1686 				del_from_list(f);
   1687 				destroy_formatted(f);
   1688 				r |= ST_SOMETHING_FREED;
   1689 			} else
   1690 				c++;
   1691 		}
   1692 	}
   1693 	if (c > max_format_cache_entries || (c && u == SH_FREE_SOMETHING)) {
   1694 		int sc_cycle = 0;
   1695 		unsigned char freed_in_cycle = 0;
   1696 a:
   1697 		scc = sc++;
   1698 		foreach (struct session, ses, lses, sessions)
   1699 			if (!scc--) {
   1700 				if (!list_empty(ses->format_cache)) {
   1701 					struct f_data *ff =
   1702 					    list_struct(ses->format_cache.prev,
   1703 					                struct f_data);
   1704 					del_from_list(ff);
   1705 					destroy_formatted(ff);
   1706 					r |= ST_SOMETHING_FREED;
   1707 					if (--c <= max_format_cache_entries
   1708 					    || u == SH_FREE_SOMETHING)
   1709 						goto ret;
   1710 					freed_in_cycle = 1;
   1711 				}
   1712 				goto a;
   1713 			}
   1714 		sc = 0;
   1715 		sc_cycle++;
   1716 		if (sc_cycle >= 2 && !freed_in_cycle)
   1717 			goto ret;
   1718 		freed_in_cycle = 0;
   1719 		goto a;
   1720 	}
   1721 ret:
   1722 	return r | (!c ? ST_CACHE_EMPTY : 0);
   1723 }
   1724 
   1725 void
   1726 init_fcache(void)
   1727 {
   1728 	register_cache_upcall(shrink_format_cache, MF_GPI, cast_uchar "format");
   1729 }
   1730 
   1731 static void
   1732 calculate_scrollbars(struct f_data_c *fd, struct f_data *f)
   1733 {
   1734 	fd->hsb = 0;
   1735 	fd->vsb = 0;
   1736 	fd->hsbsize = 0;
   1737 	fd->vsbsize = 0;
   1738 	if (!f)
   1739 		return;
   1740 	if (f->opt.scrolling == SCROLLING_YES) {
   1741 		fd->hsb = 1;
   1742 		fd->vsb = 1;
   1743 	} else if (f->opt.scrolling == SCROLLING_AUTO) {
   1744 x:
   1745 		if (!fd->hsb && f->x > fd->xw - fd->vsb * G_SCROLL_BAR_WIDTH) {
   1746 			fd->hsb = 1;
   1747 			goto x;
   1748 		}
   1749 		if (!fd->vsb && f->y > fd->yw - fd->hsb * G_SCROLL_BAR_WIDTH) {
   1750 			fd->vsb = 1;
   1751 			goto x;
   1752 		}
   1753 	}
   1754 	if (fd->hsb)
   1755 		fd->hsbsize = fd->xw - fd->vsb * G_SCROLL_BAR_WIDTH;
   1756 	if (fd->vsb)
   1757 		fd->vsbsize = fd->yw - fd->hsb * G_SCROLL_BAR_WIDTH;
   1758 	if (fd->hsbsize < 0)
   1759 		fd->hsb = 0;
   1760 	if (fd->vsbsize < 0)
   1761 		fd->vsb = 0;
   1762 }
   1763 
   1764 struct f_data *
   1765 cached_format_html(struct f_data_c *fd, struct object_request *rq,
   1766                    unsigned char *url, struct document_options *opt, int *cch,
   1767                    int report_status)
   1768 {
   1769 	struct session *ses = fd->ses;
   1770 	struct f_data *f = NULL;
   1771 	struct list_head *lf;
   1772 	if (fd->marginwidth != -1) {
   1773 		int marg =
   1774 		    (fd->marginwidth + G_HTML_MARGIN - 1) / G_HTML_MARGIN;
   1775 		if (marg >= 0 && marg < 9)
   1776 			opt->margin = marg;
   1777 	}
   1778 	if (opt->plain == 2) {
   1779 		opt->margin = 0;
   1780 		opt->display_images = 1;
   1781 	}
   1782 	pr(if (ses) {
   1783 		if (fd->f_data
   1784 		    && !strcmp(cast_const_char fd->f_data->rq->url,
   1785 		               cast_const_char url)
   1786 		    && !compare_opt(&fd->f_data->opt, opt)
   1787 		    && is_format_cache_entry_uptodate(fd->f_data)) {
   1788 			f = fd->f_data;
   1789 			goto ret_f;
   1790 		}
   1791 		foreach (struct f_data, f, lf, ses->format_cache) {
   1792 			if (!strcmp(cast_const_char f->rq->url,
   1793 			            cast_const_char url)
   1794 			    && !compare_opt(&f->opt, opt)) {
   1795 				if (!is_format_cache_entry_uptodate(f)) {
   1796 					lf = lf->prev;
   1797 					del_from_list(f);
   1798 					destroy_formatted(f);
   1799 					continue;
   1800 				}
   1801 				detach_f_data(&fd->f_data);
   1802 				del_from_list(f);
   1803 				f->fd = fd;
   1804 				if (cch)
   1805 					*cch = 1;
   1806 				f_data_attach(fd, f);
   1807 				goto ret_f;
   1808 			}
   1809 		}
   1810 	}){};
   1811 	if (ses) {
   1812 		if (report_status || !fd->f_data
   1813 		    || fd->f_data->time_to_get >= DISPLAY_FORMATTING_STATUS
   1814 		    || (rq->ce && rq->ce->length >= 1000000))
   1815 			print_progress(ses, TEXT_(T_FORMATTING_DOCUMENT));
   1816 	}
   1817 	detach_f_data(&fd->f_data);
   1818 	f = format_html(fd, rq, url, opt, cch);
   1819 	if (f)
   1820 		f->fd = fd;
   1821 	shrink_memory(SH_CHECK_QUOTA);
   1822 ret_f:
   1823 	calculate_scrollbars(fd, f);
   1824 	return f;
   1825 }
   1826 
   1827 static void
   1828 create_new_frames(struct f_data_c *fd, struct frameset_desc *fs,
   1829                   struct document_options *o)
   1830 {
   1831 	struct list_head *lloc;
   1832 	struct frame_desc *frm;
   1833 	int c_loc;
   1834 	int x, y;
   1835 	int xp, yp;
   1836 
   1837 	if (list_size(&fd->loc->subframes) != (unsigned long)fs->n) {
   1838 		while (!list_empty(fd->loc->subframes))
   1839 			destroy_location(list_struct(fd->loc->subframes.next,
   1840 			                             struct location));
   1841 		c_loc = 1;
   1842 		lloc = NULL; /* against warning */
   1843 	} else {
   1844 		c_loc = 0;
   1845 		lloc = fd->loc->subframes.next;
   1846 	}
   1847 
   1848 	yp = fd->yp;
   1849 	frm = &fs->f[0];
   1850 	for (y = 0; y < fs->y; y++) {
   1851 		xp = fd->xp;
   1852 		for (x = 0; x < fs->x; x++) {
   1853 			struct f_data_c *nfdc;
   1854 			struct location *loc;
   1855 			nfdc = create_f_data_c(fd->ses, fd);
   1856 			if (c_loc) {
   1857 				loc = new_location();
   1858 				add_to_list_end(fd->loc->subframes, loc);
   1859 				loc->parent = fd->loc;
   1860 				loc->name = stracpy(frm->name);
   1861 				if ((loc->url = stracpy(frm->url)))
   1862 					nfdc->goto_position =
   1863 					    extract_position(loc->url);
   1864 			} else {
   1865 				loc = list_struct(lloc, struct location);
   1866 			}
   1867 			nfdc->xp = xp;
   1868 			nfdc->yp = yp;
   1869 			nfdc->xw = frm->xw;
   1870 			nfdc->yw = frm->yw;
   1871 			nfdc->scrolling = frm->scrolling;
   1872 			nfdc->loc = loc;
   1873 			nfdc->vs = loc->vs;
   1874 			if (frm->marginwidth != -1)
   1875 				nfdc->marginwidth = frm->marginwidth;
   1876 			else
   1877 				nfdc->marginwidth = fd->marginwidth;
   1878 			if (frm->marginheight != -1)
   1879 				nfdc->marginheight = frm->marginheight;
   1880 			else
   1881 				nfdc->marginheight = fd->marginheight;
   1882 			add_to_list_end(fd->subframes, nfdc);
   1883 			if (frm->subframe) {
   1884 				create_new_frames(nfdc, frm->subframe, o);
   1885 				/*nfdc->f_data =
   1886 				 * init_formatted(&fd->f_data->opt);*/
   1887 				nfdc->f_data = init_formatted(o);
   1888 				nfdc->f_data->frame_desc =
   1889 				    copy_frameset_desc(frm->subframe);
   1890 				nfdc->f_data->frame_desc_link = 1;
   1891 			} else {
   1892 				if (fd->depth < HTML_MAX_FRAME_DEPTH && loc->url
   1893 				    && *loc->url) {
   1894 					struct f_data_c *rel = fd;
   1895 					while (rel->parent && !rel->rq)
   1896 						rel = rel->parent;
   1897 					request_object(
   1898 					    fd->ses->term, loc->url,
   1899 					    rel->rq ? rel->rq->url : NULL,
   1900 					    PRI_FRAME, NC_CACHE,
   1901 					    rel->rq
   1902 						? get_allow_flags(rel->rq->url)
   1903 						: 0,
   1904 					    fd_loaded, nfdc, &nfdc->rq);
   1905 				}
   1906 			}
   1907 			xp += frm->xw + 1;
   1908 			frm++;
   1909 			if (!c_loc)
   1910 				lloc = lloc->next;
   1911 		}
   1912 		yp += (frm - 1)->yw + 1;
   1913 	}
   1914 }
   1915 
   1916 static void
   1917 html_interpret(struct f_data_c *fd, int report_status)
   1918 {
   1919 	int i;
   1920 	int oxw;
   1921 	int oyw;
   1922 	int oxp;
   1923 	int oyp;
   1924 	struct f_data_c *sf = NULL;
   1925 	struct list_head *lsf;
   1926 	int cch;
   1927 	struct document_options o;
   1928 	if (!fd->loc)
   1929 		goto d;
   1930 	if (fd->f_data) {
   1931 		oxw = fd->f_data->opt.xw;
   1932 		oyw = fd->f_data->opt.yw;
   1933 		oxp = fd->f_data->opt.xp;
   1934 		oyp = fd->f_data->opt.yp;
   1935 	} else {
   1936 		oxw = oyw = oxp = oyp = -1;
   1937 	}
   1938 	memset(&o, 0, sizeof(struct document_options));
   1939 	ds2do(&fd->ses->ds, &o, fd->ses->term->spec->col);
   1940 	if (!casecmp(fd->loc->url, cast_uchar "file://", 7) && !o.hard_assume)
   1941 		o.assume_cp = 0;
   1942 	if (fd->parent && fd->parent->f_data && !o.hard_assume)
   1943 		o.assume_cp = fd->parent->f_data->cp;
   1944 	o.gamma_stamp = 0;
   1945 	o.plain = fd->vs->plain;
   1946 	if (o.plain == 1 && !o.break_long_lines) {
   1947 		o.xp = 0;
   1948 		o.yp = 0;
   1949 		o.xw = INT_MAX;
   1950 		o.yw = INT_MAX;
   1951 	} else {
   1952 		o.xp = fd->xp;
   1953 		o.yp = fd->yp;
   1954 		o.xw = fd->xw;
   1955 		o.yw = fd->yw;
   1956 	}
   1957 	o.scrolling = fd->scrolling;
   1958 	if (fd->ses->term->spec) {
   1959 		if (!fd->ses->ds.t_ignore_document_color)
   1960 			o.col = fd->ses->term->spec->col;
   1961 		else
   1962 			o.col = 0;
   1963 		o.cp = 0;
   1964 	} else {
   1965 		o.col = 3;
   1966 		o.cp = 0;
   1967 	}
   1968 	if (!(o.framename = fd->loc->name))
   1969 		o.framename = NULL;
   1970 	if (!(fd->f_data = cached_format_html(fd, fd->rq, fd->rq->url, &o, &cch,
   1971 	                                      report_status))) {
   1972 		goto d;
   1973 	}
   1974 
   1975 	/* erase frames if changed */
   1976 	i = (int)list_size(&fd->subframes);
   1977 	if (i != (fd->f_data->frame_desc ? fd->f_data->frame_desc->n : 0)
   1978 	    && (f_is_finished(fd->f_data) || !f_need_reparse(fd->f_data))) {
   1979 rd:
   1980 		foreach (struct f_data_c, sf, lsf, fd->subframes)
   1981 			reinit_f_data_c(sf);
   1982 		free_list(struct f_data_c, fd->subframes);
   1983 
   1984 		/* create new frames */
   1985 		if (fd->f_data->frame_desc)
   1986 			create_new_frames(fd, fd->f_data->frame_desc,
   1987 			                  &fd->f_data->opt);
   1988 	} else {
   1989 		if (fd->f_data->frame_desc && fd->f_data->rq->state < 0) {
   1990 			if (fd->f_data->opt.xw != oxw
   1991 			    || fd->f_data->opt.yw != oyw
   1992 			    || fd->f_data->opt.xp != oxp
   1993 			    || fd->f_data->opt.yp != oyp)
   1994 				goto rd;
   1995 		}
   1996 	}
   1997 
   1998 d:;
   1999 }
   2000 
   2001 void
   2002 html_interpret_recursive(struct f_data_c *f)
   2003 {
   2004 	struct f_data_c *fd = NULL;
   2005 	struct list_head *lfd;
   2006 	if (f->rq)
   2007 		html_interpret(f, 1);
   2008 	foreach (struct f_data_c, fd, lfd, f->subframes)
   2009 		html_interpret_recursive(fd);
   2010 }
   2011 
   2012 /* You get a struct_additionl_file. never mem_free it. When you stop
   2013  * using it, just forget the pointer.
   2014  */
   2015 struct additional_file *
   2016 request_additional_file(struct f_data *f, unsigned char *url_)
   2017 {
   2018 	size_t sl;
   2019 	struct additional_file *af = NULL;
   2020 	struct list_head *laf;
   2021 	unsigned char *url;
   2022 	url = stracpy(url_);
   2023 	extract_position(url);
   2024 	if (!f->af) {
   2025 		if (!(f->af = f->fd->af)) {
   2026 			f->af = f->fd->af =
   2027 			    xmalloc(sizeof(struct additional_files));
   2028 			f->af->refcount = 1;
   2029 			init_list(f->af->af);
   2030 		}
   2031 		f->af->refcount++;
   2032 	}
   2033 	foreach (struct additional_file, af, laf, f->af->af)
   2034 		if (!strcmp(cast_const_char af->url, cast_const_char url)) {
   2035 			free(url);
   2036 			return af;
   2037 		}
   2038 	sl = strlen(cast_const_char url);
   2039 	if (sl > INT_MAX - sizeof(struct additional_file))
   2040 		overalloc();
   2041 	af = xmalloc(sizeof(struct additional_file) + sl);
   2042 	af->use_tag = 0;
   2043 	af->use_tag2 = 0;
   2044 	strcpy(cast_char af->url, cast_const_char url);
   2045 	if (!strcmp(cast_const_char url, cast_const_char f->rq->url))
   2046 		clone_object(f->rq, &af->rq);
   2047 	else
   2048 		request_object(f->ses->term, url, f->rq->url, PRI_IMG, NC_CACHE,
   2049 		               get_allow_flags(f->rq->url), f->rq->upcall,
   2050 		               f->rq->data, &af->rq);
   2051 	af->need_reparse = 0;
   2052 	af->unknown_image_size = 0;
   2053 	add_to_list(f->af->af, af);
   2054 	free(url);
   2055 	return af;
   2056 }
   2057 
   2058 static void
   2059 copy_additional_files(struct additional_files **a)
   2060 {
   2061 	struct additional_files *afs;
   2062 	struct additional_file *af = NULL;
   2063 	struct list_head *laf;
   2064 	if (!*a || (*a)->refcount == 1)
   2065 		return;
   2066 	(*a)->refcount--;
   2067 	afs = xmalloc(sizeof(struct additional_files));
   2068 	afs->refcount = 1;
   2069 	init_list(afs->af);
   2070 	foreachback (struct additional_file, af, laf, (*a)->af) {
   2071 		struct additional_file *afc;
   2072 		size_t sl = strlen(cast_const_char af->url);
   2073 		if (sl > INT_MAX - sizeof(struct additional_file))
   2074 			overalloc();
   2075 		afc = xmalloc(sizeof(struct additional_file) + sl);
   2076 		memcpy(afc, af, sizeof(struct additional_file) + sl);
   2077 		if (af->rq)
   2078 			clone_object(af->rq, &afc->rq);
   2079 		add_to_list(afs->af, afc);
   2080 	}
   2081 	*a = afs;
   2082 }
   2083 
   2084 void
   2085 reinit_f_data_c(struct f_data_c *fd)
   2086 {
   2087 	struct additional_file *af = NULL;
   2088 	struct list_head *laf;
   2089 	struct f_data_c *fd1 = NULL;
   2090 	struct list_head *lfd1;
   2091 
   2092 	foreach (struct f_data_c, fd1, lfd1, fd->subframes) {
   2093 		if (fd->ses->wtd_target_base == fd1)
   2094 			fd->ses->wtd_target_base = NULL;
   2095 		reinit_f_data_c(fd1);
   2096 		if (fd->ses->wtd_target_base == fd1)
   2097 			fd->ses->wtd_target_base = fd;
   2098 	}
   2099 	free_list(struct f_data_c, fd->subframes);
   2100 	fd->loc = NULL;
   2101 	if (fd->f_data && fd->f_data->rq)
   2102 		fd->f_data->rq->upcall = NULL;
   2103 	if (fd->f_data && fd->f_data->af)
   2104 		foreach (struct additional_file, af, laf, fd->f_data->af->af)
   2105 			if (af->rq) {
   2106 				af->rq->upcall = NULL;
   2107 				if (af->rq->state != O_OK)
   2108 					release_object(&af->rq);
   2109 			}
   2110 	if (fd->af)
   2111 		foreach (struct additional_file, af, laf, fd->af->af)
   2112 			if (af->rq)
   2113 				af->rq->upcall = NULL;
   2114 	free_additional_files(&fd->af);
   2115 	detach_f_data(&fd->f_data);
   2116 	release_object(&fd->rq);
   2117 	free(fd->link_bg);
   2118 	fd->link_bg = NULL;
   2119 	fd->link_bg_n = 0;
   2120 	free(fd->goto_position);
   2121 	fd->goto_position = NULL;
   2122 	free(fd->went_to_position);
   2123 	fd->went_to_position = NULL;
   2124 	fd->last_update = get_time();
   2125 	fd->next_update_interval = 0;
   2126 	fd->done = 0;
   2127 	fd->parsed_done = 0;
   2128 	if (fd->image_timer != NULL) {
   2129 		kill_timer(fd->image_timer);
   2130 		fd->image_timer = NULL;
   2131 	}
   2132 	if (fd->refresh_timer != NULL) {
   2133 		kill_timer(fd->refresh_timer);
   2134 		fd->refresh_timer = NULL;
   2135 	}
   2136 }
   2137 
   2138 struct f_data_c *
   2139 create_f_data_c(struct session *ses, struct f_data_c *parent)
   2140 {
   2141 	struct f_data_c *fd;
   2142 	fd = mem_calloc(sizeof(struct f_data_c));
   2143 	fd->parent = parent;
   2144 	fd->ses = ses;
   2145 	fd->depth = parent ? parent->depth + 1 : 1;
   2146 	init_list(fd->subframes);
   2147 	fd->last_update = get_time();
   2148 	fd->next_update_interval = 0;
   2149 	fd->done = 0;
   2150 	fd->parsed_done = 0;
   2151 	fd->script_t = 0;
   2152 	fd->marginwidth = fd->marginheight = -1;
   2153 	fd->image_timer = NULL;
   2154 	fd->refresh_timer = NULL;
   2155 	fd->scrolling = SCROLLING_AUTO;
   2156 	return fd;
   2157 }
   2158 
   2159 int
   2160 f_data_c_allow_flags(struct f_data_c *fd)
   2161 {
   2162 	if (fd->rq)
   2163 		return get_allow_flags(fd->rq->url);
   2164 	return 0;
   2165 }
   2166 
   2167 static int
   2168 is_forced_download(struct object_request *rq)
   2169 {
   2170 	struct cache_entry *ce;
   2171 	unsigned char *cd;
   2172 	char *s;
   2173 	int ret = 0;
   2174 
   2175 	if (!rq || !(ce = rq->ce))
   2176 		return ret;
   2177 	if ((cd = parse_http_header(ce->head, cast_uchar "Content-Disposition",
   2178 	                            NULL))) {
   2179 		if ((s = strchr(cast_const_char cd, ';')))
   2180 			*s = 0;
   2181 		ret = !casestrcmp(cd, cast_uchar "attachment");
   2182 		free(cd);
   2183 	}
   2184 	return ret;
   2185 }
   2186 
   2187 static int
   2188 plain_type(struct object_request *rq, unsigned char **p)
   2189 {
   2190 	struct cache_entry *ce;
   2191 	unsigned char *ct;
   2192 	int r = 0;
   2193 	if (p)
   2194 		*p = NULL;
   2195 	if (!rq || !(ce = rq->ce)) {
   2196 		r = 1;
   2197 		goto f;
   2198 	}
   2199 	if (!(ct = get_content_type(ce->head, ce->url)))
   2200 		goto f;
   2201 	if (is_html_type(ct))
   2202 		goto ff;
   2203 	r = 1;
   2204 	if (!casestrcmp(ct, cast_uchar "text/plain")
   2205 	    || !casestrcmp(ct, cast_uchar "file/txt"))
   2206 		goto ff;
   2207 	r = -1;
   2208 
   2209 ff:
   2210 	if (!p)
   2211 		free(ct);
   2212 	else
   2213 		*p = ct;
   2214 f:
   2215 	return r;
   2216 }
   2217 
   2218 static void
   2219 refresh_timer(void *fd_)
   2220 {
   2221 	struct f_data_c *fd = (struct f_data_c *)fd_;
   2222 	if (fd->ses->rq) {
   2223 		fd->refresh_timer = install_timer(500, refresh_timer, fd);
   2224 		return;
   2225 	}
   2226 	fd->refresh_timer = NULL;
   2227 	if (fd->f_data && fd->f_data->refresh) {
   2228 		fd->refresh_timer = install_timer(
   2229 		    fd->f_data->refresh_seconds * 1000, refresh_timer, fd);
   2230 		goto_url_f(fd->ses, NULL, fd->f_data->refresh,
   2231 		           cast_uchar "_self", fd, -1, 0, 0, 1);
   2232 	}
   2233 }
   2234 
   2235 void
   2236 fd_loaded(struct object_request *rq, void *fd_)
   2237 {
   2238 	struct f_data_c *fd = (struct f_data_c *)fd_;
   2239 	int first = !fd->f_data;
   2240 	if (fd->done) {
   2241 		if (f_is_finished(fd->f_data))
   2242 			goto priint;
   2243 		else {
   2244 			fd->done = 0;
   2245 			fd->parsed_done = 1;
   2246 		}
   2247 	}
   2248 	if (fd->parsed_done && f_need_reparse(fd->f_data))
   2249 		fd->parsed_done = 0;
   2250 	if (fd->vs->plain == -1 && rq->state != O_WAITING) {
   2251 		fd->vs->plain = plain_type(fd->rq, NULL);
   2252 	}
   2253 	if (fd->rq->state < 0 && (f_is_finished(fd->f_data) || !fd->f_data)) {
   2254 		if (!fd->parsed_done) {
   2255 			html_interpret(fd, 1);
   2256 			if (fd->went_to_position) {
   2257 				if (!fd->goto_position) {
   2258 					fd->goto_position =
   2259 					    fd->went_to_position;
   2260 					fd->went_to_position = NULL;
   2261 				} else {
   2262 					free(fd->went_to_position);
   2263 					fd->went_to_position = NULL;
   2264 				}
   2265 			}
   2266 		}
   2267 		draw_fd(fd);
   2268 		/* it may happen that html_interpret requests load of additional
   2269 		 * file */
   2270 		if (!f_is_finished(fd->f_data))
   2271 			goto more_data;
   2272 fn:
   2273 		fd->done = 1;
   2274 		fd->parsed_done = 0;
   2275 		if (fd->f_data->refresh) {
   2276 			if (fd->refresh_timer != NULL)
   2277 				kill_timer(fd->refresh_timer);
   2278 			fd->refresh_timer =
   2279 			    install_timer(fd->f_data->refresh_seconds * 1000,
   2280 			                  refresh_timer, fd);
   2281 		}
   2282 	} else if (get_time() - fd->last_update >= fd->next_update_interval
   2283 	           || (rq == fd->rq && rq->state < 0)) {
   2284 		uttime t;
   2285 		if (!fd->parsed_done) {
   2286 			html_interpret(fd, rq == fd->rq && rq->state < 0);
   2287 			if (fd->rq->state < 0 && !f_need_reparse(fd->f_data)) {
   2288 				if (fd->went_to_position) {
   2289 					if (!fd->goto_position) {
   2290 						fd->goto_position =
   2291 						    fd->went_to_position;
   2292 						fd->went_to_position = NULL;
   2293 					} else {
   2294 						free(fd->went_to_position);
   2295 						fd->went_to_position = NULL;
   2296 					}
   2297 				}
   2298 				fd->parsed_done = 1;
   2299 			}
   2300 		}
   2301 		draw_fd(fd);
   2302 		if (fd->rq->state < 0 && f_is_finished(fd->f_data))
   2303 			goto fn;
   2304 more_data:
   2305 		t = fd->f_data ? ((fd->parsed_done
   2306 		                       ? 0
   2307 		                       : fd->f_data->time_to_get * DISPLAY_TIME)
   2308 		                  + fd->f_data->time_to_draw * IMG_DISPLAY_TIME)
   2309 		               : 0;
   2310 		if (t < DISPLAY_TIME_MIN)
   2311 			t = DISPLAY_TIME_MIN;
   2312 		if (first && t > DISPLAY_TIME_MAX_FIRST)
   2313 			t = DISPLAY_TIME_MAX_FIRST;
   2314 		fd->last_update = get_time();
   2315 		fd->next_update_interval = t;
   2316 	} else {
   2317 		change_screen_status(fd->ses);
   2318 		print_screen_status(fd->ses);
   2319 	}
   2320 priint:
   2321 	/* process onload handler of a frameset */
   2322 	if (rq && (rq->state == O_FAILED || rq->state == O_INCOMPLETE)
   2323 	    && (fd->rq == rq || fd->ses->rq == rq) && !rq->dont_print_error)
   2324 		print_error_dialog(fd->ses, &rq->stat, rq->url);
   2325 }
   2326 
   2327 static unsigned location_id = 0;
   2328 
   2329 static struct location *
   2330 new_location(void)
   2331 {
   2332 	struct location *loc;
   2333 	loc = mem_calloc(sizeof(struct location));
   2334 	loc->location_id = ++location_id;
   2335 	init_list(loc->subframes);
   2336 	loc->vs = create_vs();
   2337 	return loc;
   2338 }
   2339 
   2340 static struct location *
   2341 alloc_ses_location(struct session *ses)
   2342 {
   2343 	struct location *loc;
   2344 	loc = new_location();
   2345 	add_to_list(ses->history, loc);
   2346 	return loc;
   2347 }
   2348 
   2349 static void
   2350 subst_location(struct f_data_c *fd, struct location *old, struct location *neww)
   2351 {
   2352 	struct f_data_c *f = NULL;
   2353 	struct list_head *lf;
   2354 	foreach (struct f_data_c, f, lf, fd->subframes)
   2355 		subst_location(f, old, neww);
   2356 	if (fd->loc == old)
   2357 		fd->loc = neww;
   2358 }
   2359 
   2360 static struct location *
   2361 copy_sublocations(struct session *ses, struct location *d, struct location *s,
   2362                   struct location *x)
   2363 {
   2364 	struct location *sl = NULL, *y;
   2365 	struct list_head *lsl;
   2366 	d->name = stracpy(s->name);
   2367 	if (s == x)
   2368 		return d;
   2369 	d->url = stracpy(s->url);
   2370 	d->prev_url = stracpy(s->prev_url);
   2371 	destroy_vs(d->vs);
   2372 	d->vs = s->vs;
   2373 	s->vs->refcount++;
   2374 	subst_location(ses->screen, s, d);
   2375 	y = NULL;
   2376 	foreach (struct location, sl, lsl, s->subframes) {
   2377 		struct location *dl, *z;
   2378 
   2379 		dl = new_location();
   2380 		add_to_list_end(d->subframes, dl);
   2381 		dl->parent = d;
   2382 		z = copy_sublocations(ses, dl, sl, x);
   2383 		if (z && y)
   2384 			internal("copy_sublocations: crossed references");
   2385 		if (z)
   2386 			y = z;
   2387 	}
   2388 	return y;
   2389 }
   2390 
   2391 static struct location *
   2392 copy_location(struct session *ses, struct location *loc)
   2393 {
   2394 	struct location *l2, *l1, *nl;
   2395 	l1 = cur_loc(ses);
   2396 	l2 = alloc_ses_location(ses);
   2397 	if (!(nl = copy_sublocations(ses, l2, l1, loc)))
   2398 		internal("copy_location: sublocation not found");
   2399 	return nl;
   2400 }
   2401 
   2402 static struct f_data_c *
   2403 new_main_location(struct session *ses)
   2404 {
   2405 	struct location *loc;
   2406 	loc = alloc_ses_location(ses);
   2407 	reinit_f_data_c(ses->screen);
   2408 	ses->screen->loc = loc;
   2409 	ses->screen->vs = loc->vs;
   2410 	if (ses->wanted_framename) {
   2411 		loc->name = ses->wanted_framename;
   2412 		ses->wanted_framename = NULL;
   2413 	}
   2414 	return ses->screen;
   2415 }
   2416 
   2417 static struct f_data_c *
   2418 copy_location_and_replace_frame(struct session *ses, struct f_data_c *fd)
   2419 {
   2420 	struct location *loc;
   2421 	loc = copy_location(ses, fd->loc);
   2422 	reinit_f_data_c(fd);
   2423 	fd->loc = loc;
   2424 	fd->vs = loc->vs;
   2425 	return fd;
   2426 }
   2427 
   2428 /* vrati frame prislusici danemu targetu
   2429    pokud takovy frame nenajde, vraci NULL
   2430  */
   2431 struct f_data_c *
   2432 find_frame(struct session *ses, unsigned char *target, struct f_data_c *base)
   2433 {
   2434 	struct f_data_c *f, *ff = NULL;
   2435 	struct list_head *lff;
   2436 	if (!base)
   2437 		base = ses->screen;
   2438 	if (!target || !*target)
   2439 		return base;
   2440 	if (!casestrcmp(target, cast_uchar "_blank"))
   2441 		return NULL; /* open in new window */
   2442 	if (!casestrcmp(target, cast_uchar "_top"))
   2443 		return ses->screen;
   2444 	if (!casestrcmp(target, cast_uchar "_self"))
   2445 		return base;
   2446 	if (!casestrcmp(target, cast_uchar "_parent")) {
   2447 		for (ff = base->parent; ff && !ff->rq; ff = ff->parent)
   2448 			;
   2449 		return ff ? ff : ses->screen;
   2450 	}
   2451 	f = ses->screen;
   2452 	if (f->loc && f->loc->name && !casestrcmp(f->loc->name, target))
   2453 		return f;
   2454 d:
   2455 	foreach (struct f_data_c, ff, lff, f->subframes)
   2456 		if (ff->loc && ff->loc->name
   2457 		    && !casestrcmp(ff->loc->name, target))
   2458 			return ff;
   2459 	if (!list_empty(f->subframes)) {
   2460 		f = list_struct(f->subframes.next, struct f_data_c);
   2461 		goto d;
   2462 	}
   2463 u:
   2464 	if (!f->parent)
   2465 		return NULL;
   2466 	if (f->list_entry.next == &f->parent->subframes) {
   2467 		f = f->parent;
   2468 		goto u;
   2469 	}
   2470 	f = list_struct(f->list_entry.next, struct f_data_c);
   2471 	goto d;
   2472 }
   2473 
   2474 static void
   2475 destroy_location(struct location *loc)
   2476 {
   2477 	while (!list_empty(loc->subframes))
   2478 		destroy_location(
   2479 		    list_struct(loc->subframes.next, struct location));
   2480 	del_from_list(loc);
   2481 	free(loc->name);
   2482 	free(loc->url);
   2483 	free(loc->prev_url);
   2484 	destroy_vs(loc->vs);
   2485 	free(loc);
   2486 }
   2487 
   2488 static void
   2489 clear_forward_history(struct session *ses)
   2490 {
   2491 	while (!list_empty(ses->forward_history))
   2492 		destroy_location(
   2493 		    list_struct(ses->forward_history.next, struct location));
   2494 }
   2495 
   2496 static void
   2497 ses_go_forward(struct session *ses, int plain, int refresh)
   2498 {
   2499 	struct location *cl;
   2500 	struct f_data_c *fd;
   2501 	clear_forward_history(ses);
   2502 	free(ses->search_word);
   2503 	ses->search_word = NULL;
   2504 	free(ses->default_status);
   2505 	ses->default_status = NULL; /* smazeme default status, aby neopruzoval
   2506 	                               na jinych strankach */
   2507 	if ((fd = find_frame(ses, ses->wtd_target, ses->wtd_target_base))
   2508 	    && fd != ses->screen) {
   2509 		cl = NULL;
   2510 		if (refresh && fd->loc
   2511 		    && !strcmp(cast_const_char fd->loc->url,
   2512 		               cast_const_char ses->rq->url))
   2513 			cl = cur_loc(ses);
   2514 		fd = copy_location_and_replace_frame(ses, fd);
   2515 		if (cl)
   2516 			destroy_location(cl);
   2517 	} else
   2518 		fd = new_main_location(ses);
   2519 	fd->vs->plain = plain;
   2520 	ses->wtd = NULL;
   2521 	fd->rq = ses->rq;
   2522 	ses->rq = NULL;
   2523 	fd->goto_position = ses->goto_position;
   2524 	ses->goto_position = NULL;
   2525 	fd->loc->url = stracpy(fd->rq->url);
   2526 	fd->loc->prev_url = stracpy(fd->rq->prev_url);
   2527 	fd->rq->upcall = fd_loaded;
   2528 	fd->rq->data = fd;
   2529 	fd->rq->upcall(fd->rq, fd);
   2530 	if (!list_empty(ses->screen->subframes))
   2531 		draw_formatted(ses);
   2532 }
   2533 
   2534 static void
   2535 ses_go_backward(struct session *ses)
   2536 {
   2537 	int n;
   2538 	struct location *loc;
   2539 	struct list_head *lloc;
   2540 	free(ses->search_word);
   2541 	ses->search_word = NULL;
   2542 	free(ses->default_status);
   2543 	ses->default_status = NULL; /* smazeme default status, aby neopruzoval
   2544 	                               na jinych strankach */
   2545 	reinit_f_data_c(ses->screen);
   2546 	if (!ses->wtd_num_steps)
   2547 		internal("ses_go_backward: wtd_num_steps is zero");
   2548 	if (ses->wtd_num_steps > 0) {
   2549 		n = ses->wtd_num_steps;
   2550 		foreach (struct location, loc, lloc, ses->history) {
   2551 			if (!n--)
   2552 				goto have_back_loc;
   2553 		}
   2554 		internal("ses_go_backward: session history disappeared");
   2555 		return;
   2556 have_back_loc:
   2557 		for (n = 0; n < ses->wtd_num_steps; n++) {
   2558 			loc = cur_loc(ses);
   2559 			del_from_list(loc);
   2560 			add_to_list(ses->forward_history, loc);
   2561 		}
   2562 	} else {
   2563 		n = ses->wtd_num_steps;
   2564 		foreach (struct location, loc, lloc, ses->forward_history) {
   2565 			if (!++n)
   2566 				goto have_fwd_loc;
   2567 		}
   2568 		internal(
   2569 		    "ses_go_backward: session forward history disappeared");
   2570 		return;
   2571 have_fwd_loc:
   2572 		for (n = 0; n < -ses->wtd_num_steps; n++) {
   2573 			loc = list_struct(ses->forward_history.next,
   2574 			                  struct location);
   2575 			del_from_list(loc);
   2576 			add_to_list(ses->history, loc);
   2577 		}
   2578 	}
   2579 	ses->screen->loc = cur_loc(ses);
   2580 	ses->screen->vs = ses->screen->loc->vs;
   2581 	ses->wtd = NULL;
   2582 	ses->screen->rq = ses->rq;
   2583 	ses->rq = NULL;
   2584 	ses->screen->rq->upcall = fd_loaded;
   2585 	ses->screen->rq->data = ses->screen;
   2586 	ses->screen->rq->upcall(ses->screen->rq, ses->screen);
   2587 }
   2588 
   2589 static void
   2590 tp_cancel(void *ses_)
   2591 {
   2592 	struct session *ses = (struct session *)ses_;
   2593 	release_object(&ses->tq);
   2594 }
   2595 
   2596 static void
   2597 continue_download(struct session *ses, unsigned char *file, int mode)
   2598 {
   2599 	struct download *down;
   2600 	int h;
   2601 	int namecount = 0;
   2602 	unsigned char *url = ses->tq->url;
   2603 	unsigned char *xl_file;
   2604 	off_t last_pos = 0, file_shift = 0;
   2605 
   2606 	if (ses->tq_prog) {
   2607 		if (ses->tq_prog_flag_direct && ses->tq->state != O_OK
   2608 		    && !strchr(cast_const_char url, POST_CHAR)
   2609 		    && !check_shell_url(url)) {
   2610 			unsigned char *prog = subst_file(ses->tq_prog, url, 0);
   2611 			exec_on_terminal(ses->term, prog, cast_uchar "",
   2612 			                 !!ses->tq_prog_flag_block);
   2613 			free(prog);
   2614 			tp_cancel(ses);
   2615 			abort_background_connections();
   2616 			return;
   2617 		}
   2618 new_name:
   2619 		if (!(file = get_temp_name(url, ses->tq->ce ? ses->tq->ce->head
   2620 		                                            : NULL))) {
   2621 			tp_cancel(ses);
   2622 			return;
   2623 		}
   2624 		if ((h = create_download_file(ses, ses->term->cwd, file,
   2625 		                              CDF_RESTRICT_PERMISSION | CDF_EXCL
   2626 		                                  | CDF_NO_POPUP_ON_EEXIST,
   2627 		                              0))
   2628 		    < 0) {
   2629 			if (h == -2 && ses->tq_prog) {
   2630 				if (++namecount < DOWNLOAD_NAME_TRIES) {
   2631 					free(file);
   2632 					goto new_name;
   2633 				}
   2634 				msg_box(
   2635 				    ses->term, NULL, TEXT_(T_DOWNLOAD_ERROR),
   2636 				    AL_CENTER,
   2637 				    TEXT_(T_COULD_NOT_CREATE_TEMPORARY_FILE),
   2638 				    MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL),
   2639 				    msg_box_null, B_ENTER | B_ESC);
   2640 			}
   2641 			free(file);
   2642 			tp_cancel(ses);
   2643 			return;
   2644 		}
   2645 		xl_file = stracpy(file);
   2646 	} else {
   2647 		if (create_or_append_download_file(ses, ses->term->cwd, file,
   2648 		                                   mode, &h, &xl_file,
   2649 		                                   &last_pos, &file_shift)) {
   2650 			tp_cancel(ses);
   2651 			return;
   2652 		}
   2653 	}
   2654 	down = mem_calloc(sizeof(struct download));
   2655 	down->url = stracpy(url);
   2656 	down->stat.end = download_data;
   2657 	down->stat.data = down;
   2658 	down->decompress = 0;
   2659 	down->last_pos = last_pos;
   2660 	down->file_shift = file_shift;
   2661 	down->cwd = stracpy(ses->term->cwd);
   2662 	down->orig_file = stracpy(file);
   2663 	down->file = xl_file;
   2664 	down->handle = h;
   2665 	down->ses = ses;
   2666 	down->remotetime = 0;
   2667 	if (ses->tq_prog) {
   2668 		down->prog = subst_file(ses->tq_prog, file, 1);
   2669 		free(file);
   2670 		free(ses->tq_prog);
   2671 		ses->tq_prog = NULL;
   2672 	}
   2673 	down->prog_flag_block = ses->tq_prog_flag_block;
   2674 	add_to_list(downloads, down);
   2675 	release_object_get_stat(&ses->tq, &down->stat, PRI_DOWNLOAD);
   2676 	display_download(ses->term, down, ses);
   2677 }
   2678 
   2679 static void
   2680 tp_save(void *ses_)
   2681 {
   2682 	struct session *ses = (struct session *)ses_;
   2683 	free(ses->tq_prog);
   2684 	ses->tq_prog = NULL;
   2685 	query_file(ses, ses->tq->url, ses->tq->ce ? ses->tq->ce->head : NULL,
   2686 	           &continue_download, tp_cancel, DOWNLOAD_CONTINUE);
   2687 }
   2688 
   2689 static void
   2690 tp_open(void *ses_)
   2691 {
   2692 	struct session *ses = (struct session *)ses_;
   2693 	continue_download(ses, cast_uchar "", DOWNLOAD_DEFAULT);
   2694 }
   2695 
   2696 static int
   2697 ses_abort_1st_state_loading(struct session *ses)
   2698 {
   2699 	int r = !!ses->rq;
   2700 	release_object(&ses->rq);
   2701 	ses->wtd = NULL;
   2702 	free(ses->wtd_target);
   2703 	ses->wtd_target = NULL;
   2704 	ses->wtd_target_base = NULL;
   2705 	free(ses->goto_position);
   2706 	ses->goto_position = NULL;
   2707 	change_screen_status(ses);
   2708 	print_screen_status(ses);
   2709 	return r;
   2710 }
   2711 
   2712 static void
   2713 tp_display(void *ses_)
   2714 {
   2715 	int plain = 1;
   2716 	struct session *ses = (struct session *)ses_;
   2717 	if (plain_type(ses->tq, NULL) == 2)
   2718 		plain = 2;
   2719 	ses_abort_1st_state_loading(ses);
   2720 	ses->rq = ses->tq;
   2721 	ses->tq = NULL;
   2722 	ses_go_forward(ses, plain, 0);
   2723 }
   2724 
   2725 static int
   2726 direct_download_possible(struct object_request *rq, struct assoc *a)
   2727 {
   2728 	unsigned char *proto = get_protocol_name(rq->url);
   2729 	int ret = 0;
   2730 	if (!proto)
   2731 		return 0;
   2732 	if (a->accept_http && !casestrcmp(proto, cast_uchar "http"))
   2733 		ret = 1;
   2734 	if (a->accept_ftp && !casestrcmp(proto, cast_uchar "ftp"))
   2735 		ret = 1;
   2736 	free(proto);
   2737 	if (proxies.only_proxies)
   2738 		ret = 0;
   2739 	return ret;
   2740 }
   2741 
   2742 static int
   2743 prog_sel_save(struct dialog_data *dlg, struct dialog_item_data *idata)
   2744 {
   2745 	struct session *ses = (struct session *)dlg->dlg->udata2;
   2746 
   2747 	tp_save(ses);
   2748 
   2749 	cancel_dialog(dlg, idata);
   2750 	return 0;
   2751 }
   2752 
   2753 static int
   2754 prog_sel_display(struct dialog_data *dlg, struct dialog_item_data *idata)
   2755 {
   2756 	struct session *ses = (struct session *)dlg->dlg->udata2;
   2757 
   2758 	tp_display(ses);
   2759 
   2760 	cancel_dialog(dlg, idata);
   2761 	return 0;
   2762 }
   2763 
   2764 static int
   2765 prog_sel_cancel(struct dialog_data *dlg, struct dialog_item_data *idata)
   2766 {
   2767 	struct session *ses = (struct session *)dlg->dlg->udata2;
   2768 
   2769 	tp_cancel(ses);
   2770 
   2771 	cancel_dialog(dlg, idata);
   2772 	return 0;
   2773 }
   2774 
   2775 static int
   2776 prog_sel_open(struct dialog_data *dlg, struct dialog_item_data *idata)
   2777 {
   2778 	struct assoc *a = (struct assoc *)idata->item->udata;
   2779 	struct session *ses = (struct session *)dlg->dlg->udata2;
   2780 
   2781 	if (!a)
   2782 		internal("This should not happen.\n");
   2783 	ses->tq_prog = stracpy(a->prog);
   2784 	ses->tq_prog_flag_block = a->block;
   2785 	ses->tq_prog_flag_direct = direct_download_possible(ses->tq, a);
   2786 	tp_open(ses);
   2787 	cancel_dialog(dlg, idata);
   2788 	return 0;
   2789 }
   2790 
   2791 static void
   2792 type_query_multiple_programs(struct session *ses, unsigned char *ct,
   2793                              struct assoc *a, int n)
   2794 {
   2795 	int i;
   2796 	struct dialog *d;
   2797 	struct memory_list *ml;
   2798 	unsigned char **text_array;
   2799 
   2800 	text_array = xmalloc(6 * sizeof(unsigned char *));
   2801 	text_array[0] = TEXT_(T_CONTENT_TYPE_IS);
   2802 	text_array[1] = cast_uchar " ";
   2803 	text_array[2] = ct;
   2804 	text_array[3] = cast_uchar ".\n";
   2805 	text_array[4] =
   2806 	    !anonymous ? TEXT_(T_DO_YOU_WANT_TO_OPEN_SAVE_OR_DISPLAY_THIS_FILE)
   2807 		       : TEXT_(T_DO_YOU_WANT_TO_OPEN_OR_DISPLAY_THIS_FILE);
   2808 	text_array[5] = NULL;
   2809 
   2810 	if ((unsigned)n
   2811 	    > (INT_MAX - sizeof(struct dialog)) / sizeof(struct dialog_item)
   2812 	          - 4)
   2813 		overalloc();
   2814 	d = mem_calloc(sizeof(struct dialog)
   2815 	               + (n + 2 + (!anonymous)) * sizeof(struct dialog_item));
   2816 	d->title = TEXT_(T_WHAT_TO_DO);
   2817 	d->fn = msg_box_fn;
   2818 	d->udata = text_array;
   2819 	d->udata2 = ses;
   2820 	d->align = AL_CENTER;
   2821 	ml = getml(d, a, ct, text_array, NULL);
   2822 
   2823 	for (i = 0; i < n; i++) {
   2824 		unsigned char *bla = stracpy(
   2825 		    get_text_translation(TEXT_(T_OPEN_WITH), ses->term));
   2826 		add_to_strn(&bla, cast_uchar " ");
   2827 		add_to_strn(&bla, a[i].label);
   2828 
   2829 		d->items[i].type = D_BUTTON;
   2830 		d->items[i].fn = prog_sel_open;
   2831 		d->items[i].udata = a + i;
   2832 		d->items[i].text = bla;
   2833 		a[i].prog = stracpy(a[i].prog);
   2834 		add_to_ml(&ml, bla, a[i].prog, NULL);
   2835 	}
   2836 	if (!anonymous) {
   2837 		d->items[i].type = D_BUTTON;
   2838 		d->items[i].fn = prog_sel_save;
   2839 		d->items[i].text = TEXT_(T_SAVE);
   2840 		i++;
   2841 	}
   2842 	d->items[i].type = D_BUTTON;
   2843 	d->items[i].fn = prog_sel_display;
   2844 	d->items[i].text = TEXT_(T_DISPLAY);
   2845 	i++;
   2846 	d->items[i].type = D_BUTTON;
   2847 	d->items[i].fn = prog_sel_cancel;
   2848 	d->items[i].gid = B_ESC;
   2849 	d->items[i].text = TEXT_(T_CANCEL);
   2850 	i++;
   2851 	d->items[i].type = D_END;
   2852 	do_dialog(ses->term, d, ml);
   2853 }
   2854 
   2855 /* deallocates a */
   2856 static void
   2857 type_query(struct session *ses, unsigned char *ct, struct assoc *a, int n)
   2858 {
   2859 	unsigned char *m1;
   2860 	unsigned char *m2;
   2861 	if (!ct)
   2862 		ct = stracpy(cast_uchar "unknown");
   2863 	free(ses->tq_prog);
   2864 	ses->tq_prog = NULL;
   2865 
   2866 	if (n > 1) {
   2867 		type_query_multiple_programs(ses, ct, a, n);
   2868 		return;
   2869 	}
   2870 
   2871 	if (a) {
   2872 		ses->tq_prog = stracpy(a[0].prog);
   2873 		ses->tq_prog_flag_block = a[0].block;
   2874 		ses->tq_prog_flag_direct = direct_download_possible(ses->tq, a);
   2875 	}
   2876 	if (a && !a[0].ask) {
   2877 		tp_open(ses);
   2878 		if (n)
   2879 			free(a);
   2880 		free(ct);
   2881 		return;
   2882 	}
   2883 	m1 = stracpy(ct);
   2884 	if (!a) {
   2885 		if (!anonymous)
   2886 			msg_box(
   2887 			    ses->term, getml(m1, NULL), TEXT_(T_UNKNOWN_TYPE),
   2888 			    AL_CENTER, TEXT_(T_CONTENT_TYPE_IS), cast_uchar " ",
   2889 			    m1, cast_uchar ".\n",
   2890 			    TEXT_(T_DO_YOU_WANT_TO_SAVE_OR_DISLPAY_THIS_FILE),
   2891 			    MSG_BOX_END, (void *)ses, 3, TEXT_(T_SAVE), tp_save,
   2892 			    B_ENTER, TEXT_(T_DISPLAY), tp_display, 0,
   2893 			    TEXT_(T_CANCEL), tp_cancel, B_ESC);
   2894 		else
   2895 			msg_box(
   2896 			    ses->term, getml(m1, NULL), TEXT_(T_UNKNOWN_TYPE),
   2897 			    AL_CENTER, TEXT_(T_CONTENT_TYPE_IS), cast_uchar " ",
   2898 			    m1, cast_uchar ".\n",
   2899 			    TEXT_(T_DO_YOU_WANT_TO_SAVE_OR_DISLPAY_THIS_FILE),
   2900 			    MSG_BOX_END, (void *)ses, 2, TEXT_(T_DISPLAY),
   2901 			    tp_display, B_ENTER, TEXT_(T_CANCEL), tp_cancel,
   2902 			    B_ESC);
   2903 	} else {
   2904 		m2 = stracpy(a[0].label ? a[0].label : (unsigned char *)"");
   2905 		if (!anonymous)
   2906 			msg_box(
   2907 			    ses->term, getml(m1, m2, NULL), TEXT_(T_WHAT_TO_DO),
   2908 			    AL_CENTER, TEXT_(T_CONTENT_TYPE_IS), cast_uchar " ",
   2909 			    m1, cast_uchar ".\n",
   2910 			    TEXT_(T_DO_YOU_WANT_TO_OPEN_FILE_WITH),
   2911 			    cast_uchar " ", m2, cast_uchar ", ",
   2912 			    TEXT_(T_SAVE_IT_OR_DISPLAY_IT), MSG_BOX_END,
   2913 			    (void *)ses, 4, TEXT_(T_OPEN), tp_open, B_ENTER,
   2914 			    TEXT_(T_SAVE), tp_save, 0, TEXT_(T_DISPLAY),
   2915 			    tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
   2916 		else
   2917 			msg_box(ses->term, getml(m1, m2, NULL),
   2918 			        TEXT_(T_WHAT_TO_DO), AL_CENTER,
   2919 			        TEXT_(T_CONTENT_TYPE_IS), cast_uchar " ", m1,
   2920 			        cast_uchar ".\n",
   2921 			        TEXT_(T_DO_YOU_WANT_TO_OPEN_FILE_WITH),
   2922 			        cast_uchar " ", m2, cast_uchar ", ",
   2923 			        TEXT_(T_SAVE_IT_OR_DISPLAY_IT), MSG_BOX_END,
   2924 			        (void *)ses, 3, TEXT_(T_OPEN), tp_open, B_ENTER,
   2925 			        TEXT_(T_DISPLAY), tp_display, 0,
   2926 			        TEXT_(T_CANCEL), tp_cancel, B_ESC);
   2927 	}
   2928 	if (n)
   2929 		free(a);
   2930 	free(ct);
   2931 }
   2932 
   2933 static void
   2934 ses_go_to_2nd_state(struct session *ses)
   2935 {
   2936 	struct assoc *a;
   2937 	int n;
   2938 	unsigned char *ct = NULL;
   2939 	int r = plain_type(ses->rq, &ct);
   2940 	int force_download = is_forced_download(ses->rq);
   2941 	if (!force_download && (r == 0 || r == 1 || r == 2))
   2942 		goto go;
   2943 	if (!(a = get_type_assoc(ses->term, ct, &n))
   2944 	    && strlen(cast_const_char ct) >= 4
   2945 	    && !casecmp(ct, cast_uchar "text", 4) && !force_download) {
   2946 		r = 1;
   2947 		goto go;
   2948 	}
   2949 	if (ses->tq) {
   2950 		ses_abort_1st_state_loading(ses);
   2951 		free(a);
   2952 		free(ct);
   2953 		return;
   2954 	}
   2955 	(ses->tq = ses->rq)->upcall = NULL;
   2956 	ses->rq = NULL;
   2957 	ses_abort_1st_state_loading(ses);
   2958 	if (!a && ses->tq->ce && ses->tq->ce->head) {
   2959 		unsigned char *file =
   2960 		    get_filename_from_header(ses->tq->ce->head);
   2961 		if (file) {
   2962 			unsigned char *new_ct =
   2963 			    get_content_type_by_extension(file);
   2964 			free(file);
   2965 			if (new_ct) {
   2966 				a = get_type_assoc(ses->term, new_ct, &n);
   2967 				free(new_ct);
   2968 			}
   2969 		}
   2970 	}
   2971 	type_query(ses, ct, a, n);
   2972 	return;
   2973 go:
   2974 	ses_go_forward(ses, r, ses->wtd_refresh);
   2975 	free(ct);
   2976 }
   2977 
   2978 static void
   2979 ses_go_back_to_2nd_state(struct session *ses)
   2980 {
   2981 	ses_go_backward(ses);
   2982 }
   2983 
   2984 static void
   2985 ses_finished_1st_state(struct object_request *rq, void *ses_)
   2986 {
   2987 	struct session *ses = (struct session *)ses_;
   2988 	if (rq->state != O_WAITING) {
   2989 		if (ses->wtd_refresh && ses->wtd_target_base
   2990 		    && ses->wtd_target_base->refresh_timer != NULL) {
   2991 			kill_timer(ses->wtd_target_base->refresh_timer);
   2992 			ses->wtd_target_base->refresh_timer = NULL;
   2993 		}
   2994 	}
   2995 	switch (rq->state) {
   2996 	case O_WAITING:
   2997 		change_screen_status(ses);
   2998 		print_screen_status(ses);
   2999 		break;
   3000 	case O_FAILED:
   3001 		if (!rq->dont_print_error)
   3002 			print_error_dialog(ses, &rq->stat, rq->url);
   3003 		ses_abort_1st_state_loading(ses);
   3004 		break;
   3005 	case O_LOADING:
   3006 	case O_INCOMPLETE:
   3007 	case O_OK:
   3008 		if (!ses->goto_position && rq->goto_position)
   3009 			ses->goto_position = stracpy(rq->goto_position);
   3010 		ses->wtd(ses);
   3011 		break;
   3012 	}
   3013 }
   3014 
   3015 /* if from_goto_dialog is 1, set prev_url to NULL */
   3016 void
   3017 goto_url_f(struct session *ses, void (*state2)(struct session *),
   3018            unsigned char *url, unsigned char *target, struct f_data_c *df,
   3019            int data, int defer, int from_goto_dialog, int refresh)
   3020 {
   3021 	unsigned char *u, *pos;
   3022 	unsigned char *prev_url;
   3023 	void (*fn)(struct session *, unsigned char *);
   3024 	int reloadlevel, allow_flags;
   3025 	if (!state2)
   3026 		state2 = ses_go_to_2nd_state;
   3027 	if ((fn = get_external_protocol_function(url))) {
   3028 		if (proxies.only_proxies && url_bypasses_socks(url)) {
   3029 			msg_box(ses->term, NULL, TEXT_(T_ERROR), AL_CENTER,
   3030 			        TEXT_(T_NO_PROXY), MSG_BOX_END, NULL, 1,
   3031 			        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   3032 			return;
   3033 		}
   3034 		fn(ses, url);
   3035 		return;
   3036 	}
   3037 	ses->reloadlevel = NC_CACHE;
   3038 	if (!(u = translate_url(url, ses->term->cwd))) {
   3039 		struct status stat = { init_list_1st(NULL) NULL,
   3040 			               NULL,
   3041 			               S_BAD_URL,
   3042 			               PRI_CANCEL,
   3043 			               0,
   3044 			               NULL,
   3045 			               NULL,
   3046 			               NULL };
   3047 		print_error_dialog(ses, &stat, url);
   3048 		return;
   3049 	}
   3050 	pos = extract_position(u);
   3051 	if (ses->wtd == state2
   3052 	    && !strcmp(cast_const_char ses->rq->orig_url, cast_const_char u)
   3053 	    && !xstrcmp(ses->wtd_target, target)
   3054 	    && ses->wtd_target_base == df) {
   3055 		free(u);
   3056 		free(ses->goto_position);
   3057 		ses->goto_position = pos;
   3058 		return;
   3059 	}
   3060 	ses_abort_1st_state_loading(ses);
   3061 	ses->wtd = state2;
   3062 	ses->wtd_target = stracpy(target);
   3063 	ses->wtd_target_base = df;
   3064 	ses->wtd_refresh = refresh;
   3065 	free(ses->goto_position);
   3066 	ses->goto_position = pos;
   3067 	if (!from_goto_dialog && df && df->rq)
   3068 		prev_url = df->rq->url;
   3069 	else
   3070 		prev_url = NULL; /* previous page is empty - this probably never
   3071 		                    happens, but for sure */
   3072 	if (refresh && ses->wtd == ses_go_to_2nd_state) {
   3073 		struct f_data_c *fr =
   3074 		    find_frame(ses, ses->wtd_target, ses->wtd_target_base);
   3075 		if (fr && fr->loc)
   3076 			if (!strcmp(cast_const_char fr->loc->url,
   3077 			            cast_const_char u))
   3078 				ses->reloadlevel = NC_RELOAD;
   3079 	}
   3080 	reloadlevel = ses->reloadlevel;
   3081 	if (ses->wtd == ses_go_to_2nd_state && !from_goto_dialog && !refresh) {
   3082 		struct f_data_c *fd;
   3083 		fd = find_frame(ses, ses->wtd_target, ses->wtd_target_base);
   3084 		if (!fd)
   3085 			fd = ses->screen;
   3086 		if (fd && fd->rq
   3087 		    && !strcmp(cast_const_char fd->rq->url, cast_const_char u))
   3088 			reloadlevel = NC_ALWAYS_CACHE;
   3089 	}
   3090 
   3091 	if (from_goto_dialog)
   3092 		allow_flags = ALLOW_ALL;
   3093 	else if (df && df->rq)
   3094 		allow_flags = get_allow_flags(df->rq->url);
   3095 	else
   3096 		allow_flags = 0;
   3097 
   3098 	request_object(ses->term, u, prev_url, PRI_MAIN, reloadlevel,
   3099 	               allow_flags, ses_finished_1st_state, ses, &ses->rq);
   3100 	free(u);
   3101 }
   3102 
   3103 /* this doesn't send referer */
   3104 void
   3105 goto_url(void *ses_, unsigned char *url)
   3106 {
   3107 	struct session *ses = (struct session *)ses_;
   3108 	unsigned char *u = stracpy(url);
   3109 	goto_url_utf8(ses, u);
   3110 	free(u);
   3111 }
   3112 
   3113 void
   3114 goto_url_utf8(struct session *ses, unsigned char *url)
   3115 {
   3116 	goto_url_f(ses, NULL, url, NULL, NULL, -1, 0, 1, 0);
   3117 }
   3118 
   3119 /* this one sends referer */
   3120 void
   3121 goto_url_not_from_dialog(struct session *ses, unsigned char *url,
   3122                          struct f_data_c *df)
   3123 {
   3124 	goto_url_f(ses, NULL, url, cast_uchar "_top", df, -1, 0, 0, 0);
   3125 }
   3126 
   3127 static void
   3128 freeml_void(void *ml_)
   3129 {
   3130 	freeml((struct memory_list *)ml_);
   3131 }
   3132 
   3133 static void
   3134 ses_imgmap(struct session *ses)
   3135 {
   3136 	unsigned char *start;
   3137 	size_t len;
   3138 	struct memory_list *ml;
   3139 	struct menu_item *menu;
   3140 	struct f_data_c *fd;
   3141 	if (ses->rq->state != O_OK && ses->rq->state != O_INCOMPLETE)
   3142 		return;
   3143 	if (!(fd = current_frame(ses)) || !fd->f_data)
   3144 		return;
   3145 	if (get_file(ses->rq, &start, &len))
   3146 		return;
   3147 	if (len > INT_MAX)
   3148 		len = INT_MAX;
   3149 	d_opt = &fd->f_data->opt;
   3150 	if (get_image_map(ses->rq->ce->head, start, start + len,
   3151 	                  ses->goto_position, &menu, &ml, ses->imgmap_href_base,
   3152 	                  ses->imgmap_target_base, 0, ses->ds.assume_cp,
   3153 	                  ses->ds.hard_assume, 0)) {
   3154 		ses_abort_1st_state_loading(ses);
   3155 		return;
   3156 	}
   3157 	/*add_empty_window(ses->term, freeml_void, ml);*/
   3158 	do_menu_selected(ses->term, menu, ses, 0, freeml_void, ml);
   3159 	ses_abort_1st_state_loading(ses);
   3160 }
   3161 
   3162 void
   3163 goto_imgmap(struct session *ses, struct f_data_c *fd, unsigned char *url,
   3164             unsigned char *href, unsigned char *target)
   3165 {
   3166 	free(ses->imgmap_href_base);
   3167 	ses->imgmap_href_base = href;
   3168 	free(ses->imgmap_target_base);
   3169 	ses->imgmap_target_base = target;
   3170 	goto_url_f(ses, ses_imgmap, url, NULL, fd, -1, 0, 0, 0);
   3171 }
   3172 
   3173 void
   3174 map_selected(struct terminal *term, void *ld_, void *ses_)
   3175 {
   3176 	struct link_def *ld = (struct link_def *)ld_;
   3177 	struct session *ses = (struct session *)ses_;
   3178 	int x = 0;
   3179 	if (ld->onclick) {
   3180 		current_frame(ses);
   3181 		x = 1;
   3182 	}
   3183 	if (ld->link)
   3184 		goto_url_f(ses, NULL, ld->link, ld->target, current_frame(ses),
   3185 		           -1, x, 0, 0);
   3186 }
   3187 
   3188 void
   3189 go_back(struct session *ses, int num_steps)
   3190 {
   3191 	struct location *loc = NULL;
   3192 	struct list_head *lloc;
   3193 	int n;
   3194 	if (!num_steps)
   3195 		return;
   3196 	ses->reloadlevel = NC_CACHE;
   3197 	if (ses_abort_1st_state_loading(ses)) {
   3198 		change_screen_status(ses);
   3199 		print_screen_status(ses);
   3200 		return;
   3201 	}
   3202 	n = num_steps;
   3203 	if (num_steps > 0) {
   3204 		foreach (struct location, loc, lloc, ses->history) {
   3205 			if (!n--)
   3206 				goto have_loc;
   3207 		}
   3208 		return;
   3209 	} else {
   3210 		foreach (struct location, loc, lloc, ses->forward_history) {
   3211 			if (!++n)
   3212 				goto have_loc;
   3213 		}
   3214 		return;
   3215 	}
   3216 have_loc:
   3217 	ses->wtd = ses_go_back_to_2nd_state;
   3218 	ses->wtd_num_steps = num_steps;
   3219 	request_object(ses->term, loc->url, loc->prev_url, PRI_MAIN,
   3220 	               NC_ALWAYS_CACHE, ALLOW_ALL, ses_finished_1st_state, ses,
   3221 	               &ses->rq);
   3222 }
   3223 
   3224 static void
   3225 reload_frame(struct f_data_c *fd, int no_cache)
   3226 {
   3227 	unsigned char *u;
   3228 	if (!list_empty(fd->subframes)) {
   3229 		struct f_data_c *fdd = NULL;
   3230 		struct list_head *lfdd;
   3231 		foreach (struct f_data_c, fdd, lfdd, fd->subframes) {
   3232 			reload_frame(fdd, no_cache);
   3233 		}
   3234 		return;
   3235 	}
   3236 	if (!fd->rq)
   3237 		return;
   3238 	if (fd->f_data && !f_is_finished(fd->f_data))
   3239 		return;
   3240 	u = stracpy(fd->rq->url);
   3241 	release_object(&fd->rq);
   3242 	if (fd->f_data)
   3243 		release_object(&fd->f_data->rq);
   3244 	request_object(fd->ses->term, u, NULL, PRI_MAIN, no_cache, ALLOW_ALL,
   3245 	               fd_loaded, fd, &fd->rq);
   3246 	if (fd->f_data)
   3247 		clone_object(fd->rq, &fd->f_data->rq);
   3248 	fd->last_update = get_time();
   3249 	fd->next_update_interval = 0;
   3250 	fd->done = 0;
   3251 	fd->parsed_done = 0;
   3252 	free(u);
   3253 }
   3254 
   3255 void
   3256 reload(struct session *ses, int no_cache)
   3257 {
   3258 	if (no_cache == -1)
   3259 		no_cache = ++ses->reloadlevel;
   3260 	else
   3261 		ses->reloadlevel = no_cache;
   3262 	reload_frame(ses->screen, no_cache);
   3263 }
   3264 
   3265 static void
   3266 set_doc_view(struct session *ses)
   3267 {
   3268 	ses->screen->xp = 0;
   3269 	ses->screen->yp = 1;
   3270 	ses->screen->xw = ses->term->x;
   3271 	if (ses->term->y < 2)
   3272 		ses->screen->yw = 0;
   3273 	else
   3274 		ses->screen->yw = ses->term->y - 2;
   3275 }
   3276 
   3277 static struct session *
   3278 create_session(struct window *win)
   3279 {
   3280 	static int session_id = 1;
   3281 	struct terminal *term = win->term;
   3282 	struct session *ses;
   3283 	ses = mem_calloc(sizeof(struct session));
   3284 	init_list(ses->history);
   3285 	init_list(ses->forward_history);
   3286 	ses->term = term;
   3287 	ses->win = win;
   3288 	ses->id = session_id++;
   3289 	ses->screen = create_f_data_c(ses, NULL);
   3290 	ses->screen->xp = 0;
   3291 	ses->screen->xw = term->x;
   3292 	ses->screen->yp = 1;
   3293 	ses->screen->yw = term->y - 2;
   3294 	memcpy(&ses->ds, &dds, sizeof(struct document_setup));
   3295 	init_list(ses->format_cache);
   3296 	add_to_list(sessions, ses);
   3297 	if (first_use) {
   3298 		first_use = 0;
   3299 		msg_box(term, NULL, TEXT_(T_WELCOME), AL_CENTER,
   3300 		        TEXT_(T_WELCOME_TO_LINKS), cast_uchar "\n\n",
   3301 		        TEXT_(T_BASIC_HELP), MSG_BOX_END, NULL, 1, TEXT_(T_OK),
   3302 		        msg_box_null, B_ENTER | B_ESC);
   3303 	}
   3304 	return ses;
   3305 }
   3306 
   3307 /*vyrobi retezec znaku, ktery se posilaj skrz unix domain socket hlavni instanci
   3308    prohlizece
   3309    cp=cislo session odkud se ma kopirovat (kdyz se klikne na "open new window")
   3310    url=url kam se ma jit (v pripade kliknuti na "open link in new window" nebo
   3311    pusteni linksu z prikazove radky s url) framename=jmeno ramu, ktery se
   3312    vytvori
   3313 
   3314    vraci sekvenci bytu a delku
   3315  */
   3316 void *
   3317 create_session_info(int cp, unsigned char *url, unsigned char *framename,
   3318                     int *ll)
   3319 {
   3320 	size_t sl = strlen(cast_const_char url);
   3321 	size_t sl1 = framename ? strlen(cast_const_char framename) : 0;
   3322 	int l = (int)sl;
   3323 	int l1 = (int)sl1;
   3324 	unsigned char *i;
   3325 	if (sl > INT_MAX || sl1 > INT_MAX)
   3326 		overalloc();
   3327 	if (framename && !strcmp(cast_const_char framename, "_blank"))
   3328 		l1 = 0;
   3329 
   3330 	i = NULL;
   3331 	*ll = add_bytes_to_str(&i, 0, (unsigned char *)&cp, sizeof(int));
   3332 	*ll = add_bytes_to_str(&i, *ll, (unsigned char *)&l, sizeof(int));
   3333 	*ll = add_bytes_to_str(&i, *ll, (unsigned char *)&l1, sizeof(int));
   3334 	*ll = add_bytes_to_str(&i, *ll, url, l);
   3335 	*ll = add_bytes_to_str(&i, *ll, framename, l1);
   3336 	return i;
   3337 }
   3338 
   3339 /* dostane data z create_session_info a nainicializuje podle nich session
   3340    vraci -1 pokud jsou data vadna
   3341  */
   3342 /* FIXME: this is a disaster */
   3343 static int
   3344 read_session_info(struct session *ses, void *data, size_t len)
   3345 {
   3346 	int cpfrom, sz, sz1;
   3347 	struct session *s = NULL;
   3348 	struct list_head *ls;
   3349 	if (len < 3 * sizeof(int))
   3350 		return -1;
   3351 	cpfrom = *(int *)data;
   3352 	sz = *((int *)data + 1);
   3353 	sz1 = *((int *)data + 2);
   3354 	foreach (struct session, s, ls, sessions)
   3355 		if (s->id == cpfrom) {
   3356 			memcpy(&ses->ds, &s->ds, sizeof(struct document_setup));
   3357 			if (!sz) {
   3358 				if (!list_empty(s->history)) {
   3359 					struct location *loc = list_struct(
   3360 					    s->history.next, struct location);
   3361 					if (loc->url)
   3362 						goto_url_utf8(ses, loc->url);
   3363 				}
   3364 				return 0;
   3365 			} else
   3366 				break;
   3367 		}
   3368 	if (sz1) {
   3369 		unsigned char *tgt;
   3370 		if (len < 3 * sizeof(int) + sz + sz1)
   3371 			goto bla;
   3372 		if ((unsigned)sz1 >= INT_MAX)
   3373 			overalloc();
   3374 		tgt = xmalloc(sz1 + 1);
   3375 		memcpy(tgt, (unsigned char *)((int *)data + 3) + sz, sz1);
   3376 		tgt[sz1] = 0;
   3377 		free(ses->wanted_framename);
   3378 		ses->wanted_framename = NULL;
   3379 		ses->wanted_framename = tgt;
   3380 	}
   3381 bla:
   3382 	if (sz) {
   3383 		unsigned char *u, *uu;
   3384 		if (len < 3 * sizeof(int) + sz)
   3385 			return -1;
   3386 		if ((unsigned)sz >= INT_MAX)
   3387 			overalloc();
   3388 		u = xmalloc(sz + 1);
   3389 		memcpy(u, (int *)data + 3, sz);
   3390 		u[sz] = 0;
   3391 		uu = decode_url(u);
   3392 		goto_url(ses, uu);
   3393 		free(u);
   3394 		free(uu);
   3395 	}
   3396 	return 0;
   3397 }
   3398 
   3399 void
   3400 cleanup_session(struct session *ses)
   3401 {
   3402 	struct download *d = NULL;
   3403 	struct list_head *ld;
   3404 	foreach (struct download, d, ld, downloads)
   3405 		if (d->ses == ses && d->prog) {
   3406 			ld = ld->prev;
   3407 			abort_download(d);
   3408 		}
   3409 	ses_abort_1st_state_loading(ses);
   3410 	reinit_f_data_c(ses->screen);
   3411 	ses->screen->vs = NULL;
   3412 	while (!list_empty(ses->format_cache)) {
   3413 		struct f_data *f =
   3414 		    list_struct(ses->format_cache.next, struct f_data);
   3415 		del_from_list(f);
   3416 		destroy_formatted(f);
   3417 	}
   3418 	while (!list_empty(ses->history))
   3419 		destroy_location(
   3420 		    list_struct(ses->history.next, struct location));
   3421 	clear_forward_history(ses);
   3422 	free(ses->default_status);
   3423 	free(ses->search_word);
   3424 	ses->default_status = NULL;
   3425 	ses->search_word = NULL;
   3426 }
   3427 
   3428 void
   3429 destroy_session(struct session *ses)
   3430 {
   3431 	cleanup_session(ses);
   3432 	free(ses->screen);
   3433 	free(ses->st);
   3434 	free(ses->st_old);
   3435 	ses->st = NULL;
   3436 	ses->st_old = NULL;
   3437 	free(ses->dn_url);
   3438 	free(ses->last_search_word);
   3439 	free(ses->imgmap_href_base);
   3440 	free(ses->imgmap_target_base);
   3441 	free(ses->wanted_framename);
   3442 
   3443 	release_object(&ses->tq);
   3444 	free(ses->tq_prog);
   3445 
   3446 	del_from_list(ses);
   3447 }
   3448 
   3449 static void
   3450 move_session_to_front(struct session *ses)
   3451 {
   3452 	if (sessions.next == &ses->list_entry)
   3453 		return;
   3454 	del_from_list(ses);
   3455 	add_to_list(sessions, ses);
   3456 }
   3457 
   3458 void
   3459 win_func(struct window *win, struct links_event *ev, int fw)
   3460 {
   3461 	struct session *ses = win->data;
   3462 	switch ((int)ev->ev) {
   3463 	case EV_ABORT:
   3464 		if (ses)
   3465 			destroy_session(ses);
   3466 		break;
   3467 	case EV_INIT:
   3468 		ses = win->data = create_session(win);
   3469 		if (read_session_info(ses, (char *)(ev->b + sizeof(int)),
   3470 		                      ev->b)) {
   3471 			register_bottom_half(destroy_terminal, win->term);
   3472 			return;
   3473 		}
   3474 		/*-fallthrough*/
   3475 	case EV_RESIZE:
   3476 		move_session_to_front(ses);
   3477 		free(ses->st_old);
   3478 		ses->st_old = NULL;
   3479 		set_doc_view(ses);
   3480 		html_interpret_recursive(ses->screen);
   3481 		draw_fd(ses->screen);
   3482 		break;
   3483 	case EV_REDRAW:
   3484 		free(ses->st_old);
   3485 		ses->st_old = NULL;
   3486 		draw_formatted(ses);
   3487 		break;
   3488 	case EV_MOUSE:
   3489 		/*FALLTHROUGH*/
   3490 	case EV_KBD:
   3491 		if (ev->ev == EV_KBD || (ev->b & BM_ACT) != B_MOVE
   3492 		    || BM_IS_WHEEL(ev->b))
   3493 			move_session_to_front(ses);
   3494 		send_event(ses, ev);
   3495 		break;
   3496 	case EV_EXTRA:
   3497 		if (ev->x == EV_EXTRA_OPEN_URL)
   3498 			goto_url(ses, (unsigned char *)ev->b);
   3499 		break;
   3500 	default:
   3501 		die("session.c win_func(): unknown event\n");
   3502 	}
   3503 }
   3504 
   3505 /*
   3506   Gets the url being viewed by this session. Writes it into str.
   3507   A maximum of str_size bytes (including null) will be written.
   3508 */
   3509 unsigned char *
   3510 get_current_url(struct session *ses, unsigned char *str, size_t str_size)
   3511 {
   3512 	unsigned char *here;
   3513 	size_t url_len = 0;
   3514 
   3515 	/* Not looking at anything */
   3516 	if (list_empty(ses->history))
   3517 		return NULL;
   3518 
   3519 	here = display_url(ses->term, cur_loc(ses)->url, 0);
   3520 	url_len = strlen(cast_const_char here);
   3521 
   3522 	/* Ensure that the url size is not greater than str_size */
   3523 	if (url_len >= str_size)
   3524 		url_len = str_size - 1;
   3525 
   3526 	safe_strncpy(str, here, url_len + 1);
   3527 
   3528 	free(here);
   3529 
   3530 	return str;
   3531 }
   3532 
   3533 /*
   3534   Gets the title of the page being viewed by this session. Writes it into str.
   3535   A maximum of str_size bytes (including null) will be written.
   3536 */
   3537 unsigned char *
   3538 get_current_title(struct f_data_c *fd, unsigned char *str, size_t str_size)
   3539 {
   3540 	/* Ensure that the title is defined */
   3541 	if (!fd || !fd->f_data)
   3542 		return NULL;
   3543 
   3544 	safe_strncpy(str, fd->f_data->title, str_size);
   3545 
   3546 	return str;
   3547 }