1 /* types.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 
9 /*------------------------ ASSOCIATIONS -----------------------*/
10 
11 /* DECLARATIONS */
12 
13 static void assoc_edit_item(struct dialog_data *, struct list *, void (*)(struct dialog_data *, struct list *, struct list *, struct list_description *), struct list *, unsigned char);
14 static void assoc_copy_item(struct list *, struct list *);
15 static struct list *assoc_new_item(void *);
16 static void assoc_delete_item(struct list *);
17 static struct list *assoc_find_item(struct list *start, unsigned char *str, int direction);
18 static unsigned char *assoc_type_item(struct terminal *, struct list *, int);
19 
20 
21 struct list assoc = { init_list_1st(&assoc.list_entry) 0, -1, NULL, init_list_last(&assoc.list_entry) };
22 
23 static struct history assoc_search_history = { 0, { &assoc_search_history.items, &assoc_search_history.items } };
24 
25 struct assoc_ok_struct {
26 	void (*fn)(struct dialog_data *, struct list *, struct list *, struct list_description *);
27 	struct list *data;
28 	struct dialog_data *dlg;
29 };
30 
31 
32 static struct list_description assoc_ld={
33 	0,  /* 0= flat; 1=tree */
34 	&assoc,  /* list */
35 	assoc_new_item,
36 	assoc_edit_item,
37 	NULL,
38 	assoc_delete_item,
39 	assoc_copy_item,
40 	assoc_type_item,
41 	assoc_find_item,
42 	&assoc_search_history,
43 	0,		/* this is set in init_assoc function */
44 	15,  /* # of items in main window */
45 	T_ASSOCIATION,
46 	T_ASSOCIATIONS_ALREADY_IN_USE,
47 	T_ASSOCIATIONS_MANAGER,
48 	T_DELETE_ASSOCIATION,
49 	0,	/* no button */
50 	NULL,	/* no button */
51 	NULL,	/* no save*/
52 
53 	NULL,NULL,0,0,  /* internal vars */
54 	0, /* modified */
55 	NULL,
56 	NULL,
57 	1,
58 };
59 
assoc_new_item(void * ignore)60 static struct list *assoc_new_item(void *ignore)
61 {
62 	struct assoc *neww;
63 
64 	neww = mem_calloc(sizeof(struct assoc));
65 	neww->label = stracpy(cast_uchar "");
66 	neww->ct = stracpy(cast_uchar "");
67 	neww->prog = stracpy(cast_uchar "");
68 	neww->block = neww->xwin = neww->cons = 1;
69 	neww->ask = 1;
70 	neww->accept_http = 0;
71 	neww->accept_ftp = 0;
72 	neww->head.type = 0;
73 	neww->system = SYSTEM_ID;
74 	return &neww->head;
75 }
76 
assoc_delete_item(struct list * data)77 static void assoc_delete_item(struct list *data)
78 {
79 	struct assoc *del = get_struct(data, struct assoc, head);
80 
81 	if (del->head.list_entry.next) del_from_list(&del->head);
82 	mem_free(del->label);
83 	mem_free(del->ct);
84 	mem_free(del->prog);
85 	mem_free(del);
86 }
87 
assoc_copy_item(struct list * in,struct list * out)88 static void assoc_copy_item(struct list *in, struct list *out)
89 {
90 	struct assoc *item_in = get_struct(in, struct assoc, head);
91 	struct assoc *item_out = get_struct(out, struct assoc, head);
92 
93 	item_out->cons = item_in->cons;
94 	item_out->xwin = item_in->xwin;
95 	item_out->block = item_in->block;
96 	item_out->ask = item_in->ask;
97 	item_out->accept_http = item_in->accept_http;
98 	item_out->accept_ftp = item_in->accept_ftp;
99 	item_out->system = item_in->system;
100 
101 	mem_free(item_out->label);
102 	mem_free(item_out->ct);
103 	mem_free(item_out->prog);
104 
105 	item_out->label = stracpy(item_in->label);
106 	item_out->ct = stracpy(item_in->ct);
107 	item_out->prog = stracpy(item_in->prog);
108 }
109 
110 /* allocate string and print association into it */
111 /* x: 0=type all, 1=type title only */
assoc_type_item(struct terminal * term,struct list * data,int x)112 static unsigned char *assoc_type_item(struct terminal *term, struct list *data, int x)
113 {
114 	unsigned char *txt, *txt1;
115 	struct assoc *item;
116 
117 	if (data == &assoc) return stracpy(get_text_translation(TEXT_(T_ASSOCIATIONS), term));
118 
119 	item = get_struct(data, struct assoc, head);
120 	txt = stracpy(cast_uchar "");
121 	if (item->system != SYSTEM_ID) add_to_strn(&txt, cast_uchar "XX ");
122 	add_to_strn(&txt, item->label);
123 	add_to_strn(&txt, cast_uchar ": ");
124 	add_to_strn(&txt, item->ct);
125 	if (!x) {
126 		add_to_strn(&txt, cast_uchar " -> ");
127 		if (item->prog) add_to_strn(&txt, item->prog);
128 	}
129 	txt1 = convert(assoc_ld.codepage, term_charset(term), txt, NULL);
130 	mem_free(txt);
131 
132 	return txt1;
133 }
134 
menu_assoc_manager(struct terminal * term,void * fcp,void * ses_)135 void menu_assoc_manager(struct terminal *term, void *fcp, void *ses_)
136 {
137 	struct session *ses = (struct session *)ses_;
138 	create_list_window(&assoc_ld,&assoc, term, ses);
139 }
140 
141 static unsigned char * const ct_msg[] = {
142 	TEXT_(T_LABEL),
143 	TEXT_(T_CONTENT_TYPES),
144 	TEXT_(T_PROGRAM__IS_REPLACED_WITH_FILE_NAME),
145 #ifdef ASSOC_BLOCK
146 	TEXT_(T_BLOCK_TERMINAL_WHILE_PROGRAM_RUNNING),
147 #endif
148 #ifdef ASSOC_CONS_XWIN
149 	TEXT_(T_RUN_ON_TERMINAL),
150 	TEXT_(T_RUN_IN_XWINDOW),
151 #endif
152 	TEXT_(T_ASK_BEFORE_OPENING),
153 	TEXT_(T_ACCEPT_HTTP),
154 	TEXT_(T_ACCEPT_FTP),
155 };
156 
assoc_edit_item_fn(struct dialog_data * dlg)157 static void assoc_edit_item_fn(struct dialog_data *dlg)
158 {
159 	struct terminal *term = dlg->win->term;
160 	int max = 0, min = 0;
161 	int w, rw;
162 	int y = gf_val(-1, -G_BFU_FONT_SIZE);
163 	int p = 3;
164 	if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
165 #ifdef ASSOC_BLOCK
166 	p++;
167 #endif
168 #ifdef ASSOC_CONS_XWIN
169 	p += 2;
170 #endif
171 	max_text_width(term, ct_msg[0], &max, AL_LEFT);
172 	min_text_width(term, ct_msg[0], &min, AL_LEFT);
173 	max_text_width(term, ct_msg[1], &max, AL_LEFT);
174 	min_text_width(term, ct_msg[1], &min, AL_LEFT);
175 	max_text_width(term, ct_msg[2], &max, AL_LEFT);
176 	min_text_width(term, ct_msg[2], &min, AL_LEFT);
177 	max_group_width(term, ct_msg + 3, dlg->items + 3, p, &max);
178 	min_group_width(term, ct_msg + 3, dlg->items + 3, p, &min);
179 	max_buttons_width(term, dlg->items + 3 + p, 2, &max);
180 	min_buttons_width(term, dlg->items + 3 + p, 2, &min);
181 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
182 	if (w > max) w = max;
183 	if (w < min) w = min;
184 	if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
185 	if (w < 1) w = 1;
186 	rw = 0;
187 	dlg_format_text_and_field(dlg, NULL, get_text_translation(ct_msg[0], term), &dlg->items[0], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
188 	y += gf_val(1, G_BFU_FONT_SIZE * 1);
189 	dlg_format_text_and_field(dlg, NULL, get_text_translation(ct_msg[1], term), &dlg->items[1], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
190 	y += gf_val(1, G_BFU_FONT_SIZE * 1);
191 	dlg_format_text_and_field(dlg, NULL, get_text_translation(ct_msg[2], term), &dlg->items[2], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
192 	y += gf_val(1, G_BFU_FONT_SIZE * 1);
193 	dlg_format_group(dlg, NULL, ct_msg + 3, dlg->items + 3, p, 0, &y, w, &rw);
194 	y += gf_val(1, G_BFU_FONT_SIZE);
195 	dlg_format_buttons(dlg, NULL, dlg->items + 3 + p, 2, 0, &y, w, &rw, AL_CENTER);
196 	w = rw;
197 	dlg->xw = w + 2 * DIALOG_LB;
198 	dlg->yw = y + 2 * DIALOG_TB;
199 	center_dlg(dlg);
200 	draw_dlg(dlg);
201 	y = dlg->y + DIALOG_TB;
202 	if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
203 	dlg_format_text_and_field(dlg, term, ct_msg[0], &dlg->items[0], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
204 	y += gf_val(1, G_BFU_FONT_SIZE);
205 	dlg_format_text_and_field(dlg, term, ct_msg[1], &dlg->items[1], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
206 	y += gf_val(1, G_BFU_FONT_SIZE);
207 	dlg_format_text_and_field(dlg, term, ct_msg[2], &dlg->items[2], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
208 	y += gf_val(1, G_BFU_FONT_SIZE);
209 	dlg_format_group(dlg, term, ct_msg + 3, &dlg->items[3], p, dlg->x + DIALOG_LB, &y, w, NULL);
210 	y += gf_val(1, G_BFU_FONT_SIZE);
211 	dlg_format_buttons(dlg, term, &dlg->items[3 + p], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
212 }
213 
214 /* Puts url and title into the bookmark item */
assoc_edit_done(void * data)215 static void assoc_edit_done(void *data)
216 {
217 	struct dialog *d = (struct dialog *)data;
218 	struct assoc *item = (struct assoc *)d->udata;
219 	struct assoc_ok_struct *s = (struct assoc_ok_struct *)d->udata2;
220 	unsigned char *txt;
221 	unsigned char *label, *ct, *prog;
222 
223 	label = (unsigned char *)&d->items[12];
224 	ct = label + MAX_STR_LEN;
225 	prog = ct + MAX_STR_LEN;
226 
227 	txt = convert(term_charset(s->dlg->win->term), assoc_ld.codepage, label, NULL);
228 	mem_free(item->label);
229 	item->label = txt;
230 
231 	txt = convert(term_charset(s->dlg->win->term), assoc_ld.codepage, ct, NULL);
232 	mem_free(item->ct);
233 	item->ct = txt;
234 
235 	txt = convert(term_charset(s->dlg->win->term), assoc_ld.codepage, prog, NULL);
236 	mem_free(item->prog);
237 	item->prog = txt;
238 
239 	s->fn(s->dlg, s->data, &item->head, &assoc_ld);
240 	d->udata = NULL;  /* for abort function */
241 }
242 
243 /* destroys an item, this function is called when edit window is aborted */
assoc_edit_abort(struct dialog_data * data)244 static void assoc_edit_abort(struct dialog_data *data)
245 {
246 	struct dialog *dlg = data->dlg;
247 	struct assoc *item = (struct assoc *)dlg->udata;
248 
249 	mem_free(dlg->udata2);
250 	if (item) assoc_delete_item(&item->head);
251 }
252 
assoc_edit_item(struct dialog_data * dlg,struct list * data,void (* ok_fn)(struct dialog_data *,struct list *,struct list *,struct list_description *),struct list * ok_arg,unsigned char dlg_title)253 static void assoc_edit_item(struct dialog_data *dlg, struct list *data, void (*ok_fn)(struct dialog_data *, struct list *, struct list *, struct list_description *), struct list *ok_arg, unsigned char dlg_title)
254 {
255 	int p;
256 	struct assoc *neww = get_struct(data, struct assoc, head);
257 	struct terminal *term = dlg->win->term;
258 	struct dialog *d;
259 	struct assoc_ok_struct *s;
260 	unsigned char *ct, *prog, *label;
261 
262 	d = mem_calloc(sizeof(struct dialog) + 11 * sizeof(struct dialog_item) + 3 * MAX_STR_LEN);
263 
264 	label = (unsigned char *)&d->items[12];
265 	ct = label + MAX_STR_LEN;
266 	prog = ct + MAX_STR_LEN;
267 
268 	safe_strncpy(label, neww->label, MAX_STR_LEN);
269 	safe_strncpy(ct, neww->ct, MAX_STR_LEN);
270 	safe_strncpy(prog, neww->prog, MAX_STR_LEN);
271 
272 	/* Create the dialog */
273 	s = mem_alloc(sizeof(struct assoc_ok_struct));
274 	s->fn = ok_fn;
275 	s->data = ok_arg;
276 	s->dlg = dlg;
277 
278 	switch (dlg_title) {
279 		case TITLE_EDIT:
280 			d->title = TEXT_(T_EDIT_ASSOCIATION);
281 			break;
282 
283 		case TITLE_ADD:
284 			d->title = TEXT_(T_ADD_ASSOCIATION);
285 			break;
286 
287 		default:
288 			internal_error("Unsupported dialog title.\n");
289 	}
290 
291 	d->udata = neww;
292 	d->udata2 = s;
293 	d->fn = assoc_edit_item_fn;
294 	d->abort = assoc_edit_abort;
295 	d->refresh = assoc_edit_done;
296 	d->refresh_data = d;
297 	d->items[0].type = D_FIELD;
298 	d->items[0].dlen = MAX_STR_LEN;
299 	d->items[0].data = label;
300 	d->items[0].fn = check_nonempty;
301 	d->items[1].type = D_FIELD;
302 	d->items[1].dlen = MAX_STR_LEN;
303 	d->items[1].data = ct;
304 	d->items[1].fn = check_nonempty;
305 	d->items[2].type = D_FIELD;
306 	d->items[2].dlen = MAX_STR_LEN;
307 	d->items[2].data = prog;
308 	d->items[2].fn = check_nonempty;
309 	p = 3;
310 #ifdef ASSOC_BLOCK
311 	d->items[p].type = D_CHECKBOX;
312 	d->items[p].data = (unsigned char *)&neww->block;
313 	d->items[p++].dlen = sizeof(int);
314 #endif
315 #ifdef ASSOC_CONS_XWIN
316 	d->items[p].type = D_CHECKBOX;
317 	d->items[p].data = (unsigned char *)&neww->cons;
318 	d->items[p++].dlen = sizeof(int);
319 	d->items[p].type = D_CHECKBOX;
320 	d->items[p].data = (unsigned char *)&neww->xwin;
321 	d->items[p++].dlen = sizeof(int);
322 #endif
323 	d->items[p].type = D_CHECKBOX;
324 	d->items[p].data = (unsigned char *)&neww->ask;
325 	d->items[p++].dlen = sizeof(int);
326 	d->items[p].type = D_CHECKBOX;
327 	d->items[p].data = (unsigned char *)&neww->accept_http;
328 	d->items[p++].dlen = sizeof(int);
329 	d->items[p].type = D_CHECKBOX;
330 	d->items[p].data = (unsigned char *)&neww->accept_ftp;
331 	d->items[p++].dlen = sizeof(int);
332 	d->items[p].type = D_BUTTON;
333 	d->items[p].gid = B_ENTER;
334 	d->items[p].fn = ok_dialog;
335 	d->items[p++].text = TEXT_(T_OK);
336 	d->items[p].type = D_BUTTON;
337 	d->items[p].gid = B_ESC;
338 	d->items[p].text = TEXT_(T_CANCEL);
339 	d->items[p++].fn = cancel_dialog;
340 	d->items[p++].type = D_END;
341 	do_dialog(term, d, getml(d, NULL));
342 }
343 
assoc_test_entry(struct list * e,unsigned char * str)344 static int assoc_test_entry(struct list *e, unsigned char *str)
345 {
346 	struct assoc *a = get_struct(e, struct assoc, head);
347 	return casestrstr(a->label, str) || casestrstr(a->ct, str);
348 }
349 
assoc_find_item(struct list * s,unsigned char * str,int direction)350 static struct list *assoc_find_item(struct list *s, unsigned char *str, int direction)
351 {
352 	struct list *e;
353 
354 	if (direction >= 0) {
355 		for (e = list_next(s); e != s; e = list_next(e)) {
356 			if (e->depth >= 0 && assoc_test_entry(e, str))
357 				return e;
358 		}
359 	} else {
360 		for (e = list_prev(s); e != s; e = list_prev(e)) {
361 			if (e->depth >= 0 && assoc_test_entry(e, str))
362 				return e;
363 		}
364 	}
365 
366 	if (e->depth >= 0 && assoc_test_entry(e, str))
367 		return e;
368 
369 	return NULL;
370 }
371 
372 
update_assoc(struct assoc * neww)373 void update_assoc(struct assoc *neww)
374 {
375 	struct assoc *repl;
376 	struct list *r;
377 	struct list_head *lr;
378 	if (!neww->label[0] || !neww->ct[0] || !neww->prog[0]) return;
379 	foreach(struct list, r, lr, assoc.list_entry) {
380 		repl = get_struct(r, struct assoc, head);
381 		if (!strcmp(cast_const_char repl->label, cast_const_char neww->label)
382 		 && !strcmp(cast_const_char repl->ct, cast_const_char neww->ct)
383 		 && !strcmp(cast_const_char repl->prog, cast_const_char neww->prog)
384 		 && repl->block == neww->block
385 		 && repl->cons == neww->cons
386 		 && repl->xwin == neww->xwin
387 		 && repl->ask == neww->ask
388 		 && repl->accept_http == neww->accept_http
389 		 && repl->accept_ftp == neww->accept_ftp
390 		 && repl->system == neww->system) {
391 			del_from_list(&repl->head);
392 			add_to_list(assoc.list_entry, &repl->head);
393 			return;
394 		}
395 	}
396 	repl = mem_calloc(sizeof(struct assoc));
397 	repl->label = stracpy(neww->label);
398 	repl->ct = stracpy(neww->ct);
399 	repl->prog = stracpy(neww->prog);
400 	repl->block = neww->block;
401 	repl->cons = neww->cons;
402 	repl->xwin = neww->xwin;
403 	repl->ask = neww->ask;
404 	repl->accept_http = neww->accept_http;
405 	repl->accept_ftp = neww->accept_ftp;
406 	repl->system = neww->system;
407 	repl->head.type = 0;
408 	add_to_list(assoc.list_entry, &repl->head);
409 }
410 
411 /*------------------------ EXTENSIONS -----------------------*/
412 
413 /* DECLARATIONS */
414 static void ext_edit_item(struct dialog_data *, struct list *, void (*)(struct dialog_data *, struct list *, struct list *, struct list_description *), struct list *, unsigned char);
415 static void ext_copy_item(struct list *, struct list *);
416 static struct list *ext_new_item(void *);
417 static void ext_delete_item(struct list *);
418 static struct list *ext_find_item(struct list *start, unsigned char *str, int direction);
419 static unsigned char *ext_type_item(struct terminal *, struct list *, int);
420 
421 struct list extensions = { init_list_1st(&extensions.list_entry) 0, -1, NULL, init_list_last(&extensions.list_entry)  };
422 
423 static struct history ext_search_history = { 0, { &ext_search_history.items, &ext_search_history.items } };
424 
425 
426 static struct list_description ext_ld={
427 	0,  /* 0= flat; 1=tree */
428 	&extensions,  /* list */
429 	ext_new_item,
430 	ext_edit_item,
431 	NULL,
432 	ext_delete_item,
433 	ext_copy_item,
434 	ext_type_item,
435 	ext_find_item,
436 	&ext_search_history,
437 	0,		/* this is set in init_assoc function */
438 	15,  /* # of items in main window */
439 	T_eXTENSION,
440 	T_EXTENSIONS_ALREADY_IN_USE,
441 	T_EXTENSIONS_MANAGER,
442 	T_DELETE_EXTENSION,
443 	0,	/* no button */
444 	NULL,	/* no button */
445 	NULL,	/* no save*/
446 
447 	NULL,NULL,0,0,  /* internal vars */
448 	0, /* modified */
449 	NULL,
450 	NULL,
451 	0,
452 };
453 
454 
455 
456 
457 
ext_new_item(void * ignore)458 static struct list *ext_new_item(void *ignore)
459 {
460 	struct extension *neww;
461 
462 	neww = mem_calloc(sizeof(struct extension));
463 	neww->ext = stracpy(cast_uchar "");
464 	neww->ct = stracpy(cast_uchar "");
465 	neww->head.type = 0;
466 	return &neww->head;
467 }
468 
469 
ext_delete_item(struct list * data)470 static void ext_delete_item(struct list *data)
471 {
472 	struct extension *del = get_struct(data, struct extension, head);
473 
474 	if (del->head.list_entry.next) del_from_list(&del->head);
475 	if (del->ext) mem_free(del->ext);
476 	if (del->ct) mem_free(del->ct);
477 	mem_free(del);
478 }
479 
480 
ext_copy_item(struct list * in,struct list * out)481 static void ext_copy_item(struct list *in, struct list *out)
482 {
483 	struct extension *item_in = get_struct(in, struct extension, head);
484 	struct extension *item_out = get_struct(out, struct extension, head);
485 
486 	mem_free(item_out->ext);
487 	mem_free(item_out->ct);
488 
489 	item_out->ext = stracpy(item_in->ext);
490 	item_out->ct = stracpy(item_in->ct);
491 }
492 
493 
494 /* allocate string and print extension into it */
495 /* x: 0=type all, 1=type title only */
ext_type_item(struct terminal * term,struct list * data,int x)496 static unsigned char *ext_type_item(struct terminal *term, struct list *data, int x)
497 {
498 	unsigned char *txt, *txt1;
499 	struct extension *item;
500 
501 	if (data == &extensions) return stracpy(get_text_translation(TEXT_(T_FILE_EXTENSIONS), term));
502 
503 	item = get_struct(data, struct extension, head);
504 	txt = stracpy(item->ext);
505 	add_to_strn(&txt, cast_uchar ": ");
506 	add_to_strn(&txt, item->ct);
507 	txt1 = convert(assoc_ld.codepage, term_charset(term), txt, NULL);
508 	mem_free(txt);
509 
510 	return txt1;
511 }
512 
513 
menu_ext_manager(struct terminal * term,void * fcp,void * ses_)514 void menu_ext_manager(struct terminal *term, void *fcp, void *ses_)
515 {
516 	struct session *ses = (struct session *)ses_;
517 	create_list_window(&ext_ld, &extensions, term, ses);
518 }
519 
520 static unsigned char * const ext_msg[] = {
521 	TEXT_(T_EXTENSION_S),
522 	TEXT_(T_CONTENT_TYPE),
523 };
524 
ext_edit_item_fn(struct dialog_data * dlg)525 static void ext_edit_item_fn(struct dialog_data *dlg)
526 {
527 	struct terminal *term = dlg->win->term;
528 	int max = 0, min = 0;
529 	int w, rw;
530 	int y = gf_val(-1, -G_BFU_FONT_SIZE);
531 	if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
532 	max_text_width(term, ext_msg[0], &max, AL_LEFT);
533 	min_text_width(term, ext_msg[0], &min, AL_LEFT);
534 	max_text_width(term, ext_msg[1], &max, AL_LEFT);
535 	min_text_width(term, ext_msg[1], &min, AL_LEFT);
536 	max_buttons_width(term, dlg->items + 2, 2, &max);
537 	min_buttons_width(term, dlg->items + 2, 2, &min);
538 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
539 	if (w > max) w = max;
540 	if (w < min) w = min;
541 	if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
542 	if (w < 1) w = 1;
543 	rw = 0;
544 	dlg_format_text_and_field(dlg, NULL, ext_msg[0], &dlg->items[0], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
545 	y += gf_val(1, G_BFU_FONT_SIZE * 1);
546 	dlg_format_text_and_field(dlg, NULL, ext_msg[1], &dlg->items[1], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
547 	y += gf_val(1, G_BFU_FONT_SIZE * 1);
548 	dlg_format_buttons(dlg, NULL, dlg->items + 2, 2, 0, &y, w, &rw, AL_CENTER);
549 	w = rw;
550 	dlg->xw = w + 2 * DIALOG_LB;
551 	dlg->yw = y + 2 * DIALOG_TB;
552 	center_dlg(dlg);
553 	draw_dlg(dlg);
554 	y = dlg->y + DIALOG_TB;
555 	if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
556 	dlg_format_text_and_field(dlg, term, ext_msg[0], &dlg->items[0], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
557 	y += gf_val(1, G_BFU_FONT_SIZE);
558 	dlg_format_text_and_field(dlg, term, ext_msg[1], &dlg->items[1], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
559 	y += gf_val(1, G_BFU_FONT_SIZE);
560 	dlg_format_buttons(dlg, term, &dlg->items[2], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
561 }
562 
563 
564 /* Puts url and title into the bookmark item */
ext_edit_done(void * data)565 static void ext_edit_done(void *data)
566 {
567 	struct dialog *d = (struct dialog*)data;
568 	struct extension *item = (struct extension *)d->udata;
569 	struct assoc_ok_struct *s = (struct assoc_ok_struct *)d->udata2;
570 	unsigned char *txt;
571 	unsigned char *ext, *ct;
572 
573 	ext = (unsigned char *)&d->items[5];
574 	ct = ext + MAX_STR_LEN;
575 
576 	txt = convert(term_charset(s->dlg->win->term), ext_ld.codepage,ext, NULL);
577 	mem_free(item->ext);
578 	item->ext = txt;
579 
580 	txt = convert(term_charset(s->dlg->win->term), ext_ld.codepage,ct, NULL);
581 	mem_free(item->ct);
582 	item->ct=txt;
583 
584 	s->fn(s->dlg, s->data, &item->head, &ext_ld);
585 	d->udata = NULL;  /* for abort function */
586 }
587 
588 
589 /* destroys an item, this function is called when edit window is aborted */
ext_edit_abort(struct dialog_data * data)590 static void ext_edit_abort(struct dialog_data *data)
591 {
592 	struct dialog *dlg = data->dlg;
593 	struct extension *item = (struct extension *)dlg->udata;
594 
595 	mem_free(dlg->udata2);
596 	if (item) ext_delete_item(&item->head);
597 }
598 
599 
ext_edit_item(struct dialog_data * dlg,struct list * data,void (* ok_fn)(struct dialog_data *,struct list *,struct list *,struct list_description *),struct list * ok_arg,unsigned char dlg_title)600 static void ext_edit_item(struct dialog_data *dlg, struct list *data, void (*ok_fn)(struct dialog_data *, struct list *, struct list *, struct list_description *), struct list *ok_arg, unsigned char dlg_title)
601 {
602 	struct extension *neww = get_struct(data, struct extension, head);
603 	struct terminal *term = dlg->win->term;
604 	struct dialog *d;
605 	struct assoc_ok_struct *s;
606 	unsigned char *ext;
607 	unsigned char *ct;
608 
609 	d = mem_calloc(sizeof(struct dialog) + 4 * sizeof(struct dialog_item) + 2 * MAX_STR_LEN);
610 
611 	ext = (unsigned char *)&d->items[5];
612 	ct = ext + MAX_STR_LEN;
613 	safe_strncpy(ext, neww->ext, MAX_STR_LEN);
614 	safe_strncpy(ct, neww->ct, MAX_STR_LEN);
615 
616 	/* Create the dialog */
617 	s = mem_alloc(sizeof(struct assoc_ok_struct));
618 	s->fn = ok_fn;
619 	s->data = ok_arg;
620 	s->dlg = dlg;
621 
622 	switch (dlg_title) {
623 		case TITLE_EDIT:
624 			d->title = TEXT_(T_EDIT_EXTENSION);
625 			break;
626 
627 		case TITLE_ADD:
628 			d->title = TEXT_(T_ADD_EXTENSION);
629 			break;
630 
631 		default:
632 			internal_error("Unsupported dialog title.\n");
633 	}
634 
635 	d->udata = neww;
636 	d->udata2 = s;
637 	d->abort = ext_edit_abort;
638 	d->refresh = ext_edit_done;
639 	d->refresh_data = d;
640 	d->title = TEXT_(T_EXTENSION);
641 	d->fn = ext_edit_item_fn;
642 	d->items[0].type = D_FIELD;
643 	d->items[0].dlen = MAX_STR_LEN;
644 	d->items[0].data = ext;
645 	d->items[0].fn = check_nonempty;
646 	d->items[1].type = D_FIELD;
647 	d->items[1].dlen = MAX_STR_LEN;
648 	d->items[1].data = ct;
649 	d->items[1].fn = check_nonempty;
650 	d->items[2].type = D_BUTTON;
651 	d->items[2].gid = B_ENTER;
652 	d->items[2].fn = ok_dialog;
653 	d->items[2].text = TEXT_(T_OK);
654 	d->items[3].type = D_BUTTON;
655 	d->items[3].gid = B_ESC;
656 	d->items[3].text = TEXT_(T_CANCEL);
657 	d->items[3].fn = cancel_dialog;
658 	d->items[4].type = D_END;
659 	do_dialog(term, d, getml(d, NULL));
660 }
661 
662 
ext_test_entry(struct list * e,unsigned char * str)663 static int ext_test_entry(struct list *e, unsigned char *str)
664 {
665 	struct extension *ext = get_struct(e, struct extension, head);
666 	return casestrstr(ext->ext, str) || casestrstr(ext->ct, str);
667 }
668 
ext_find_item(struct list * s,unsigned char * str,int direction)669 static struct list *ext_find_item(struct list *s, unsigned char *str, int direction)
670 {
671 	struct list *e;
672 
673 	if (direction >= 0) {
674 		for (e = list_next(s); e != s; e = list_next(e)) {
675 			if (e->depth >= 0 && ext_test_entry(e, str))
676 				return e;
677 		}
678 	} else {
679 		for (e = list_prev(s); e != s; e = list_prev(e)) {
680 			if (e->depth >= 0 && ext_test_entry(e, str))
681 				return e;
682 		}
683 	}
684 
685 	if (e->depth >= 0 && ext_test_entry(e, str))
686 		return e;
687 
688 	return NULL;
689 }
690 
691 
update_ext(struct extension * neww)692 void update_ext(struct extension *neww)
693 {
694 	struct extension *repl;
695 	struct list *r;
696 	struct list_head *lr;
697 	if (!neww->ext[0] || !neww->ct[0]) return;
698 	foreach(struct list, r, lr, extensions.list_entry) {
699 		repl = get_struct(r, struct extension, head);
700 		if (!strcmp(cast_const_char repl->ext, cast_const_char neww->ext)
701 		 && !strcmp(cast_const_char repl->ct, cast_const_char neww->ct)) {
702 			del_from_list(&repl->head);
703 			add_to_list(extensions.list_entry, &repl->head);
704 			return;
705 		}
706 	}
707 	repl = mem_calloc(sizeof(struct extension));
708 	repl->ext = stracpy(neww->ext);
709 	repl->ct = stracpy(neww->ct);
710 	repl->head.type = 0;
711 	add_to_list(extensions.list_entry, &repl->head);
712 }
713 
update_prog(struct list_head * l,unsigned char * p,int s)714 void update_prog(struct list_head *l, unsigned char *p, int s)
715 {
716 	struct protocol_program *repl;
717 	struct list_head *lrepl;
718 	foreach(struct protocol_program, repl, lrepl, *l) if (repl->system == s) {
719 		mem_free(repl->prog);
720 		goto ss;
721 	}
722 	repl = mem_alloc(sizeof(struct protocol_program));
723 	add_to_list(*l, repl);
724 	repl->system = s;
725 ss:
726 	repl->prog = mem_alloc(MAX_STR_LEN);
727 	safe_strncpy(repl->prog, p, MAX_STR_LEN);
728 }
729 
get_prog(struct list_head * l)730 unsigned char *get_prog(struct list_head *l)
731 {
732 	struct protocol_program *repl;
733 	struct list_head *lrepl;
734 	foreach(struct protocol_program, repl, lrepl, *l) if (repl->system == SYSTEM_ID) return repl->prog;
735 	update_prog(l, cast_uchar "", SYSTEM_ID);
736 	foreach(struct protocol_program, repl, lrepl, *l) if (repl->system == SYSTEM_ID) return repl->prog;
737 	internal_error("get_prog: program was not added");
738 	return cast_uchar "";;
739 }
740 
741 
742 /* creates default extensions if extension list is empty */
create_initial_extensions(void)743 void create_initial_extensions(void)
744 {
745 	struct extension ext;
746 
747 	if (!list_empty(extensions.list_entry)) return;
748 
749 	/* here you can add any default extension you want */
750 	ext.ext = cast_uchar "xpm", ext.ct=cast_uchar "image/x-xpixmap", update_ext(&ext);
751 	ext.ext = cast_uchar "xls", ext.ct=cast_uchar "application/excel", update_ext(&ext);
752 	ext.ext = cast_uchar "xbm", ext.ct=cast_uchar "image/x-xbitmap", update_ext(&ext);
753 	ext.ext = cast_uchar "wav", ext.ct=cast_uchar "audio/x-wav", update_ext(&ext);
754 	ext.ext = cast_uchar "tiff,tif", ext.ct=cast_uchar "image/tiff", update_ext(&ext);
755 	ext.ext = cast_uchar "tga", ext.ct=cast_uchar "image/targa", update_ext(&ext);
756 	ext.ext = cast_uchar "sxw", ext.ct=cast_uchar "application/x-openoffice", update_ext(&ext);
757 	ext.ext = cast_uchar "swf", ext.ct=cast_uchar "application/x-shockwave-flash", update_ext(&ext);
758 	ext.ext = cast_uchar "svg", ext.ct=cast_uchar "image/svg+xml", update_ext(&ext);
759 	ext.ext = cast_uchar "sch", ext.ct=cast_uchar "application/gschem", update_ext(&ext);
760 	ext.ext = cast_uchar "rtf", ext.ct=cast_uchar "application/rtf", update_ext(&ext);
761 	ext.ext = cast_uchar "ra,rm,ram", ext.ct=cast_uchar "audio/x-pn-realaudio", update_ext(&ext);
762 	ext.ext = cast_uchar "qt,mov", ext.ct=cast_uchar "video/quicktime", update_ext(&ext);
763 	ext.ext = cast_uchar "ps,eps,ai", ext.ct=cast_uchar "application/postscript", update_ext(&ext);
764 	ext.ext = cast_uchar "ppt", ext.ct=cast_uchar "application/powerpoint", update_ext(&ext);
765 	ext.ext = cast_uchar "ppm", ext.ct=cast_uchar "image/x-portable-pixmap", update_ext(&ext);
766 	ext.ext = cast_uchar "pnm", ext.ct=cast_uchar "image/x-portable-anymap", update_ext(&ext);
767 	ext.ext = cast_uchar "png", ext.ct=cast_uchar "image/png", update_ext(&ext);
768 	ext.ext = cast_uchar "pgp", ext.ct=cast_uchar "application/pgp-signature", update_ext(&ext);
769 	ext.ext = cast_uchar "pgm", ext.ct=cast_uchar "image/x-portable-graymap", update_ext(&ext);
770 	ext.ext = cast_uchar "pdf", ext.ct=cast_uchar "application/pdf", update_ext(&ext);
771 	ext.ext = cast_uchar "pcb", ext.ct=cast_uchar "application/pcb", update_ext(&ext);
772 	ext.ext = cast_uchar "pbm", ext.ct=cast_uchar "image/x-portable-bitmap", update_ext(&ext);
773 	ext.ext = cast_uchar "mpeg,mpg,mpe", ext.ct=cast_uchar "video/mpeg", update_ext(&ext);
774 	ext.ext = cast_uchar "mp3", ext.ct=cast_uchar "audio/mpeg", update_ext(&ext);
775 	ext.ext = cast_uchar "mid,midi", ext.ct=cast_uchar "audio/midi", update_ext(&ext);
776 	ext.ext = cast_uchar "jpg,jpeg,jpe", ext.ct=cast_uchar "image/jpeg", update_ext(&ext);
777 	ext.ext = cast_uchar "grb", ext.ct=cast_uchar "application/gerber", update_ext(&ext);
778 	ext.ext = cast_uchar "gl", ext.ct=cast_uchar "video/gl", update_ext(&ext);
779 	ext.ext = cast_uchar "gif", ext.ct=cast_uchar "image/gif", update_ext(&ext);
780 	ext.ext = cast_uchar "gbr", ext.ct=cast_uchar "application/gerber", update_ext(&ext);
781 	ext.ext = cast_uchar "g", ext.ct=cast_uchar "application/brlcad", update_ext(&ext);
782 	ext.ext = cast_uchar "fli", ext.ct=cast_uchar "video/fli", update_ext(&ext);
783 	ext.ext = cast_uchar "dxf", ext.ct=cast_uchar "application/dxf", update_ext(&ext);
784 	ext.ext = cast_uchar "dvi", ext.ct=cast_uchar "application/x-dvi", update_ext(&ext);
785 	ext.ext = cast_uchar "dl", ext.ct=cast_uchar "video/dl", update_ext(&ext);
786 	ext.ext = cast_uchar "deb", ext.ct=cast_uchar "application/x-debian-package", update_ext(&ext);
787 	ext.ext = cast_uchar "avi", ext.ct=cast_uchar "video/x-msvideo", update_ext(&ext);
788 	ext.ext = cast_uchar "au,snd", ext.ct=cast_uchar "audio/basic", update_ext(&ext);
789 	ext.ext = cast_uchar "aif,aiff,aifc", ext.ct=cast_uchar "audio/x-aiff", update_ext(&ext);
790 }
791 
792 /* --------------------------- PROG -----------------------------*/
793 
794 
795 struct list_head mailto_prog = { &mailto_prog, &mailto_prog };
796 struct list_head telnet_prog = { &telnet_prog, &telnet_prog };
797 struct list_head tn3270_prog = { &tn3270_prog, &tn3270_prog };
798 struct list_head mms_prog = { &mms_prog, &mms_prog };
799 struct list_head magnet_prog = { &magnet_prog, &magnet_prog };
800 
801 
is_in_list(unsigned char * list,unsigned char * str,int l)802 static int is_in_list(unsigned char *list, unsigned char *str, int l)
803 {
804 	unsigned char *l2, *l3;
805 	if (!l) return 0;
806 	rep:
807 	while (*list && *list <= ' ') list++;
808 	if (!*list) return 0;
809 	for (l2 = list; *l2 && *l2 != ','; l2++)
810 		;
811 	for (l3 = l2 - 1; l3 >= list && *l3 <= ' '; l3--)
812 		;
813 	l3++;
814 	if (l3 - list == l && !casecmp(str, list, l)) return 1;
815 	list = l2;
816 	if (*list == ',') list++;
817 	goto rep;
818 }
819 
canonical_compressed_ext(unsigned char * ext,unsigned char * ext_end)820 static unsigned char *canonical_compressed_ext(unsigned char *ext, unsigned char *ext_end)
821 {
822 	size_t len;
823 	if (!ext_end) ext_end = cast_uchar strchr(cast_const_char ext, 0);
824 	len = ext_end - ext;
825 	if (len == 3 && !casecmp(ext, cast_uchar "tgz", 3)) return cast_uchar "gz";
826 	if (len == 3 && !casecmp(ext, cast_uchar "tbz", 3)) return cast_uchar "bz2";
827 	if (len == 3 && !casecmp(ext, cast_uchar "txz", 3)) return cast_uchar "xz";
828 	if (len == 6 && !casecmp(ext, cast_uchar "tar-gz", 3)) return cast_uchar "gz";
829 	if (len == 7 && !casecmp(ext, cast_uchar "tar-bz2", 3)) return cast_uchar "bz2";
830 	if (len == 6 && !casecmp(ext, cast_uchar "tar-xz", 3)) return cast_uchar "xz";
831 	return NULL;
832 }
833 
get_compress_by_extension(unsigned char * ext,unsigned char * ext_end)834 unsigned char *get_compress_by_extension(unsigned char *ext, unsigned char *ext_end)
835 {
836 	size_t len;
837 	unsigned char *x;
838 	if ((x = canonical_compressed_ext(ext, ext_end))) {
839 		ext = x;
840 		ext_end = cast_uchar strchr(cast_const_char x, 0);
841 	}
842 	len = ext_end - ext;
843 	if (len == 1 && !casecmp(ext, cast_uchar "z", 1)) return cast_uchar "compress";
844 	if (len == 2 && !casecmp(ext, cast_uchar "gz", 2)) return cast_uchar "gzip";
845 	if (len == 2 && !casecmp(ext, cast_uchar "br", 2)) return cast_uchar "br";
846 	if (len == 3 && !casecmp(ext, cast_uchar "zst", 3)) return cast_uchar "zstd";
847 	if (len == 3 && !casecmp(ext, cast_uchar "bz2", 3)) return cast_uchar "bzip2";
848 	if (len == 4 && !casecmp(ext, cast_uchar "lzma", 4)) return cast_uchar "lzma";
849 	if (len == 2 && !casecmp(ext, cast_uchar "xz", 2)) return cast_uchar "lzma2";
850 	if (len == 2 && !casecmp(ext, cast_uchar "lz", 2)) return cast_uchar "lzip";
851 	return NULL;
852 }
853 
get_content_type_by_extension(unsigned char * url)854 unsigned char *get_content_type_by_extension(unsigned char *url)
855 {
856 	struct list *l;
857 	struct list_head *ll;
858 	unsigned char *ct, *eod, *ext, *exxt;
859 	int extl, el;
860 	ext = NULL, extl = 0;
861 	if (!(ct = get_url_data(url))) ct = url;
862 	for (eod = ct; *eod && !end_of_dir(url, *eod); eod++)
863 		;
864 	for (; ct < eod; ct++)
865 		if (*ct == '.') {
866 			if (ext) {
867 				if (get_compress_by_extension(ct + 1, eod))
868 					break;
869 			}
870 			ext = ct + 1;
871 		} else if (dir_sep(*ct)) {
872 			ext = NULL;
873 		}
874 	if (ext) while (ext[extl] && ext[extl] != '.' && !dir_sep(ext[extl]) && !end_of_dir(url, ext[extl])) extl++;
875  	if (force_html ||
876 	    (extl == 3 && !casecmp(ext, cast_uchar "htm", 3)) ||
877 	    (extl == 4 && !casecmp(ext, cast_uchar "html", 4)) ||
878 	    (extl == 5 && !casecmp(ext, cast_uchar "xhtml", 5))) return stracpy(cast_uchar "text/html");
879 	foreach(struct list, l, ll, extensions.list_entry) {
880 		struct extension *e = get_struct(l, struct extension, head);
881 		unsigned char *fname = NULL;
882 		if (!(ct = get_url_data(url))) ct = url;
883 		for (; *ct && !end_of_dir(url, *ct); ct++)
884 			if (dir_sep(*ct)) fname = ct + 1;
885 		if (!fname) {
886 			if (is_in_list(e->ext, ext, extl)) return stracpy(e->ct);
887 		} else {
888 			int fnlen = 0;
889 			int x;
890 			while (fname[fnlen] && !end_of_dir(url, fname[fnlen])) fnlen++;
891 			for (x = 0; x < fnlen; x++) if (fname[x] == '.') if (is_in_list(e->ext, fname + x + 1, fnlen - x - 1)) return stracpy(e->ct);
892 		}
893 	}
894 
895 	if ((extl == 3 && !casecmp(ext, cast_uchar "jpg", 3)) ||
896 	    (extl == 4 && !casecmp(ext, cast_uchar "pjpg", 4))||
897 	    (extl == 4 && !casecmp(ext, cast_uchar "jpeg", 4))||
898 	    (extl == 5 && !casecmp(ext, cast_uchar "pjpeg", 5))) return stracpy(cast_uchar "image/jpeg");
899 	if ((extl == 3 && !casecmp(ext, cast_uchar "png", 3))) return stracpy(cast_uchar "image/png");
900 	if ((extl == 3 && !casecmp(ext, cast_uchar "gif", 3))) return stracpy(cast_uchar "image/gif");
901 	if ((extl == 3 && !casecmp(ext, cast_uchar "xbm", 3))) return stracpy(cast_uchar "image/x-xbitmap");
902 	if ((extl == 3 && !casecmp(ext, cast_uchar "tif", 3)) ||
903 	    (extl == 4 && !casecmp(ext, cast_uchar "tiff", 4))) return stracpy(cast_uchar "image/tiff");
904 	exxt = init_str(); el = 0;
905 	add_to_str(&exxt, &el, cast_uchar "application/x-");
906 	add_bytes_to_str(&exxt, &el, ext, extl);
907 	foreach(struct list, l, ll, assoc.list_entry) {
908 		struct assoc *a = get_struct(l, struct assoc, head);
909 		if (is_in_list(a->ct, exxt, el))
910 			return exxt;
911 	}
912 	mem_free(exxt);
913 	return NULL;
914 }
915 
get_content_type_by_header_and_extension(unsigned char * head,unsigned char * url)916 static unsigned char *get_content_type_by_header_and_extension(unsigned char *head, unsigned char *url)
917 {
918 	unsigned char *ct, *file;
919 	ct = get_content_type_by_extension(url);
920 	if (ct) return ct;
921 	file = get_filename_from_header(head);
922 	if (file) {
923 		ct = get_content_type_by_extension(file);
924 		mem_free(file);
925 		if (ct) return ct;
926 	}
927 	return NULL;
928 }
929 
get_extension_by_content_type(unsigned char * ct)930 static unsigned char *get_extension_by_content_type(unsigned char *ct)
931 {
932 	struct list *l;
933 	struct list_head *ll;
934 	unsigned char *x, *y;
935 	if (is_html_type(ct)) return stracpy(cast_uchar "html");
936 	foreach(struct list, l, ll, extensions.list_entry) {
937 		struct extension *e = get_struct(l, struct extension, head);
938 		if (!casestrcmp(e->ct, ct)) {
939 			x = stracpy(e->ext);
940 			if ((y = cast_uchar strchr(cast_const_char x, ','))) *y = 0;
941 			return x;
942 		}
943 	}
944 	if (!casestrcmp(ct, cast_uchar "image/jpeg") ||
945 	    !casestrcmp(ct, cast_uchar "image/jpg") ||
946 	    !casestrcmp(ct, cast_uchar "image/jpe") ||
947 	    !casestrcmp(ct, cast_uchar "image/pjpe") ||
948 	    !casestrcmp(ct, cast_uchar "image/pjpeg") ||
949 	    !casestrcmp(ct, cast_uchar "image/pjpg"))
950 		return stracpy(cast_uchar "jpg");
951 	if (!casestrcmp(ct, cast_uchar "image/png") ||
952 	    !casestrcmp(ct, cast_uchar "image/x-png"))
953 		return stracpy(cast_uchar "png");
954 	if (!casestrcmp(ct, cast_uchar "image/gif"))
955 		return stracpy(cast_uchar "gif");
956 	if (!casestrcmp(ct, cast_uchar "image/x-bitmap"))
957 		return stracpy(cast_uchar "xbm");
958 	if (!casestrcmp(ct, cast_uchar "image/tiff") ||
959 	    !casestrcmp(ct, cast_uchar "image/tif"))
960 		return stracpy(cast_uchar "tiff");
961 	if (!casestrcmp(ct, cast_uchar "image/svg") ||
962 	    !casestrcmp(ct, cast_uchar "image/svg+xml"))
963 		return stracpy(cast_uchar "svg");
964 	if (!cmpbeg(ct, cast_uchar "application/x-")) {
965 		x = ct + strlen("application/x-");
966 		if (casestrcmp(x, cast_uchar "z") &&
967 		    casestrcmp(x, cast_uchar "gz") &&
968 		    casestrcmp(x, cast_uchar "gzip") &&
969 		    casestrcmp(x, cast_uchar "br") &&
970 		    casestrcmp(x, cast_uchar "zst") &&
971 		    casestrcmp(x, cast_uchar "bz2") &&
972 		    casestrcmp(x, cast_uchar "bzip2") &&
973 		    casestrcmp(x, cast_uchar "lzma") &&
974 		    casestrcmp(x, cast_uchar "lzma2") &&
975 		    casestrcmp(x, cast_uchar "xz") &&
976 		    casestrcmp(x, cast_uchar "lz") &&
977 		    !strchr(cast_const_char x, '-') &&
978 		    strlen(cast_const_char x) <= 4) {
979 			return stracpy(x);
980 		}
981 	}
982 	return NULL;
983 }
984 
get_content_encoding_from_content_type(unsigned char * ct)985 static unsigned char *get_content_encoding_from_content_type(unsigned char *ct)
986 {
987 	if (!casestrcmp(ct, cast_uchar "application/x-gzip") ||
988 	    !casestrcmp(ct, cast_uchar "application/x-tgz") ||
989 	    !casestrcmp(ct, cast_uchar "application/x-gtar")) return cast_uchar "gzip";
990 	if (!casestrcmp(ct, cast_uchar "application/x-br")) return cast_uchar "br";
991 	if (!casestrcmp(ct, cast_uchar "application/x-zstd") ||
992 	    !casestrcmp(ct, cast_uchar "application/zstd")) return cast_uchar "zstd";
993 	if (!casestrcmp(ct, cast_uchar "application/x-bzip2") ||
994 	    !casestrcmp(ct, cast_uchar "application/x-bzip")) return cast_uchar "bzip2";
995 	if (!casestrcmp(ct, cast_uchar "application/x-lzma")) return cast_uchar "lzma";
996 	if (!casestrcmp(ct, cast_uchar "application/x-lzma2") ||
997 	    !casestrcmp(ct, cast_uchar "application/x-xz")) return cast_uchar "lzma2";
998 	if (!casestrcmp(ct, cast_uchar "application/x-lz") ||
999 	    !casestrcmp(ct, cast_uchar "application/x-lzip")) return cast_uchar "lzip";
1000 	return NULL;
1001 }
1002 
get_content_type(unsigned char * head,unsigned char * url)1003 unsigned char *get_content_type(unsigned char *head, unsigned char *url)
1004 {
1005 	unsigned char *ct;
1006 	int code;
1007 	if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
1008 		unsigned char *s;
1009 		if ((s = cast_uchar strchr(cast_const_char ct, ';'))) *s = 0;
1010 		while (*ct && ct[strlen(cast_const_char ct) - 1] <= ' ') ct[strlen(cast_const_char ct) - 1] = 0;
1011 		if (*ct == '"' && ct[1] && ct[strlen(cast_const_char ct) - 1] == '"') {
1012 			memmove(ct, ct + 1, strlen(cast_const_char ct));
1013 			ct[strlen(cast_const_char ct) - 1] = 0;
1014 		}
1015 		if (!casestrcmp(ct, cast_uchar "text/plain") ||
1016 		    !casestrcmp(ct, cast_uchar "application/octet-stream") ||
1017 		    !casestrcmp(ct, cast_uchar "application/octetstream") ||
1018 		    !casestrcmp(ct, cast_uchar "application/octet_stream") ||
1019 		    !casestrcmp(ct, cast_uchar "application/binary") ||
1020 		    !casestrcmp(ct, cast_uchar "application/x-www-form-urlencoded") ||
1021 		    get_content_encoding_from_content_type(ct)) {
1022 			unsigned char *ctt;
1023 			if (!get_http_code(head, &code, NULL) && code >= 300)
1024 				goto no_code_by_extension;
1025 			ctt = get_content_type_by_header_and_extension(head, url);
1026 			if (ctt) {
1027 				mem_free(ct);
1028 				return ctt;
1029 			}
1030 		}
1031 no_code_by_extension:
1032 		if (!*ct) mem_free(ct);
1033 		else return ct;
1034 	}
1035 	if (!get_http_code(head, &code, NULL) && code >= 300)
1036 		return stracpy(cast_uchar "text/html");
1037 	ct = get_content_type_by_header_and_extension(head, url);
1038 	if (ct) return ct;
1039 	return !force_html ? stracpy(cast_uchar "text/plain") : stracpy(cast_uchar "text/html");
1040 }
1041 
get_content_encoding(unsigned char * head,unsigned char * url,int just_ce)1042 unsigned char *get_content_encoding(unsigned char *head, unsigned char *url, int just_ce)
1043 {
1044 	unsigned char *ce, *ct, *ext, *extd;
1045 	unsigned char *u;
1046 	int code;
1047 	if ((ce = parse_http_header(head, cast_uchar "Content-Encoding", NULL))) return ce;
1048 	if (just_ce) return NULL;
1049 	if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
1050 		unsigned char *s;
1051 		if ((s = cast_uchar strchr(cast_const_char ct, ';'))) *s = 0;
1052 		ce = get_content_encoding_from_content_type(ct);
1053 		if (ce) {
1054 			mem_free(ct);
1055 			return stracpy(ce);
1056 		}
1057 		if (is_html_type(ct)) {
1058 			mem_free(ct);
1059 			return NULL;
1060 		}
1061 		mem_free(ct);
1062 	}
1063 	if (!get_http_code(head, &code, NULL) && code >= 300) return NULL;
1064 	if (!(ext = get_url_data(url))) ext = url;
1065 	for (u = ext; *u; u++) if (end_of_dir(url, *u)) goto skip_ext;
1066 	extd = cast_uchar strrchr(cast_const_char ext, '.');
1067 	if (extd) {
1068 		ce = get_compress_by_extension(extd + 1, cast_uchar strchr(cast_const_char(extd + 1), 0));
1069 		if (ce) return stracpy(ce);
1070 	}
1071 	skip_ext:
1072 	if ((ext = get_filename_from_header(head))) {
1073 		extd = cast_uchar strrchr(cast_const_char ext, '.');
1074 		if (extd) {
1075 			ce = get_compress_by_extension(extd + 1, cast_uchar strchr(cast_const_char(extd + 1), 0));
1076 			if (ce) {
1077 				mem_free(ext);
1078 				return stracpy(ce);
1079 			}
1080 		}
1081 		mem_free(ext);
1082 	}
1083 	return NULL;
1084 }
1085 
encoding_2_extension(unsigned char * encoding)1086 unsigned char *encoding_2_extension(unsigned char *encoding)
1087 {
1088 	if (!casestrcmp(encoding, cast_uchar "gzip") ||
1089 	    !casestrcmp(encoding, cast_uchar "x-gzip")) return cast_uchar "gz";
1090 	if (!casestrcmp(encoding, cast_uchar "compress") ||
1091 	    !casestrcmp(encoding, cast_uchar "x-compress")) return cast_uchar "Z";
1092 	if (!casestrcmp(encoding, cast_uchar "br")) return cast_uchar "br";
1093 	if (!casestrcmp(encoding, cast_uchar "zstd")) return cast_uchar "zst";
1094 	if (!casestrcmp(encoding, cast_uchar "bzip2")) return cast_uchar "bz2";
1095 	if (!casestrcmp(encoding, cast_uchar "lzma")) return cast_uchar "lzma";
1096 	if (!casestrcmp(encoding, cast_uchar "lzma2")) return cast_uchar "xz";
1097 	if (!casestrcmp(encoding, cast_uchar "lzip")) return cast_uchar "lz";
1098 	return NULL;
1099 }
1100 
1101 /* returns field with associations */
get_type_assoc(struct terminal * term,unsigned char * type,int * n)1102 struct assoc *get_type_assoc(struct terminal *term, unsigned char *type, int *n)
1103 {
1104 	struct assoc *assoc_array;
1105 	struct list *l;
1106 	struct list_head *ll;
1107 	int count=0;
1108 	foreach(struct list, l, ll, assoc.list_entry) {
1109 		struct assoc *a = get_struct(l, struct assoc, head);
1110 		if (a->system == SYSTEM_ID && (term->environment & ENV_XWIN ? a->xwin : a->cons) && is_in_list(a->ct, type, (int)strlen(cast_const_char type))) {
1111 			if (count == MAXINT) overalloc();
1112 			count++;
1113 		}
1114 	}
1115 	*n = count;
1116 	if (!count) return NULL;
1117 	if ((unsigned)count > MAXINT / sizeof(struct assoc)) overalloc();
1118 	assoc_array = mem_alloc(count * sizeof(struct assoc));
1119 	count = 0;
1120 	foreach(struct list, l, ll, assoc.list_entry) {
1121 		struct assoc *a = get_struct(l, struct assoc, head);
1122 		if (a->system == SYSTEM_ID && (term->environment & ENV_XWIN ? a->xwin : a->cons) && is_in_list(a->ct, type, (int)strlen(cast_const_char type))) {
1123 			assoc_array[count++] = *a;
1124 		}
1125 	}
1126 	return assoc_array;
1127 }
1128 
is_html_type(unsigned char * ct)1129 int is_html_type(unsigned char *ct)
1130 {
1131 	return	!casestrcmp(ct, cast_uchar "text/html") ||
1132 		!casestrcmp(ct, cast_uchar "text-html") ||
1133 		!casestrcmp(ct, cast_uchar "text/x-server-parsed-html") ||
1134 		!casestrcmp(ct, cast_uchar "text/xml") ||
1135 		!casecmp(ct, cast_uchar "application/xhtml", strlen("application/xhtml"));
1136 }
1137 
get_filename_from_header(unsigned char * head)1138 unsigned char *get_filename_from_header(unsigned char *head)
1139 {
1140 	int extended = 0;
1141 	unsigned char *ct, *x, *y, *codepage;
1142 	int ly;
1143 	int cp_idx;
1144 	if ((ct = parse_http_header(head, cast_uchar "Content-Disposition", NULL))) {
1145 		x = parse_header_param(ct, cast_uchar "filename*", 1);
1146 		if (x) extended = 1;
1147 		else x = parse_header_param(ct, cast_uchar "filename", 1);
1148 		mem_free(ct);
1149 		if (x) {
1150 			if (*x) goto ret_x;
1151 			mem_free(x);
1152 		}
1153 	}
1154 	if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
1155 		x = parse_header_param(ct, cast_uchar "name*", 0);
1156 		if (x) extended = 1;
1157 		else x = parse_header_param(ct, cast_uchar "name", 0);
1158 		mem_free(ct);
1159 		if (x) {
1160 			if (*x) goto ret_x;
1161 			mem_free(x);
1162 		}
1163 	}
1164 	return NULL;
1165 ret_x:
1166 	codepage = NULL;
1167 	if (extended) {
1168 		unsigned char *ap1, *ap2;
1169 		ap1 = cast_uchar strchr(cast_const_char x, '\'');
1170 		if (!ap1)
1171 			goto no_extended;
1172 		ap2 = cast_uchar strchr(cast_const_char (ap1 + 1), '\'');
1173 		if (ap2) ap2++;
1174 		else ap2 = ap1 + 1;
1175 		codepage = memacpy(x, ap1 - x);
1176 		memmove(x, ap2, strlen(cast_const_char ap2) + 1);
1177 	}
1178 
1179 no_extended:
1180 	y = init_str();
1181 	ly = 0;
1182 	add_conv_str(&y, &ly, x, (int)strlen(cast_const_char x), -2);
1183 	mem_free(x);
1184 	x = y;
1185 
1186 	cp_idx = -1;
1187 	if (codepage) {
1188 		cp_idx = get_cp_index(codepage);
1189 		mem_free(codepage);
1190 	}
1191 	if (cp_idx < 0) {
1192 		cp_idx = get_cp_index(cast_uchar "iso-8859-1");
1193 		if (cp_idx < 0) cp_idx = 0;
1194 	}
1195 	y = convert(cp_idx, 0, x, NULL);
1196 	mem_free(x);
1197 	x = y;
1198 
1199 	for (y = x; *y; y++) if (dir_sep(*y)
1200 #if defined(DOS_FS) || defined(SPAD)
1201 		|| *y == ':'
1202 #endif
1203 		) *y = '-';
1204 	return x;
1205 }
1206 
get_filename_from_url(unsigned char * url,unsigned char * head,int tmp)1207 unsigned char *get_filename_from_url(unsigned char *url, unsigned char *head, int tmp)
1208 {
1209 	int ll = 0;
1210 	unsigned char *u, *s, *e, *f, *x, *ww;
1211 	unsigned char *ct, *want_ext;
1212 	if (!casecmp(url, cast_uchar "data:", 5)) {
1213 		url = cast_uchar "data:/data";
1214 	}
1215 	want_ext = stracpy(cast_uchar "");
1216 	f = get_filename_from_header(head);
1217 	if (f) {
1218 		goto no_ct;
1219 	}
1220 	if (!(u = get_url_data(url))) u = url;
1221 	for (e = s = u; *e && !end_of_dir(url, *e); e++) {
1222 		if (dir_sep(*e)) s = e + 1;
1223 	}
1224 	ll = 0;
1225 	f = init_str();
1226 	add_conv_str(&f, &ll, s, (int)(e - s), -2);
1227 	if (!(ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) goto no_ct;
1228 	mem_free(ct);
1229 	ct = get_content_type(head, url);
1230 	if (ct) {
1231 		x = get_extension_by_content_type(ct);
1232 		if (x) {
1233 			add_to_strn(&want_ext, cast_uchar ".");
1234 			add_to_strn(&want_ext, x);
1235 			mem_free(x);
1236 		}
1237 		mem_free(ct);
1238 	}
1239 	no_ct:
1240 	if (!*want_ext) {
1241 		x = cast_uchar strrchr(cast_const_char f, '.');
1242 		if (x) {
1243 			mem_free(want_ext);
1244 			want_ext = stracpy(x);
1245 		}
1246 	}
1247 	ct = get_content_encoding(head, url, 0);
1248 	if (ct) {
1249 		x = encoding_2_extension(ct);
1250 		if (!tmp) {
1251 			unsigned char *ct1;
1252 			ct1 = get_content_encoding(head, url, 1);
1253 			if (ct1) {
1254 				mem_free(ct1);
1255 			} else if (x) {
1256 				unsigned char *w = cast_uchar strrchr(cast_const_char want_ext, '.');
1257 				if (w && (ww = canonical_compressed_ext(w + 1, NULL)) && !casestrcmp(x, ww))
1258 					goto skip_want_ext;
1259 				if (w && !casestrcmp(w + 1, x))
1260 					goto skip_want_ext;
1261 				add_to_strn(&want_ext, cast_uchar ".");
1262 				add_to_strn(&want_ext, x);
1263 				skip_want_ext:;
1264 			}
1265 		} else {
1266 			if (x) {
1267 				if (strlen(cast_const_char x) + 1 < strlen(cast_const_char f) && f[strlen(cast_const_char f) - strlen(cast_const_char x) - 1] == '.' && !casestrcmp(f + strlen(cast_const_char f) - strlen(cast_const_char x), x)) {
1268 					f[strlen(cast_const_char f) - strlen(cast_const_char x) - 1] = 0;
1269 				}
1270 			}
1271 		}
1272 		mem_free(ct);
1273 	}
1274 	if (strlen(cast_const_char want_ext) > strlen(cast_const_char f) || casestrcmp(want_ext, f + strlen(cast_const_char f) - strlen(cast_const_char want_ext))) {
1275 		x = cast_uchar strrchr(cast_const_char f, '.');
1276 		if (x && (ww = canonical_compressed_ext(x + 1, NULL)) && want_ext[0] == '.' && !casestrcmp(want_ext + 1, ww))
1277 			goto skip_tgz_2;
1278 		if (x) *x = 0;
1279 		add_to_strn(&f, want_ext);
1280 		skip_tgz_2:;
1281 	}
1282 	mem_free(want_ext);
1283 	return f;
1284 }
1285 
free_prog_list(struct list_head * l)1286 static void free_prog_list(struct list_head *l)
1287 {
1288 	struct list_head *lp;
1289 	struct protocol_program *p;
1290 	foreach(struct protocol_program, p, lp, *l)
1291 		mem_free(p->prog);
1292 	free_list(struct protocol_program, *l);
1293 }
1294 
free_types(void)1295 void free_types(void)
1296 {
1297 	struct list *l;
1298 	struct list_head *ll;
1299 	foreach(struct list, l, ll, assoc.list_entry) {
1300 		struct assoc *a = get_struct(l, struct assoc, head);
1301 		mem_free(a->ct);
1302 		mem_free(a->prog);
1303 		mem_free(a->label);
1304 		ll = ll->prev;
1305 		del_from_list(&a->head);
1306 		mem_free(a);
1307 	}
1308 	foreach(struct list, l, ll, extensions.list_entry) {
1309 		struct extension *e = get_struct(l, struct extension, head);
1310 		mem_free(e->ext);
1311 		mem_free(e->ct);
1312 		ll = ll->prev;
1313 		del_from_list(&e->head);
1314 		mem_free(e);
1315 	}
1316 	free_prog_list(&mailto_prog);
1317 	free_prog_list(&telnet_prog);
1318 	free_prog_list(&tn3270_prog);
1319 	free_prog_list(&mms_prog);
1320 	free_prog_list(&magnet_prog);
1321 
1322 	free_history(ext_search_history);
1323 	free_history(assoc_search_history);
1324 }
1325 
1326