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