1334 lines
44 KiB
C
1334 lines
44 KiB
C
/* types.c
|
|
* (c) 2002 Mikulas Patocka
|
|
* This file is a part of the Links program, released under GPL.
|
|
*/
|
|
|
|
#include "links.h"
|
|
|
|
|
|
/*------------------------ ASSOCIATIONS -----------------------*/
|
|
|
|
/* DECLARATIONS */
|
|
|
|
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);
|
|
static void assoc_copy_item(struct list *, struct list *);
|
|
static struct list *assoc_new_item(void *);
|
|
static void assoc_delete_item(struct list *);
|
|
static struct list *assoc_find_item(struct list *start, unsigned char *str, int direction);
|
|
static unsigned char *assoc_type_item(struct terminal *, struct list *, int);
|
|
|
|
|
|
struct list assoc = { init_list_1st(&assoc.list_entry) 0, -1, NULL, init_list_last(&assoc.list_entry) };
|
|
|
|
static struct history assoc_search_history = { 0, { &assoc_search_history.items, &assoc_search_history.items } };
|
|
|
|
struct assoc_ok_struct {
|
|
void (*fn)(struct dialog_data *, struct list *, struct list *, struct list_description *);
|
|
struct list *data;
|
|
struct dialog_data *dlg;
|
|
};
|
|
|
|
|
|
static struct list_description assoc_ld={
|
|
0, /* 0= flat; 1=tree */
|
|
&assoc, /* list */
|
|
assoc_new_item,
|
|
assoc_edit_item,
|
|
NULL,
|
|
assoc_delete_item,
|
|
assoc_copy_item,
|
|
assoc_type_item,
|
|
assoc_find_item,
|
|
&assoc_search_history,
|
|
0, /* this is set in init_assoc function */
|
|
15, /* # of items in main window */
|
|
T_ASSOCIATION,
|
|
T_ASSOCIATIONS_ALREADY_IN_USE,
|
|
T_ASSOCIATIONS_MANAGER,
|
|
T_DELETE_ASSOCIATION,
|
|
0, /* no button */
|
|
NULL, /* no button */
|
|
NULL, /* no save*/
|
|
|
|
NULL,NULL,0,0, /* internal vars */
|
|
0, /* modified */
|
|
NULL,
|
|
NULL,
|
|
1,
|
|
};
|
|
|
|
static struct list *assoc_new_item(void *ignore)
|
|
{
|
|
struct assoc *neww;
|
|
|
|
neww = mem_calloc(sizeof(struct assoc));
|
|
neww->label = stracpy(cast_uchar "");
|
|
neww->ct = stracpy(cast_uchar "");
|
|
neww->prog = stracpy(cast_uchar "");
|
|
neww->block = neww->xwin = neww->cons = 1;
|
|
neww->ask = 1;
|
|
neww->accept_http = 0;
|
|
neww->accept_ftp = 0;
|
|
neww->head.type = 0;
|
|
neww->system = SYSTEM_ID;
|
|
return &neww->head;
|
|
}
|
|
|
|
static void assoc_delete_item(struct list *data)
|
|
{
|
|
struct assoc *del = get_struct(data, struct assoc, head);
|
|
|
|
if (del->head.list_entry.next) del_from_list(&del->head);
|
|
mem_free(del->label);
|
|
mem_free(del->ct);
|
|
mem_free(del->prog);
|
|
mem_free(del);
|
|
}
|
|
|
|
static void assoc_copy_item(struct list *in, struct list *out)
|
|
{
|
|
struct assoc *item_in = get_struct(in, struct assoc, head);
|
|
struct assoc *item_out = get_struct(out, struct assoc, head);
|
|
|
|
item_out->cons = item_in->cons;
|
|
item_out->xwin = item_in->xwin;
|
|
item_out->block = item_in->block;
|
|
item_out->ask = item_in->ask;
|
|
item_out->accept_http = item_in->accept_http;
|
|
item_out->accept_ftp = item_in->accept_ftp;
|
|
item_out->system = item_in->system;
|
|
|
|
mem_free(item_out->label);
|
|
mem_free(item_out->ct);
|
|
mem_free(item_out->prog);
|
|
|
|
item_out->label = stracpy(item_in->label);
|
|
item_out->ct = stracpy(item_in->ct);
|
|
item_out->prog = stracpy(item_in->prog);
|
|
}
|
|
|
|
/* allocate string and print association into it */
|
|
/* x: 0=type all, 1=type title only */
|
|
static unsigned char *assoc_type_item(struct terminal *term, struct list *data, int x)
|
|
{
|
|
unsigned char *txt, *txt1;
|
|
struct assoc *item;
|
|
|
|
if (data == &assoc) return stracpy(get_text_translation(TEXT_(T_ASSOCIATIONS), term));
|
|
|
|
item = get_struct(data, struct assoc, head);
|
|
txt = stracpy(cast_uchar "");
|
|
if (item->system != SYSTEM_ID) add_to_strn(&txt, cast_uchar "XX ");
|
|
add_to_strn(&txt, item->label);
|
|
add_to_strn(&txt, cast_uchar ": ");
|
|
add_to_strn(&txt, item->ct);
|
|
if (!x) {
|
|
add_to_strn(&txt, cast_uchar " -> ");
|
|
if (item->prog) add_to_strn(&txt, item->prog);
|
|
}
|
|
txt1 = convert(assoc_ld.codepage, term_charset(term), txt, NULL);
|
|
mem_free(txt);
|
|
|
|
return txt1;
|
|
}
|
|
|
|
void menu_assoc_manager(struct terminal *term, void *fcp, void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
create_list_window(&assoc_ld,&assoc, term, ses);
|
|
}
|
|
|
|
static unsigned char * const ct_msg[] = {
|
|
TEXT_(T_LABEL),
|
|
TEXT_(T_CONTENT_TYPES),
|
|
TEXT_(T_PROGRAM__IS_REPLACED_WITH_FILE_NAME),
|
|
#ifdef ASSOC_BLOCK
|
|
TEXT_(T_BLOCK_TERMINAL_WHILE_PROGRAM_RUNNING),
|
|
#endif
|
|
#ifdef ASSOC_CONS_XWIN
|
|
TEXT_(T_RUN_ON_TERMINAL),
|
|
TEXT_(T_RUN_IN_XWINDOW),
|
|
#endif
|
|
TEXT_(T_ASK_BEFORE_OPENING),
|
|
TEXT_(T_ACCEPT_HTTP),
|
|
TEXT_(T_ACCEPT_FTP),
|
|
};
|
|
|
|
static void assoc_edit_item_fn(struct dialog_data *dlg)
|
|
{
|
|
struct terminal *term = dlg->win->term;
|
|
int max = 0, min = 0;
|
|
int w, rw;
|
|
int y = gf_val(-1, -G_BFU_FONT_SIZE);
|
|
int p = 3;
|
|
if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
|
|
#ifdef ASSOC_BLOCK
|
|
p++;
|
|
#endif
|
|
#ifdef ASSOC_CONS_XWIN
|
|
p += 2;
|
|
#endif
|
|
max_text_width(term, ct_msg[0], &max, AL_LEFT);
|
|
min_text_width(term, ct_msg[0], &min, AL_LEFT);
|
|
max_text_width(term, ct_msg[1], &max, AL_LEFT);
|
|
min_text_width(term, ct_msg[1], &min, AL_LEFT);
|
|
max_text_width(term, ct_msg[2], &max, AL_LEFT);
|
|
min_text_width(term, ct_msg[2], &min, AL_LEFT);
|
|
max_group_width(term, ct_msg + 3, dlg->items + 3, p, &max);
|
|
min_group_width(term, ct_msg + 3, dlg->items + 3, p, &min);
|
|
max_buttons_width(term, dlg->items + 3 + p, 2, &max);
|
|
min_buttons_width(term, dlg->items + 3 + p, 2, &min);
|
|
w = term->x * 9 / 10 - 2 * DIALOG_LB;
|
|
if (w > max) w = max;
|
|
if (w < min) w = min;
|
|
if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
|
|
if (w < 1) w = 1;
|
|
rw = 0;
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE * 1);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE * 1);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE * 1);
|
|
dlg_format_group(dlg, NULL, ct_msg + 3, dlg->items + 3, p, 0, &y, w, &rw);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
dlg_format_buttons(dlg, NULL, dlg->items + 3 + p, 2, 0, &y, w, &rw, AL_CENTER);
|
|
w = rw;
|
|
dlg->xw = w + 2 * DIALOG_LB;
|
|
dlg->yw = y + 2 * DIALOG_TB;
|
|
center_dlg(dlg);
|
|
draw_dlg(dlg);
|
|
y = dlg->y + DIALOG_TB;
|
|
if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
dlg_format_group(dlg, term, ct_msg + 3, &dlg->items[3], p, dlg->x + DIALOG_LB, &y, w, NULL);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
dlg_format_buttons(dlg, term, &dlg->items[3 + p], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
|
|
}
|
|
|
|
/* Puts url and title into the bookmark item */
|
|
static void assoc_edit_done(void *data)
|
|
{
|
|
struct dialog *d = (struct dialog *)data;
|
|
struct assoc *item = (struct assoc *)d->udata;
|
|
struct assoc_ok_struct *s = (struct assoc_ok_struct *)d->udata2;
|
|
unsigned char *txt;
|
|
unsigned char *label, *ct, *prog;
|
|
|
|
label = (unsigned char *)&d->items[12];
|
|
ct = label + MAX_STR_LEN;
|
|
prog = ct + MAX_STR_LEN;
|
|
|
|
txt = convert(term_charset(s->dlg->win->term), assoc_ld.codepage, label, NULL);
|
|
mem_free(item->label);
|
|
item->label = txt;
|
|
|
|
txt = convert(term_charset(s->dlg->win->term), assoc_ld.codepage, ct, NULL);
|
|
mem_free(item->ct);
|
|
item->ct = txt;
|
|
|
|
txt = convert(term_charset(s->dlg->win->term), assoc_ld.codepage, prog, NULL);
|
|
mem_free(item->prog);
|
|
item->prog = txt;
|
|
|
|
s->fn(s->dlg, s->data, &item->head, &assoc_ld);
|
|
d->udata = NULL; /* for abort function */
|
|
}
|
|
|
|
/* destroys an item, this function is called when edit window is aborted */
|
|
static void assoc_edit_abort(struct dialog_data *data)
|
|
{
|
|
struct dialog *dlg = data->dlg;
|
|
struct assoc *item = (struct assoc *)dlg->udata;
|
|
|
|
mem_free(dlg->udata2);
|
|
if (item) assoc_delete_item(&item->head);
|
|
}
|
|
|
|
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)
|
|
{
|
|
int p;
|
|
struct assoc *neww = get_struct(data, struct assoc, head);
|
|
struct terminal *term = dlg->win->term;
|
|
struct dialog *d;
|
|
struct assoc_ok_struct *s;
|
|
unsigned char *ct, *prog, *label;
|
|
|
|
d = mem_calloc(sizeof(struct dialog) + 11 * sizeof(struct dialog_item) + 3 * MAX_STR_LEN);
|
|
|
|
label = (unsigned char *)&d->items[12];
|
|
ct = label + MAX_STR_LEN;
|
|
prog = ct + MAX_STR_LEN;
|
|
|
|
safe_strncpy(label, neww->label, MAX_STR_LEN);
|
|
safe_strncpy(ct, neww->ct, MAX_STR_LEN);
|
|
safe_strncpy(prog, neww->prog, MAX_STR_LEN);
|
|
|
|
/* Create the dialog */
|
|
s = mem_alloc(sizeof(struct assoc_ok_struct));
|
|
s->fn = ok_fn;
|
|
s->data = ok_arg;
|
|
s->dlg = dlg;
|
|
|
|
switch (dlg_title) {
|
|
case TITLE_EDIT:
|
|
d->title = TEXT_(T_EDIT_ASSOCIATION);
|
|
break;
|
|
|
|
case TITLE_ADD:
|
|
d->title = TEXT_(T_ADD_ASSOCIATION);
|
|
break;
|
|
|
|
default:
|
|
internal_error("Unsupported dialog title.\n");
|
|
}
|
|
|
|
d->udata = neww;
|
|
d->udata2 = s;
|
|
d->fn = assoc_edit_item_fn;
|
|
d->abort = assoc_edit_abort;
|
|
d->refresh = assoc_edit_done;
|
|
d->refresh_data = d;
|
|
d->items[0].type = D_FIELD;
|
|
d->items[0].dlen = MAX_STR_LEN;
|
|
d->items[0].data = label;
|
|
d->items[0].fn = check_nonempty;
|
|
d->items[1].type = D_FIELD;
|
|
d->items[1].dlen = MAX_STR_LEN;
|
|
d->items[1].data = ct;
|
|
d->items[1].fn = check_nonempty;
|
|
d->items[2].type = D_FIELD;
|
|
d->items[2].dlen = MAX_STR_LEN;
|
|
d->items[2].data = prog;
|
|
d->items[2].fn = check_nonempty;
|
|
p = 3;
|
|
#ifdef ASSOC_BLOCK
|
|
d->items[p].type = D_CHECKBOX;
|
|
d->items[p].data = (unsigned char *)&neww->block;
|
|
d->items[p++].dlen = sizeof(int);
|
|
#endif
|
|
#ifdef ASSOC_CONS_XWIN
|
|
d->items[p].type = D_CHECKBOX;
|
|
d->items[p].data = (unsigned char *)&neww->cons;
|
|
d->items[p++].dlen = sizeof(int);
|
|
d->items[p].type = D_CHECKBOX;
|
|
d->items[p].data = (unsigned char *)&neww->xwin;
|
|
d->items[p++].dlen = sizeof(int);
|
|
#endif
|
|
d->items[p].type = D_CHECKBOX;
|
|
d->items[p].data = (unsigned char *)&neww->ask;
|
|
d->items[p++].dlen = sizeof(int);
|
|
d->items[p].type = D_CHECKBOX;
|
|
d->items[p].data = (unsigned char *)&neww->accept_http;
|
|
d->items[p++].dlen = sizeof(int);
|
|
d->items[p].type = D_CHECKBOX;
|
|
d->items[p].data = (unsigned char *)&neww->accept_ftp;
|
|
d->items[p++].dlen = sizeof(int);
|
|
d->items[p].type = D_BUTTON;
|
|
d->items[p].gid = B_ENTER;
|
|
d->items[p].fn = ok_dialog;
|
|
d->items[p++].text = TEXT_(T_OK);
|
|
d->items[p].type = D_BUTTON;
|
|
d->items[p].gid = B_ESC;
|
|
d->items[p].text = TEXT_(T_CANCEL);
|
|
d->items[p++].fn = cancel_dialog;
|
|
d->items[p++].type = D_END;
|
|
do_dialog(term, d, getml(d, NULL));
|
|
}
|
|
|
|
static int assoc_test_entry(struct list *e, unsigned char *str)
|
|
{
|
|
struct assoc *a = get_struct(e, struct assoc, head);
|
|
return casestrstr(a->label, str) || casestrstr(a->ct, str);
|
|
}
|
|
|
|
static struct list *assoc_find_item(struct list *s, unsigned char *str, int direction)
|
|
{
|
|
struct list *e;
|
|
|
|
if (direction >= 0) {
|
|
for (e = list_next(s); e != s; e = list_next(e)) {
|
|
if (e->depth >= 0 && assoc_test_entry(e, str))
|
|
return e;
|
|
}
|
|
} else {
|
|
for (e = list_prev(s); e != s; e = list_prev(e)) {
|
|
if (e->depth >= 0 && assoc_test_entry(e, str))
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if (e->depth >= 0 && assoc_test_entry(e, str))
|
|
return e;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void update_assoc(struct assoc *neww)
|
|
{
|
|
struct assoc *repl;
|
|
struct list *r;
|
|
struct list_head *lr;
|
|
if (!neww->label[0] || !neww->ct[0] || !neww->prog[0]) return;
|
|
foreach(struct list, r, lr, assoc.list_entry) {
|
|
repl = get_struct(r, struct assoc, head);
|
|
if (!strcmp(cast_const_char repl->label, cast_const_char neww->label)
|
|
&& !strcmp(cast_const_char repl->ct, cast_const_char neww->ct)
|
|
&& !strcmp(cast_const_char repl->prog, cast_const_char neww->prog)
|
|
&& repl->block == neww->block
|
|
&& repl->cons == neww->cons
|
|
&& repl->xwin == neww->xwin
|
|
&& repl->ask == neww->ask
|
|
&& repl->accept_http == neww->accept_http
|
|
&& repl->accept_ftp == neww->accept_ftp
|
|
&& repl->system == neww->system) {
|
|
del_from_list(&repl->head);
|
|
add_to_list(assoc.list_entry, &repl->head);
|
|
return;
|
|
}
|
|
}
|
|
repl = mem_calloc(sizeof(struct assoc));
|
|
repl->label = stracpy(neww->label);
|
|
repl->ct = stracpy(neww->ct);
|
|
repl->prog = stracpy(neww->prog);
|
|
repl->block = neww->block;
|
|
repl->cons = neww->cons;
|
|
repl->xwin = neww->xwin;
|
|
repl->ask = neww->ask;
|
|
repl->accept_http = neww->accept_http;
|
|
repl->accept_ftp = neww->accept_ftp;
|
|
repl->system = neww->system;
|
|
repl->head.type = 0;
|
|
add_to_list(assoc.list_entry, &repl->head);
|
|
}
|
|
|
|
/*------------------------ EXTENSIONS -----------------------*/
|
|
|
|
/* DECLARATIONS */
|
|
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);
|
|
static void ext_copy_item(struct list *, struct list *);
|
|
static struct list *ext_new_item(void *);
|
|
static void ext_delete_item(struct list *);
|
|
static struct list *ext_find_item(struct list *start, unsigned char *str, int direction);
|
|
static unsigned char *ext_type_item(struct terminal *, struct list *, int);
|
|
|
|
struct list extensions = { init_list_1st(&extensions.list_entry) 0, -1, NULL, init_list_last(&extensions.list_entry) };
|
|
|
|
static struct history ext_search_history = { 0, { &ext_search_history.items, &ext_search_history.items } };
|
|
|
|
|
|
static struct list_description ext_ld={
|
|
0, /* 0= flat; 1=tree */
|
|
&extensions, /* list */
|
|
ext_new_item,
|
|
ext_edit_item,
|
|
NULL,
|
|
ext_delete_item,
|
|
ext_copy_item,
|
|
ext_type_item,
|
|
ext_find_item,
|
|
&ext_search_history,
|
|
0, /* this is set in init_assoc function */
|
|
15, /* # of items in main window */
|
|
T_eXTENSION,
|
|
T_EXTENSIONS_ALREADY_IN_USE,
|
|
T_EXTENSIONS_MANAGER,
|
|
T_DELETE_EXTENSION,
|
|
0, /* no button */
|
|
NULL, /* no button */
|
|
NULL, /* no save*/
|
|
|
|
NULL,NULL,0,0, /* internal vars */
|
|
0, /* modified */
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct list *ext_new_item(void *ignore)
|
|
{
|
|
struct extension *neww;
|
|
|
|
neww = mem_calloc(sizeof(struct extension));
|
|
neww->ext = stracpy(cast_uchar "");
|
|
neww->ct = stracpy(cast_uchar "");
|
|
neww->head.type = 0;
|
|
return &neww->head;
|
|
}
|
|
|
|
|
|
static void ext_delete_item(struct list *data)
|
|
{
|
|
struct extension *del = get_struct(data, struct extension, head);
|
|
|
|
if (del->head.list_entry.next) del_from_list(&del->head);
|
|
if (del->ext) mem_free(del->ext);
|
|
if (del->ct) mem_free(del->ct);
|
|
mem_free(del);
|
|
}
|
|
|
|
|
|
static void ext_copy_item(struct list *in, struct list *out)
|
|
{
|
|
struct extension *item_in = get_struct(in, struct extension, head);
|
|
struct extension *item_out = get_struct(out, struct extension, head);
|
|
|
|
mem_free(item_out->ext);
|
|
mem_free(item_out->ct);
|
|
|
|
item_out->ext = stracpy(item_in->ext);
|
|
item_out->ct = stracpy(item_in->ct);
|
|
}
|
|
|
|
|
|
/* allocate string and print extension into it */
|
|
/* x: 0=type all, 1=type title only */
|
|
static unsigned char *ext_type_item(struct terminal *term, struct list *data, int x)
|
|
{
|
|
unsigned char *txt, *txt1;
|
|
struct extension *item;
|
|
|
|
if (data == &extensions) return stracpy(get_text_translation(TEXT_(T_FILE_EXTENSIONS), term));
|
|
|
|
item = get_struct(data, struct extension, head);
|
|
txt = stracpy(item->ext);
|
|
add_to_strn(&txt, cast_uchar ": ");
|
|
add_to_strn(&txt, item->ct);
|
|
txt1 = convert(assoc_ld.codepage, term_charset(term), txt, NULL);
|
|
mem_free(txt);
|
|
|
|
return txt1;
|
|
}
|
|
|
|
|
|
void menu_ext_manager(struct terminal *term, void *fcp, void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
create_list_window(&ext_ld, &extensions, term, ses);
|
|
}
|
|
|
|
static unsigned char * const ext_msg[] = {
|
|
TEXT_(T_EXTENSION_S),
|
|
TEXT_(T_CONTENT_TYPE),
|
|
};
|
|
|
|
static void ext_edit_item_fn(struct dialog_data *dlg)
|
|
{
|
|
struct terminal *term = dlg->win->term;
|
|
int max = 0, min = 0;
|
|
int w, rw;
|
|
int y = gf_val(-1, -G_BFU_FONT_SIZE);
|
|
if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
|
|
max_text_width(term, ext_msg[0], &max, AL_LEFT);
|
|
min_text_width(term, ext_msg[0], &min, AL_LEFT);
|
|
max_text_width(term, ext_msg[1], &max, AL_LEFT);
|
|
min_text_width(term, ext_msg[1], &min, AL_LEFT);
|
|
max_buttons_width(term, dlg->items + 2, 2, &max);
|
|
min_buttons_width(term, dlg->items + 2, 2, &min);
|
|
w = term->x * 9 / 10 - 2 * DIALOG_LB;
|
|
if (w > max) w = max;
|
|
if (w < min) w = min;
|
|
if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
|
|
if (w < 1) w = 1;
|
|
rw = 0;
|
|
dlg_format_text_and_field(dlg, NULL, ext_msg[0], &dlg->items[0], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
|
|
y += gf_val(1, G_BFU_FONT_SIZE * 1);
|
|
dlg_format_text_and_field(dlg, NULL, ext_msg[1], &dlg->items[1], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
|
|
y += gf_val(1, G_BFU_FONT_SIZE * 1);
|
|
dlg_format_buttons(dlg, NULL, dlg->items + 2, 2, 0, &y, w, &rw, AL_CENTER);
|
|
w = rw;
|
|
dlg->xw = w + 2 * DIALOG_LB;
|
|
dlg->yw = y + 2 * DIALOG_TB;
|
|
center_dlg(dlg);
|
|
draw_dlg(dlg);
|
|
y = dlg->y + DIALOG_TB;
|
|
if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
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);
|
|
y += gf_val(1, G_BFU_FONT_SIZE);
|
|
dlg_format_buttons(dlg, term, &dlg->items[2], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
|
|
}
|
|
|
|
|
|
/* Puts url and title into the bookmark item */
|
|
static void ext_edit_done(void *data)
|
|
{
|
|
struct dialog *d = (struct dialog*)data;
|
|
struct extension *item = (struct extension *)d->udata;
|
|
struct assoc_ok_struct *s = (struct assoc_ok_struct *)d->udata2;
|
|
unsigned char *txt;
|
|
unsigned char *ext, *ct;
|
|
|
|
ext = (unsigned char *)&d->items[5];
|
|
ct = ext + MAX_STR_LEN;
|
|
|
|
txt = convert(term_charset(s->dlg->win->term), ext_ld.codepage,ext, NULL);
|
|
mem_free(item->ext);
|
|
item->ext = txt;
|
|
|
|
txt = convert(term_charset(s->dlg->win->term), ext_ld.codepage,ct, NULL);
|
|
mem_free(item->ct);
|
|
item->ct=txt;
|
|
|
|
s->fn(s->dlg, s->data, &item->head, &ext_ld);
|
|
d->udata = NULL; /* for abort function */
|
|
}
|
|
|
|
|
|
/* destroys an item, this function is called when edit window is aborted */
|
|
static void ext_edit_abort(struct dialog_data *data)
|
|
{
|
|
struct dialog *dlg = data->dlg;
|
|
struct extension *item = (struct extension *)dlg->udata;
|
|
|
|
mem_free(dlg->udata2);
|
|
if (item) ext_delete_item(&item->head);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
struct extension *neww = get_struct(data, struct extension, head);
|
|
struct terminal *term = dlg->win->term;
|
|
struct dialog *d;
|
|
struct assoc_ok_struct *s;
|
|
unsigned char *ext;
|
|
unsigned char *ct;
|
|
|
|
d = mem_calloc(sizeof(struct dialog) + 4 * sizeof(struct dialog_item) + 2 * MAX_STR_LEN);
|
|
|
|
ext = (unsigned char *)&d->items[5];
|
|
ct = ext + MAX_STR_LEN;
|
|
safe_strncpy(ext, neww->ext, MAX_STR_LEN);
|
|
safe_strncpy(ct, neww->ct, MAX_STR_LEN);
|
|
|
|
/* Create the dialog */
|
|
s = mem_alloc(sizeof(struct assoc_ok_struct));
|
|
s->fn = ok_fn;
|
|
s->data = ok_arg;
|
|
s->dlg = dlg;
|
|
|
|
switch (dlg_title) {
|
|
case TITLE_EDIT:
|
|
d->title = TEXT_(T_EDIT_EXTENSION);
|
|
break;
|
|
|
|
case TITLE_ADD:
|
|
d->title = TEXT_(T_ADD_EXTENSION);
|
|
break;
|
|
|
|
default:
|
|
internal_error("Unsupported dialog title.\n");
|
|
}
|
|
|
|
d->udata = neww;
|
|
d->udata2 = s;
|
|
d->abort = ext_edit_abort;
|
|
d->refresh = ext_edit_done;
|
|
d->refresh_data = d;
|
|
d->title = TEXT_(T_EXTENSION);
|
|
d->fn = ext_edit_item_fn;
|
|
d->items[0].type = D_FIELD;
|
|
d->items[0].dlen = MAX_STR_LEN;
|
|
d->items[0].data = ext;
|
|
d->items[0].fn = check_nonempty;
|
|
d->items[1].type = D_FIELD;
|
|
d->items[1].dlen = MAX_STR_LEN;
|
|
d->items[1].data = ct;
|
|
d->items[1].fn = check_nonempty;
|
|
d->items[2].type = D_BUTTON;
|
|
d->items[2].gid = B_ENTER;
|
|
d->items[2].fn = ok_dialog;
|
|
d->items[2].text = TEXT_(T_OK);
|
|
d->items[3].type = D_BUTTON;
|
|
d->items[3].gid = B_ESC;
|
|
d->items[3].text = TEXT_(T_CANCEL);
|
|
d->items[3].fn = cancel_dialog;
|
|
d->items[4].type = D_END;
|
|
do_dialog(term, d, getml(d, NULL));
|
|
}
|
|
|
|
|
|
static int ext_test_entry(struct list *e, unsigned char *str)
|
|
{
|
|
struct extension *ext = get_struct(e, struct extension, head);
|
|
return casestrstr(ext->ext, str) || casestrstr(ext->ct, str);
|
|
}
|
|
|
|
static struct list *ext_find_item(struct list *s, unsigned char *str, int direction)
|
|
{
|
|
struct list *e;
|
|
|
|
if (direction >= 0) {
|
|
for (e = list_next(s); e != s; e = list_next(e)) {
|
|
if (e->depth >= 0 && ext_test_entry(e, str))
|
|
return e;
|
|
}
|
|
} else {
|
|
for (e = list_prev(s); e != s; e = list_prev(e)) {
|
|
if (e->depth >= 0 && ext_test_entry(e, str))
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if (e->depth >= 0 && ext_test_entry(e, str))
|
|
return e;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void update_ext(struct extension *neww)
|
|
{
|
|
struct extension *repl;
|
|
struct list *r;
|
|
struct list_head *lr;
|
|
if (!neww->ext[0] || !neww->ct[0]) return;
|
|
foreach(struct list, r, lr, extensions.list_entry) {
|
|
repl = get_struct(r, struct extension, head);
|
|
if (!strcmp(cast_const_char repl->ext, cast_const_char neww->ext)
|
|
&& !strcmp(cast_const_char repl->ct, cast_const_char neww->ct)) {
|
|
del_from_list(&repl->head);
|
|
add_to_list(extensions.list_entry, &repl->head);
|
|
return;
|
|
}
|
|
}
|
|
repl = mem_calloc(sizeof(struct extension));
|
|
repl->ext = stracpy(neww->ext);
|
|
repl->ct = stracpy(neww->ct);
|
|
repl->head.type = 0;
|
|
add_to_list(extensions.list_entry, &repl->head);
|
|
}
|
|
|
|
void update_prog(struct list_head *l, unsigned char *p, int s)
|
|
{
|
|
struct protocol_program *repl;
|
|
struct list_head *lrepl;
|
|
foreach(struct protocol_program, repl, lrepl, *l) if (repl->system == s) {
|
|
mem_free(repl->prog);
|
|
goto ss;
|
|
}
|
|
repl = mem_alloc(sizeof(struct protocol_program));
|
|
add_to_list(*l, repl);
|
|
repl->system = s;
|
|
ss:
|
|
repl->prog = mem_alloc(MAX_STR_LEN);
|
|
safe_strncpy(repl->prog, p, MAX_STR_LEN);
|
|
}
|
|
|
|
unsigned char *get_prog(struct list_head *l)
|
|
{
|
|
struct protocol_program *repl;
|
|
struct list_head *lrepl;
|
|
foreach(struct protocol_program, repl, lrepl, *l) if (repl->system == SYSTEM_ID) return repl->prog;
|
|
update_prog(l, cast_uchar "", SYSTEM_ID);
|
|
foreach(struct protocol_program, repl, lrepl, *l) if (repl->system == SYSTEM_ID) return repl->prog;
|
|
internal_error("get_prog: program was not added");
|
|
return cast_uchar "";;
|
|
}
|
|
|
|
|
|
/* creates default extensions if extension list is empty */
|
|
void create_initial_extensions(void)
|
|
{
|
|
struct extension ext;
|
|
|
|
if (!list_empty(extensions.list_entry)) return;
|
|
|
|
/* here you can add any default extension you want */
|
|
ext.ext = cast_uchar "xpm", ext.ct=cast_uchar "image/x-xpixmap", update_ext(&ext);
|
|
ext.ext = cast_uchar "xls", ext.ct=cast_uchar "application/excel", update_ext(&ext);
|
|
ext.ext = cast_uchar "xbm", ext.ct=cast_uchar "image/x-xbitmap", update_ext(&ext);
|
|
ext.ext = cast_uchar "webp", ext.ct=cast_uchar "image/webp", update_ext(&ext);
|
|
ext.ext = cast_uchar "wav", ext.ct=cast_uchar "audio/x-wav", update_ext(&ext);
|
|
ext.ext = cast_uchar "tiff,tif", ext.ct=cast_uchar "image/tiff", update_ext(&ext);
|
|
ext.ext = cast_uchar "tga", ext.ct=cast_uchar "image/targa", update_ext(&ext);
|
|
ext.ext = cast_uchar "sxw", ext.ct=cast_uchar "application/x-openoffice", update_ext(&ext);
|
|
ext.ext = cast_uchar "swf", ext.ct=cast_uchar "application/x-shockwave-flash", update_ext(&ext);
|
|
ext.ext = cast_uchar "svg", ext.ct=cast_uchar "image/svg+xml", update_ext(&ext);
|
|
ext.ext = cast_uchar "sch", ext.ct=cast_uchar "application/gschem", update_ext(&ext);
|
|
ext.ext = cast_uchar "rtf", ext.ct=cast_uchar "application/rtf", update_ext(&ext);
|
|
ext.ext = cast_uchar "ra,rm,ram", ext.ct=cast_uchar "audio/x-pn-realaudio", update_ext(&ext);
|
|
ext.ext = cast_uchar "qt,mov", ext.ct=cast_uchar "video/quicktime", update_ext(&ext);
|
|
ext.ext = cast_uchar "ps,eps,ai", ext.ct=cast_uchar "application/postscript", update_ext(&ext);
|
|
ext.ext = cast_uchar "ppt", ext.ct=cast_uchar "application/powerpoint", update_ext(&ext);
|
|
ext.ext = cast_uchar "ppm", ext.ct=cast_uchar "image/x-portable-pixmap", update_ext(&ext);
|
|
ext.ext = cast_uchar "pnm", ext.ct=cast_uchar "image/x-portable-anymap", update_ext(&ext);
|
|
ext.ext = cast_uchar "png", ext.ct=cast_uchar "image/png", update_ext(&ext);
|
|
ext.ext = cast_uchar "pgp", ext.ct=cast_uchar "application/pgp-signature", update_ext(&ext);
|
|
ext.ext = cast_uchar "pgm", ext.ct=cast_uchar "image/x-portable-graymap", update_ext(&ext);
|
|
ext.ext = cast_uchar "pdf", ext.ct=cast_uchar "application/pdf", update_ext(&ext);
|
|
ext.ext = cast_uchar "pcb", ext.ct=cast_uchar "application/pcb", update_ext(&ext);
|
|
ext.ext = cast_uchar "pbm", ext.ct=cast_uchar "image/x-portable-bitmap", update_ext(&ext);
|
|
ext.ext = cast_uchar "mpeg,mpg,mpe", ext.ct=cast_uchar "video/mpeg", update_ext(&ext);
|
|
ext.ext = cast_uchar "mp3", ext.ct=cast_uchar "audio/mpeg", update_ext(&ext);
|
|
ext.ext = cast_uchar "mid,midi", ext.ct=cast_uchar "audio/midi", update_ext(&ext);
|
|
ext.ext = cast_uchar "jpg,jpeg,jpe", ext.ct=cast_uchar "image/jpeg", update_ext(&ext);
|
|
ext.ext = cast_uchar "grb", ext.ct=cast_uchar "application/gerber", update_ext(&ext);
|
|
ext.ext = cast_uchar "gl", ext.ct=cast_uchar "video/gl", update_ext(&ext);
|
|
ext.ext = cast_uchar "gif", ext.ct=cast_uchar "image/gif", update_ext(&ext);
|
|
ext.ext = cast_uchar "gbr", ext.ct=cast_uchar "application/gerber", update_ext(&ext);
|
|
ext.ext = cast_uchar "g", ext.ct=cast_uchar "application/brlcad", update_ext(&ext);
|
|
ext.ext = cast_uchar "fli", ext.ct=cast_uchar "video/fli", update_ext(&ext);
|
|
ext.ext = cast_uchar "dxf", ext.ct=cast_uchar "application/dxf", update_ext(&ext);
|
|
ext.ext = cast_uchar "dvi", ext.ct=cast_uchar "application/x-dvi", update_ext(&ext);
|
|
ext.ext = cast_uchar "dl", ext.ct=cast_uchar "video/dl", update_ext(&ext);
|
|
ext.ext = cast_uchar "deb", ext.ct=cast_uchar "application/x-debian-package", update_ext(&ext);
|
|
ext.ext = cast_uchar "avif", ext.ct=cast_uchar "image/avif", update_ext(&ext);
|
|
ext.ext = cast_uchar "avi", ext.ct=cast_uchar "video/x-msvideo", update_ext(&ext);
|
|
ext.ext = cast_uchar "au,snd", ext.ct=cast_uchar "audio/basic", update_ext(&ext);
|
|
ext.ext = cast_uchar "aif,aiff,aifc", ext.ct=cast_uchar "audio/x-aiff", update_ext(&ext);
|
|
}
|
|
|
|
/* --------------------------- PROG -----------------------------*/
|
|
|
|
|
|
struct list_head mailto_prog = { &mailto_prog, &mailto_prog };
|
|
struct list_head telnet_prog = { &telnet_prog, &telnet_prog };
|
|
struct list_head tn3270_prog = { &tn3270_prog, &tn3270_prog };
|
|
struct list_head mms_prog = { &mms_prog, &mms_prog };
|
|
struct list_head magnet_prog = { &magnet_prog, &magnet_prog };
|
|
struct list_head gopher_prog = { &gopher_prog, &gopher_prog };
|
|
|
|
|
|
static int is_in_list(unsigned char *list, unsigned char *str, int l)
|
|
{
|
|
unsigned char *l2, *l3;
|
|
if (!l) return 0;
|
|
rep:
|
|
while (*list && *list <= ' ') list++;
|
|
if (!*list) return 0;
|
|
for (l2 = list; *l2 && *l2 != ','; l2++)
|
|
;
|
|
for (l3 = l2 - 1; l3 >= list && *l3 <= ' '; l3--)
|
|
;
|
|
l3++;
|
|
if (l3 - list == l && !casecmp(str, list, l)) return 1;
|
|
list = l2;
|
|
if (*list == ',') list++;
|
|
goto rep;
|
|
}
|
|
|
|
static unsigned char *canonical_compressed_ext(unsigned char *ext, unsigned char *ext_end)
|
|
{
|
|
size_t len;
|
|
if (!ext_end) ext_end = cast_uchar strchr(cast_const_char ext, 0);
|
|
len = ext_end - ext;
|
|
if (len == 3 && !casecmp(ext, cast_uchar "tgz", 3)) return cast_uchar "gz";
|
|
if (len == 3 && !casecmp(ext, cast_uchar "tbz", 3)) return cast_uchar "bz2";
|
|
if (len == 3 && !casecmp(ext, cast_uchar "txz", 3)) return cast_uchar "xz";
|
|
if (len == 6 && !casecmp(ext, cast_uchar "tar-gz", 3)) return cast_uchar "gz";
|
|
if (len == 7 && !casecmp(ext, cast_uchar "tar-bz2", 3)) return cast_uchar "bz2";
|
|
if (len == 6 && !casecmp(ext, cast_uchar "tar-xz", 3)) return cast_uchar "xz";
|
|
return NULL;
|
|
}
|
|
|
|
unsigned char *get_compress_by_extension(unsigned char *ext, unsigned char *ext_end)
|
|
{
|
|
size_t len;
|
|
unsigned char *x;
|
|
if ((x = canonical_compressed_ext(ext, ext_end))) {
|
|
ext = x;
|
|
ext_end = cast_uchar strchr(cast_const_char x, 0);
|
|
}
|
|
len = ext_end - ext;
|
|
if (len == 1 && !casecmp(ext, cast_uchar "z", 1)) return cast_uchar "compress";
|
|
if (len == 2 && !casecmp(ext, cast_uchar "gz", 2)) return cast_uchar "gzip";
|
|
if (len == 2 && !casecmp(ext, cast_uchar "br", 2)) return cast_uchar "br";
|
|
if (len == 3 && !casecmp(ext, cast_uchar "zst", 3)) return cast_uchar "zstd";
|
|
if (len == 3 && !casecmp(ext, cast_uchar "bz2", 3)) return cast_uchar "bzip2";
|
|
if (len == 4 && !casecmp(ext, cast_uchar "lzma", 4)) return cast_uchar "lzma";
|
|
if (len == 2 && !casecmp(ext, cast_uchar "xz", 2)) return cast_uchar "lzma2";
|
|
if (len == 2 && !casecmp(ext, cast_uchar "lz", 2)) return cast_uchar "lzip";
|
|
return NULL;
|
|
}
|
|
|
|
unsigned char *get_content_type_by_extension(unsigned char *url)
|
|
{
|
|
struct list *l;
|
|
struct list_head *ll;
|
|
unsigned char *ct, *eod, *ext, *exxt;
|
|
int extl, el;
|
|
ext = NULL, extl = 0;
|
|
if (!(ct = get_url_data(url))) ct = url;
|
|
for (eod = ct; *eod && !end_of_dir(url, *eod); eod++)
|
|
;
|
|
for (; ct < eod; ct++)
|
|
if (*ct == '.') {
|
|
if (ext) {
|
|
if (get_compress_by_extension(ct + 1, eod))
|
|
break;
|
|
}
|
|
ext = ct + 1;
|
|
} else if (dir_sep(*ct)) {
|
|
ext = NULL;
|
|
}
|
|
if (ext) while (ext[extl] && ext[extl] != '.' && !dir_sep(ext[extl]) && !end_of_dir(url, ext[extl])) extl++;
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "htm", 3)) ||
|
|
(extl == 4 && !casecmp(ext, cast_uchar "html", 4)) ||
|
|
(extl == 5 && !casecmp(ext, cast_uchar "xhtml", 5))) return stracpy(cast_uchar "text/html");
|
|
foreach(struct list, l, ll, extensions.list_entry) {
|
|
struct extension *e = get_struct(l, struct extension, head);
|
|
unsigned char *fname = NULL;
|
|
if (!(ct = get_url_data(url))) ct = url;
|
|
for (; *ct && !end_of_dir(url, *ct); ct++)
|
|
if (dir_sep(*ct)) fname = ct + 1;
|
|
if (!fname) {
|
|
if (is_in_list(e->ext, ext, extl)) return stracpy(e->ct);
|
|
} else {
|
|
int fnlen = 0;
|
|
int x;
|
|
while (fname[fnlen] && !end_of_dir(url, fname[fnlen])) fnlen++;
|
|
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);
|
|
}
|
|
}
|
|
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "jpg", 3)) ||
|
|
(extl == 4 && !casecmp(ext, cast_uchar "pjpg", 4))||
|
|
(extl == 4 && !casecmp(ext, cast_uchar "jpeg", 4))||
|
|
(extl == 5 && !casecmp(ext, cast_uchar "pjpeg", 5))) return stracpy(cast_uchar "image/jpeg");
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "png", 3))) return stracpy(cast_uchar "image/png");
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "gif", 3))) return stracpy(cast_uchar "image/gif");
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "xbm", 3))) return stracpy(cast_uchar "image/x-xbitmap");
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "tif", 3)) ||
|
|
(extl == 4 && !casecmp(ext, cast_uchar "tiff", 4))) return stracpy(cast_uchar "image/tiff");
|
|
if ((extl == 3 && !casecmp(ext, cast_uchar "svg", 3))) return stracpy(cast_uchar "image/svg+xml");
|
|
if ((extl == 4 && !casecmp(ext, cast_uchar "webp", 4))) return stracpy(cast_uchar "image/webp");
|
|
if ((extl == 4 && !casecmp(ext, cast_uchar "avif", 4))) return stracpy(cast_uchar "image/avif");
|
|
exxt = init_str(); el = 0;
|
|
add_to_str(&exxt, &el, cast_uchar "application/x-");
|
|
add_bytes_to_str(&exxt, &el, ext, extl);
|
|
foreach(struct list, l, ll, assoc.list_entry) {
|
|
struct assoc *a = get_struct(l, struct assoc, head);
|
|
if (is_in_list(a->ct, exxt, el))
|
|
return exxt;
|
|
}
|
|
mem_free(exxt);
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned char *get_content_type_by_header_and_extension(unsigned char *head, unsigned char *url)
|
|
{
|
|
unsigned char *ct, *file;
|
|
ct = get_content_type_by_extension(url);
|
|
if (ct) return ct;
|
|
file = get_filename_from_header(head);
|
|
if (file) {
|
|
ct = get_content_type_by_extension(file);
|
|
mem_free(file);
|
|
if (ct) return ct;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned char *get_extension_by_content_type(unsigned char *ct)
|
|
{
|
|
struct list *l;
|
|
struct list_head *ll;
|
|
unsigned char *x, *y;
|
|
if (is_html_type(ct)) return stracpy(cast_uchar "html");
|
|
foreach(struct list, l, ll, extensions.list_entry) {
|
|
struct extension *e = get_struct(l, struct extension, head);
|
|
if (!casestrcmp(e->ct, ct)) {
|
|
x = stracpy(e->ext);
|
|
if ((y = cast_uchar strchr(cast_const_char x, ','))) *y = 0;
|
|
return x;
|
|
}
|
|
}
|
|
if (!casestrcmp(ct, cast_uchar "image/jpeg") ||
|
|
!casestrcmp(ct, cast_uchar "image/jpg") ||
|
|
!casestrcmp(ct, cast_uchar "image/jpe") ||
|
|
!casestrcmp(ct, cast_uchar "image/pjpe") ||
|
|
!casestrcmp(ct, cast_uchar "image/pjpeg") ||
|
|
!casestrcmp(ct, cast_uchar "image/pjpg"))
|
|
return stracpy(cast_uchar "jpg");
|
|
if (!casestrcmp(ct, cast_uchar "image/png") ||
|
|
!casestrcmp(ct, cast_uchar "image/x-png"))
|
|
return stracpy(cast_uchar "png");
|
|
if (!casestrcmp(ct, cast_uchar "image/gif"))
|
|
return stracpy(cast_uchar "gif");
|
|
if (!casestrcmp(ct, cast_uchar "image/x-bitmap"))
|
|
return stracpy(cast_uchar "xbm");
|
|
if (!casestrcmp(ct, cast_uchar "image/tiff") ||
|
|
!casestrcmp(ct, cast_uchar "image/tif"))
|
|
return stracpy(cast_uchar "tiff");
|
|
if (!casestrcmp(ct, cast_uchar "image/svg") ||
|
|
!casestrcmp(ct, cast_uchar "image/svg+xml"))
|
|
return stracpy(cast_uchar "svg");
|
|
if (!casestrcmp(ct, cast_uchar "image/webp"))
|
|
return stracpy(cast_uchar "webp");
|
|
if (!casestrcmp(ct, cast_uchar "image/avif"))
|
|
return stracpy(cast_uchar "avif");
|
|
if (!cmpbeg(ct, cast_uchar "application/x-")) {
|
|
x = ct + strlen("application/x-");
|
|
if (casestrcmp(x, cast_uchar "z") &&
|
|
casestrcmp(x, cast_uchar "gz") &&
|
|
casestrcmp(x, cast_uchar "gzip") &&
|
|
casestrcmp(x, cast_uchar "br") &&
|
|
casestrcmp(x, cast_uchar "zst") &&
|
|
casestrcmp(x, cast_uchar "bz2") &&
|
|
casestrcmp(x, cast_uchar "bzip2") &&
|
|
casestrcmp(x, cast_uchar "lzma") &&
|
|
casestrcmp(x, cast_uchar "lzma2") &&
|
|
casestrcmp(x, cast_uchar "xz") &&
|
|
casestrcmp(x, cast_uchar "lz") &&
|
|
!strchr(cast_const_char x, '-') &&
|
|
strlen(cast_const_char x) <= 4) {
|
|
return stracpy(x);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned char *get_content_encoding_from_content_type(unsigned char *ct)
|
|
{
|
|
if (!casestrcmp(ct, cast_uchar "application/x-gzip") ||
|
|
!casestrcmp(ct, cast_uchar "application/x-tgz") ||
|
|
!casestrcmp(ct, cast_uchar "application/x-gtar")) return cast_uchar "gzip";
|
|
if (!casestrcmp(ct, cast_uchar "application/x-br")) return cast_uchar "br";
|
|
if (!casestrcmp(ct, cast_uchar "application/x-zstd") ||
|
|
!casestrcmp(ct, cast_uchar "application/zstd")) return cast_uchar "zstd";
|
|
if (!casestrcmp(ct, cast_uchar "application/x-bzip2") ||
|
|
!casestrcmp(ct, cast_uchar "application/x-bzip")) return cast_uchar "bzip2";
|
|
if (!casestrcmp(ct, cast_uchar "application/x-lzma")) return cast_uchar "lzma";
|
|
if (!casestrcmp(ct, cast_uchar "application/x-lzma2") ||
|
|
!casestrcmp(ct, cast_uchar "application/x-xz")) return cast_uchar "lzma2";
|
|
if (!casestrcmp(ct, cast_uchar "application/x-lz") ||
|
|
!casestrcmp(ct, cast_uchar "application/x-lzip")) return cast_uchar "lzip";
|
|
return NULL;
|
|
}
|
|
|
|
unsigned char *get_content_type(unsigned char *head, unsigned char *url)
|
|
{
|
|
unsigned char *ct;
|
|
int code;
|
|
if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
|
|
unsigned char *s;
|
|
if ((s = cast_uchar strchr(cast_const_char ct, ';'))) *s = 0;
|
|
while (*ct && ct[strlen(cast_const_char ct) - 1] <= ' ') ct[strlen(cast_const_char ct) - 1] = 0;
|
|
if (*ct == '"' && ct[1] && ct[strlen(cast_const_char ct) - 1] == '"') {
|
|
memmove(ct, ct + 1, strlen(cast_const_char ct));
|
|
ct[strlen(cast_const_char ct) - 1] = 0;
|
|
}
|
|
if (!casestrcmp(ct, cast_uchar "text/plain") ||
|
|
!casestrcmp(ct, cast_uchar "application/octet-stream") ||
|
|
!casestrcmp(ct, cast_uchar "application/octetstream") ||
|
|
!casestrcmp(ct, cast_uchar "application/octet_stream") ||
|
|
!casestrcmp(ct, cast_uchar "application/binary") ||
|
|
!casestrcmp(ct, cast_uchar "application/x-www-form-urlencoded") ||
|
|
get_content_encoding_from_content_type(ct)) {
|
|
unsigned char *ctt;
|
|
if (!get_http_code(head, &code, NULL) && code >= 300)
|
|
goto no_code_by_extension;
|
|
ctt = get_content_type_by_header_and_extension(head, url);
|
|
if (ctt) {
|
|
mem_free(ct);
|
|
return ctt;
|
|
}
|
|
}
|
|
no_code_by_extension:
|
|
if (!*ct) mem_free(ct);
|
|
else return ct;
|
|
}
|
|
if (!get_http_code(head, &code, NULL) && code >= 300)
|
|
return stracpy(cast_uchar "text/html");
|
|
ct = get_content_type_by_header_and_extension(head, url);
|
|
if (ct) return ct;
|
|
return !force_html ? stracpy(cast_uchar "text/plain") : stracpy(cast_uchar "text/html");
|
|
}
|
|
|
|
unsigned char *get_content_encoding(unsigned char *head, unsigned char *url, int just_ce)
|
|
{
|
|
unsigned char *ce, *ct, *ext, *extd;
|
|
unsigned char *u;
|
|
int code;
|
|
if ((ce = parse_http_header(head, cast_uchar "Content-Encoding", NULL))) return ce;
|
|
if (just_ce) return NULL;
|
|
if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
|
|
unsigned char *s;
|
|
if ((s = cast_uchar strchr(cast_const_char ct, ';'))) *s = 0;
|
|
ce = get_content_encoding_from_content_type(ct);
|
|
if (ce) {
|
|
mem_free(ct);
|
|
return stracpy(ce);
|
|
}
|
|
if (is_html_type(ct)) {
|
|
mem_free(ct);
|
|
return NULL;
|
|
}
|
|
mem_free(ct);
|
|
}
|
|
if (!get_http_code(head, &code, NULL) && code >= 300) return NULL;
|
|
if (!(ext = get_url_data(url))) ext = url;
|
|
for (u = ext; *u; u++) if (end_of_dir(url, *u)) goto skip_ext;
|
|
extd = cast_uchar strrchr(cast_const_char ext, '.');
|
|
if (extd) {
|
|
ce = get_compress_by_extension(extd + 1, cast_uchar strchr(cast_const_char(extd + 1), 0));
|
|
if (ce) return stracpy(ce);
|
|
}
|
|
skip_ext:
|
|
if ((ext = get_filename_from_header(head))) {
|
|
extd = cast_uchar strrchr(cast_const_char ext, '.');
|
|
if (extd) {
|
|
ce = get_compress_by_extension(extd + 1, cast_uchar strchr(cast_const_char(extd + 1), 0));
|
|
if (ce) {
|
|
mem_free(ext);
|
|
return stracpy(ce);
|
|
}
|
|
}
|
|
mem_free(ext);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
unsigned char *encoding_2_extension(unsigned char *encoding)
|
|
{
|
|
if (!casestrcmp(encoding, cast_uchar "gzip") ||
|
|
!casestrcmp(encoding, cast_uchar "x-gzip")) return cast_uchar "gz";
|
|
if (!casestrcmp(encoding, cast_uchar "compress") ||
|
|
!casestrcmp(encoding, cast_uchar "x-compress")) return cast_uchar "Z";
|
|
if (!casestrcmp(encoding, cast_uchar "br")) return cast_uchar "br";
|
|
if (!casestrcmp(encoding, cast_uchar "zstd")) return cast_uchar "zst";
|
|
if (!casestrcmp(encoding, cast_uchar "bzip2")) return cast_uchar "bz2";
|
|
if (!casestrcmp(encoding, cast_uchar "lzma")) return cast_uchar "lzma";
|
|
if (!casestrcmp(encoding, cast_uchar "lzma2")) return cast_uchar "xz";
|
|
if (!casestrcmp(encoding, cast_uchar "lzip")) return cast_uchar "lz";
|
|
return NULL;
|
|
}
|
|
|
|
/* returns field with associations */
|
|
struct assoc *get_type_assoc(struct terminal *term, unsigned char *type, int *n)
|
|
{
|
|
struct assoc *assoc_array;
|
|
struct list *l;
|
|
struct list_head *ll;
|
|
int count=0;
|
|
foreach(struct list, l, ll, assoc.list_entry) {
|
|
struct assoc *a = get_struct(l, struct assoc, head);
|
|
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))) {
|
|
if (count == MAXINT) overalloc();
|
|
count++;
|
|
}
|
|
}
|
|
*n = count;
|
|
if (!count) return NULL;
|
|
if ((unsigned)count > MAXINT / sizeof(struct assoc)) overalloc();
|
|
assoc_array = mem_alloc(count * sizeof(struct assoc));
|
|
count = 0;
|
|
foreach(struct list, l, ll, assoc.list_entry) {
|
|
struct assoc *a = get_struct(l, struct assoc, head);
|
|
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))) {
|
|
assoc_array[count++] = *a;
|
|
}
|
|
}
|
|
return assoc_array;
|
|
}
|
|
|
|
int is_html_type(unsigned char *ct)
|
|
{
|
|
return !casestrcmp(ct, cast_uchar "text/html") ||
|
|
!casestrcmp(ct, cast_uchar "text-html") ||
|
|
!casestrcmp(ct, cast_uchar "text/x-server-parsed-html") ||
|
|
!casestrcmp(ct, cast_uchar "text/xml") ||
|
|
!casecmp(ct, cast_uchar "application/xhtml", strlen("application/xhtml"));
|
|
}
|
|
|
|
unsigned char *get_filename_from_header(unsigned char *head)
|
|
{
|
|
int extended = 0;
|
|
unsigned char *ct, *x, *y, *codepage;
|
|
int ly;
|
|
int cp_idx;
|
|
if ((ct = parse_http_header(head, cast_uchar "Content-Disposition", NULL))) {
|
|
x = parse_header_param(ct, cast_uchar "filename*", 1);
|
|
if (x) extended = 1;
|
|
else x = parse_header_param(ct, cast_uchar "filename", 1);
|
|
mem_free(ct);
|
|
if (x) {
|
|
if (*x) goto ret_x;
|
|
mem_free(x);
|
|
}
|
|
}
|
|
if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
|
|
x = parse_header_param(ct, cast_uchar "name*", 0);
|
|
if (x) extended = 1;
|
|
else x = parse_header_param(ct, cast_uchar "name", 0);
|
|
mem_free(ct);
|
|
if (x) {
|
|
if (*x) goto ret_x;
|
|
mem_free(x);
|
|
}
|
|
}
|
|
return NULL;
|
|
ret_x:
|
|
codepage = NULL;
|
|
if (extended) {
|
|
unsigned char *ap1, *ap2;
|
|
ap1 = cast_uchar strchr(cast_const_char x, '\'');
|
|
if (!ap1)
|
|
goto no_extended;
|
|
ap2 = cast_uchar strchr(cast_const_char (ap1 + 1), '\'');
|
|
if (ap2) ap2++;
|
|
else ap2 = ap1 + 1;
|
|
codepage = memacpy(x, ap1 - x);
|
|
memmove(x, ap2, strlen(cast_const_char ap2) + 1);
|
|
}
|
|
|
|
no_extended:
|
|
y = init_str();
|
|
ly = 0;
|
|
add_conv_str(&y, &ly, x, (int)strlen(cast_const_char x), -2);
|
|
mem_free(x);
|
|
x = y;
|
|
|
|
cp_idx = -1;
|
|
if (codepage) {
|
|
cp_idx = get_cp_index(codepage);
|
|
mem_free(codepage);
|
|
}
|
|
if (cp_idx < 0) {
|
|
cp_idx = get_cp_index(cast_uchar "iso-8859-1");
|
|
if (cp_idx < 0) cp_idx = 0;
|
|
}
|
|
y = convert(cp_idx, 0, x, NULL);
|
|
mem_free(x);
|
|
x = y;
|
|
|
|
for (y = x; *y; y++) if (dir_sep(*y)
|
|
#if defined(DOS_FS) || defined(SPAD)
|
|
|| *y == ':'
|
|
#endif
|
|
) *y = '-';
|
|
return x;
|
|
}
|
|
|
|
unsigned char *get_filename_from_url(unsigned char *url, unsigned char *head, int tmp)
|
|
{
|
|
int ll = 0;
|
|
unsigned char *u, *s, *e, *f, *x, *ww;
|
|
unsigned char *ct, *want_ext;
|
|
if (!casecmp(url, cast_uchar "data:", 5)) {
|
|
url = cast_uchar "data:/data";
|
|
}
|
|
want_ext = stracpy(cast_uchar "");
|
|
f = get_filename_from_header(head);
|
|
if (f) {
|
|
goto no_ct;
|
|
}
|
|
if (!(u = get_url_data(url))) u = url;
|
|
for (e = s = u; *e && !end_of_dir(url, *e); e++) {
|
|
if (dir_sep(*e)) s = e + 1;
|
|
}
|
|
ll = 0;
|
|
f = init_str();
|
|
add_conv_str(&f, &ll, s, (int)(e - s), -2);
|
|
if (!(ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) goto no_ct;
|
|
mem_free(ct);
|
|
ct = get_content_type(head, url);
|
|
if (ct) {
|
|
x = get_extension_by_content_type(ct);
|
|
if (x) {
|
|
add_to_strn(&want_ext, cast_uchar ".");
|
|
add_to_strn(&want_ext, x);
|
|
mem_free(x);
|
|
}
|
|
mem_free(ct);
|
|
}
|
|
no_ct:
|
|
if (!*want_ext) {
|
|
x = cast_uchar strrchr(cast_const_char f, '.');
|
|
if (x) {
|
|
mem_free(want_ext);
|
|
want_ext = stracpy(x);
|
|
}
|
|
}
|
|
ct = get_content_encoding(head, url, 0);
|
|
if (ct) {
|
|
x = encoding_2_extension(ct);
|
|
if (!tmp) {
|
|
unsigned char *ct1;
|
|
ct1 = get_content_encoding(head, url, 1);
|
|
if (ct1) {
|
|
mem_free(ct1);
|
|
} else if (x) {
|
|
unsigned char *w = cast_uchar strrchr(cast_const_char want_ext, '.');
|
|
if (w && (ww = canonical_compressed_ext(w + 1, NULL)) && !casestrcmp(x, ww))
|
|
goto skip_want_ext;
|
|
if (w && !casestrcmp(w + 1, x))
|
|
goto skip_want_ext;
|
|
add_to_strn(&want_ext, cast_uchar ".");
|
|
add_to_strn(&want_ext, x);
|
|
skip_want_ext:;
|
|
}
|
|
} else {
|
|
if (x) {
|
|
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)) {
|
|
f[strlen(cast_const_char f) - strlen(cast_const_char x) - 1] = 0;
|
|
}
|
|
}
|
|
}
|
|
mem_free(ct);
|
|
}
|
|
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))) {
|
|
x = cast_uchar strrchr(cast_const_char f, '.');
|
|
if (x && (ww = canonical_compressed_ext(x + 1, NULL)) && want_ext[0] == '.' && !casestrcmp(want_ext + 1, ww))
|
|
goto skip_tgz_2;
|
|
if (x) *x = 0;
|
|
add_to_strn(&f, want_ext);
|
|
skip_tgz_2:;
|
|
}
|
|
mem_free(want_ext);
|
|
return f;
|
|
}
|
|
|
|
static void free_prog_list(struct list_head *l)
|
|
{
|
|
struct list_head *lp;
|
|
struct protocol_program *p;
|
|
foreach(struct protocol_program, p, lp, *l)
|
|
mem_free(p->prog);
|
|
free_list(struct protocol_program, *l);
|
|
}
|
|
|
|
void free_types(void)
|
|
{
|
|
struct list *l;
|
|
struct list_head *ll;
|
|
foreach(struct list, l, ll, assoc.list_entry) {
|
|
struct assoc *a = get_struct(l, struct assoc, head);
|
|
mem_free(a->ct);
|
|
mem_free(a->prog);
|
|
mem_free(a->label);
|
|
ll = ll->prev;
|
|
del_from_list(&a->head);
|
|
mem_free(a);
|
|
}
|
|
foreach(struct list, l, ll, extensions.list_entry) {
|
|
struct extension *e = get_struct(l, struct extension, head);
|
|
mem_free(e->ext);
|
|
mem_free(e->ct);
|
|
ll = ll->prev;
|
|
del_from_list(&e->head);
|
|
mem_free(e);
|
|
}
|
|
free_prog_list(&mailto_prog);
|
|
free_prog_list(&telnet_prog);
|
|
free_prog_list(&tn3270_prog);
|
|
free_prog_list(&mms_prog);
|
|
free_prog_list(&magnet_prog);
|
|
free_prog_list(&gopher_prog);
|
|
|
|
free_history(ext_search_history);
|
|
free_history(assoc_search_history);
|
|
}
|