summaryrefslogtreecommitdiff
path: root/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'session.c')
-rw-r--r--session.c2855
1 files changed, 2855 insertions, 0 deletions
diff --git a/session.c b/session.c
new file mode 100644
index 0000000..2a522af
--- /dev/null
+++ b/session.c
@@ -0,0 +1,2855 @@
+/* session.c
+ * (c) 2002 Mikulas Patocka
+ * This file is a part of the Links program, released under GPL.
+ */
+
+#include "links.h"
+
+struct list_head downloads = {&downloads, &downloads};
+
+/* prototypes */
+static void abort_and_delete_download(struct download *down);
+static void undisplay_download(struct download *down);
+static void increase_download_file(unsigned char **f);
+static void copy_additional_files(struct additional_files **a);
+static struct location *new_location(void);
+static void destroy_location(struct location *loc);
+
+
+int are_there_downloads(void)
+{
+ int d = 0;
+ struct download *down;
+ foreach(down, downloads) if (!down->prog) d = 1;
+ return d;
+}
+
+struct list_head sessions = {&sessions, &sessions};
+
+struct strerror_val {
+ struct strerror_val *next;
+ struct strerror_val *prev;
+ unsigned char msg[1];
+};
+
+static struct list_head strerror_buf = { &strerror_buf, &strerror_buf };
+
+void free_strerror_buf(void)
+{
+ free_list(strerror_buf);
+}
+
+int get_error_from_errno(int errn)
+{
+ if (errn > 0 && (errn < -S__OK || errn > -S_MAX))
+ return -errn;
+#ifdef BEOS
+ if (-errn > 0 && (-errn < -S__OK || -errn > -S_MAX))
+ return errn;
+#endif
+ return S_UNKNOWN_ERROR;
+}
+
+unsigned char *get_err_msg(int state)
+{
+ unsigned char *e;
+ struct strerror_val *s;
+ if ((state >= S_MAX && state <= S__OK) || state >= S_WAIT) {
+ int i;
+ for (i = 0; msg_dsc[i].msg; i++)
+ if (msg_dsc[i].n == state) return msg_dsc[i].msg;
+ unk:
+ return TEXT_(T_UNKNOWN_ERROR);
+ }
+#ifdef BEOS
+ if ((e = strerror(state)) && *e && !strstr(e, "No Error")) goto have_error;
+#endif
+ if ((e = strerror(-state)) && *e) goto have_error;
+ goto unk;
+have_error:
+ foreach(s, strerror_buf) if (!strcmp(s->msg, e)) return s->msg;
+ s = mem_alloc(sizeof(struct strerror_val) + strlen(e));
+ strcpy(s->msg, e);
+ add_to_list(strerror_buf, s);
+ return s->msg;
+}
+
+static void add_xnum_to_str(unsigned char **s, int *l, off_t n)
+{
+ unsigned char suff = 0;
+ int d = -1;
+ if (n >= 1000000000) suff = 'G', d = (n / 100000000) % 10, n /= 1000000000;
+ else if (n >= 1000000) suff = 'M', d = (n / 100000) % 10, n /= 1000000;
+ else if (n >= 1000) suff = 'k', d = (n / 100) % 10, n /= 1000;
+ add_num_to_str(s, l, n);
+ if (n < 10 && d != -1) add_chr_to_str(s, l, '.'), add_num_to_str(s, l, d);
+ add_chr_to_str(s, l, ' ');
+ if (suff) add_chr_to_str(s, l, suff);
+ add_chr_to_str(s, l, 'B');
+}
+
+static void add_time_to_str(unsigned char **s, int *l, ttime t)
+{
+ unsigned char q[64];
+ if (t < 0) t = 0;
+ if (t >= 86400) sprintf(q, "%ud ", (unsigned)(t / 86400)), add_to_str(s, l, q);
+ if (t >= 3600) t %= 86400, sprintf(q, "%d:%02d", (int)(t / 3600), (int)(t / 60 % 60)), add_to_str(s, l, q);
+ else sprintf(q, "%d", (int)(t / 60)), add_to_str(s, l, q);
+ sprintf(q, ":%02d", (int)(t % 60)), add_to_str(s, l, q);
+}
+
+static unsigned char *get_stat_msg(struct status *stat, struct terminal *term)
+{
+ if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
+ unsigned char *m = init_str();
+ int l = 0;
+ add_to_str(&m, &l, _(TEXT_(T_RECEIVED), term));
+ add_to_str(&m, &l, " ");
+ add_xnum_to_str(&m, &l, stat->prg->pos);
+ if (stat->prg->size >= 0)
+ add_to_str(&m, &l, " "), add_to_str(&m, &l, _(TEXT_(T_OF), term)), add_to_str(&m, &l, " "), add_xnum_to_str(&m, &l, stat->prg->size);
+ add_to_str(&m, &l, ", ");
+ if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
+ add_to_str(&m, &l, _(TEXT_(T_AVG), term)), add_to_str(&m, &l, " ");
+ add_xnum_to_str(&m, &l, stat->prg->loaded * 10 / (stat->prg->elapsed / 100));
+ add_to_str(&m, &l, "/s");
+ if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
+ add_to_str(&m, &l, ", "), add_to_str(&m, &l, _(TEXT_(T_CUR), term)), add_to_str(&m, &l, " "),
+ add_xnum_to_str(&m, &l, stat->prg->cur_loaded / (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000)),
+ add_to_str(&m, &l, "/s");
+ return m;
+ }
+ return stracpy(_(get_err_msg(stat->state), term));
+}
+
+void change_screen_status(struct session *ses)
+{
+ struct status *stat = NULL;
+ if (ses->rq) {
+ stat = &ses->rq->stat;
+ } else {
+ struct f_data_c *fd = current_frame(ses);
+ if (fd->rq) stat = &fd->rq->stat;
+ if (stat && stat->state == S__OK && fd->af) {
+ struct additional_file *af;
+ foreach(af, fd->af->af) {
+ if (af->rq && af->rq->stat.state >= 0) {
+ if (af->rq->stat.state > stat->state ||
+ (af->rq->stat.state == S_TRANS &&
+ stat->state == S_TRANS &&
+ af->rq->stat.prg->pos >
+ stat->prg->pos))
+ stat = &af->rq->stat;
+ }
+ }
+ }
+ }
+ if (ses->st) mem_free(ses->st);
+
+ /* default status se ukazuje, kdyz
+ * a) by se jinak ukazovalo prazdno
+ * b) neni NULL a ukazovalo by se OK
+ */
+ ses->st = NULL;
+ if (stat) {
+ if (stat->state == S__OK) ses->st = print_current_link(ses);
+ if (!ses->st) ses->st = ses->default_status ? stracpy(ses->default_status) : get_stat_msg(stat, ses->term);
+ } else {
+ ses->st = stracpy(ses->default_status);
+ }
+}
+
+static void x_print_screen_status(struct terminal *term, struct session *ses)
+{
+ unsigned char *m;
+ if (!F) {
+ int color = (ses->ds.t_background_color << 3) | (ses->ds.t_text_color & 7) | ((ses->ds.t_text_color & 8) << 3);
+ if (!term->spec->col) color = COLOR_TITLE;
+ fill_area(term, 0, term->y - 1, term->x, 1, ' ', color);
+ if (ses->st) print_text(term, 0, term->y - 1, strlen(ses->st), ses->st, COLOR_STATUS);
+ fill_area(term, 0, 0, term->x, 1, ' ', color);
+ if ((m = print_current_title(ses))) {
+ int p = term->x - 1 - cp_len(ses->term->spec->charset, m);
+ if (p < 0) p = 0;
+ if (term->spec->braille) p = 0;
+ print_text(term, p, 0, cp_len(ses->term->spec->charset, m), m, color);
+ mem_free(m);
+ }
+#ifdef G
+ } else {
+ int l = 0;
+ if (ses->st) g_print_text(drv, term->dev, 0, term->y - G_BFU_FONT_SIZE, bfu_style_wb_mono, ses->st, &l);
+ drv->fill_area(term->dev, l, term->y - G_BFU_FONT_SIZE, term->x, term->y, bfu_bg_color);
+#endif
+ }
+}
+
+void print_screen_status(struct session *ses)
+{
+ unsigned char *m;
+#ifdef G
+ if (F) {
+ /*debug("%s - %s", ses->st_old, ses->st);
+ debug("clip: %d.%d , %d.%d", ses->term->dev->clip.x1, ses->term->dev->clip.y1, ses->term->dev->clip.x2, ses->term->dev->clip.y2);
+ debug("size: %d.%d , %d.%d", ses->term->dev->size.x1, ses->term->dev->size.y1, ses->term->dev->size.x2, ses->term->dev->size.y2);*/
+ if (ses->st_old) {
+ if (ses->st && !strcmp(ses->st, ses->st_old)) goto skip_status;
+ mem_free(ses->st_old);
+ ses->st_old = NULL;
+ }
+ if (!memcmp(&ses->term->dev->clip, &ses->term->dev->size, sizeof(struct rect))) ses->st_old = stracpy(ses->st);
+ }
+#endif
+ draw_to_window(ses->win, (void (*)(struct terminal *, void *))x_print_screen_status, ses);
+#ifdef G
+ skip_status:
+#endif
+ if ((m = stracpy("Links"))) {
+ if (ses->screen && ses->screen->f_data && ses->screen->f_data->title && ses->screen->f_data->title[0]) add_to_strn(&m, " - "), add_to_strn(&m, ses->screen->f_data->title);
+ set_terminal_title(ses->term, m);
+ /*mem_free(m); -- set_terminal_title frees it */
+ }
+ if (!F && ses->brl_cursor_mode) {
+ if (ses->brl_cursor_mode == 1) set_cursor(ses->term, 0, 0, 0, 0);
+ if (ses->brl_cursor_mode == 2) set_cursor(ses->term, 0, ses->term->y - 1, 0, ses->term->y - 1);
+ }
+}
+
+void print_error_dialog(struct session *ses, struct status *stat, unsigned char *title)
+{
+ unsigned char *t = get_err_msg(stat->state);
+ unsigned char *u = stracpy(title);
+ unsigned char *pc;
+ if ((pc = strchr(u, POST_CHAR))) *pc = 0;
+ if (!t) return;
+ msg_box(ses->term, getml(u, NULL), TEXT_(T_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_ERROR_LOADING), " ", u, ":\n\n", t, NULL, ses, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC/*, _("Retry"), NULL, 0 !!! FIXME: retry */);
+}
+
+static inline unsigned char hx(int a)
+{
+ return a >= 10 ? a + 'A' - 10 : a + '0';
+}
+
+static inline int unhx(unsigned char a)
+{
+ if (a >= '0' && a <= '9') return a - '0';
+ if (a >= 'A' && a <= 'F') return a - 'A' + 10;
+ if (a >= 'a' && a <= 'f') return a - 'a' + 10;
+ return -1;
+}
+
+unsigned char *encode_url(unsigned char *url)
+{
+ unsigned char *u = init_str();
+ int l = 0;
+ add_to_str(&u, &l, "+++");
+ for (; *url; url++) {
+ if (is_safe_in_shell(*url) && *url != '+') add_chr_to_str(&u, &l, *url);
+ else add_chr_to_str(&u, &l, '+'), add_chr_to_str(&u, &l, hx(*url >> 4)), add_chr_to_str(&u, &l, hx(*url & 0xf));
+ }
+ return u;
+}
+
+unsigned char *decode_url(unsigned char *url)
+{
+ unsigned char *u;
+ int l;
+ if (casecmp(url, "+++", 3)) return stracpy(url);
+ url += 3;
+ u = init_str();
+ l = 0;
+ for (; *url; url++) {
+ if (*url != '+' || unhx(url[1]) == -1 || unhx(url[2]) == -1) add_chr_to_str(&u, &l, *url);
+ else add_chr_to_str(&u, &l, (unhx(url[1]) << 4) + unhx(url[2])), url += 2;
+ }
+ return u;
+}
+
+static struct session *get_download_ses(struct download *down)
+{
+ struct session *ses;
+ foreach(ses, sessions) if (ses == down->ses) return ses;
+ if (!list_empty(sessions)) return sessions.next;
+ return NULL;
+}
+
+static void close_download_file(struct download *down)
+{
+ int rs;
+ if (down->handle != -1) {
+ EINTRLOOP(rs, ftruncate(down->handle, down->last_pos - down->file_shift));
+ EINTRLOOP(rs, close(down->handle));
+ down->handle = -1;
+ }
+}
+
+static void delete_download_file(struct download *down)
+{
+ int rs;
+ unsigned char *file = stracpy(down->orig_file);
+ unsigned char *wd = get_cwd();
+ set_cwd(down->cwd);
+ while (1) {
+ unsigned char *f = translate_download_file(file);
+ EINTRLOOP(rs, unlink(f));
+ mem_free(f);
+ if (!strcmp(file, down->file)) break;
+ increase_download_file(&file);
+ }
+ mem_free(file);
+ if (wd) set_cwd(wd), mem_free(wd);
+}
+
+static void abort_download(struct download *down)
+{
+ unregister_bottom_half((void (*)(void *))abort_download, down);
+ unregister_bottom_half((void (*)(void *))abort_and_delete_download, down);
+ unregister_bottom_half((void (*)(void *))undisplay_download, down);
+
+ if (down->win) delete_window(down->win);
+ if (down->ask) delete_window(down->ask);
+ if (down->stat.state >= 0) change_connection(&down->stat, NULL, PRI_CANCEL);
+ mem_free(down->url);
+ close_download_file(down);
+ if (down->prog) {
+ delete_download_file(down);
+
+ mem_free(down->prog);
+ }
+ mem_free(down->cwd);
+ mem_free(down->orig_file);
+ mem_free(down->file);
+ del_from_list(down);
+ mem_free(down);
+}
+
+static void abort_and_delete_download(struct download *down)
+{
+ if (!down->prog) down->prog = DUMMY;
+ abort_download(down);
+}
+
+int test_abort_downloads_to_file(unsigned char *file, unsigned char *cwd, int abort_downloads)
+{
+ int ret = 0;
+ struct download *down;
+ foreach(down, downloads) {
+ if (strcmp(down->cwd, cwd)) {
+#if defined(DOS_FS)
+ if (file[0] && file[1] == ':' && dir_sep(file[2])) goto abs;
+#elif defined(SPAD)
+ if (_is_absolute(file) == _ABS_TOTAL) goto abs;
+#else
+ if (file[0] == '/') goto abs;
+#endif
+ continue;
+ }
+ abs:
+ if (!strcmp(down->file, file) || !strcmp(down->orig_file, file)) {
+ ret = 1;
+ if (!abort_downloads) break;
+ down = down->prev;
+ abort_download(down->next);
+ }
+ }
+ return ret;
+}
+
+static void undisplay_download(struct download *down)
+{
+ if (down->win) delete_window(down->win);
+}
+
+static int dlg_abort_download(struct dialog_data *dlg, struct dialog_item_data *di)
+{
+ register_bottom_half((void (*)(void *))abort_download, dlg->dlg->udata);
+ return 0;
+}
+
+static int dlg_abort_and_delete_download(struct dialog_data *dlg, struct dialog_item_data *di)
+{
+ register_bottom_half((void (*)(void *))abort_and_delete_download, dlg->dlg->udata);
+ return 0;
+}
+
+static int dlg_undisplay_download(struct dialog_data *dlg, struct dialog_item_data *di)
+{
+ register_bottom_half((void (*)(void *))undisplay_download, dlg->dlg->udata);
+ return 0;
+}
+
+static void download_abort_function(struct dialog_data *dlg)
+{
+ struct download *down = dlg->dlg->udata;
+ down->win = NULL;
+}
+
+static int test_percentage(struct status *stat)
+{
+ return stat->prg->size > 0;
+}
+
+static int download_meter(int size, struct status *stat)
+{
+ int m;
+ if (!stat->prg->size) return 0;
+ m = (int)((double)size * (double)stat->prg->pos / (double)stat->prg->size);
+ if (m < 0) m = 0;
+ if (m > size) m = size;
+ return m;
+}
+
+unsigned char *download_percentage(struct download *down, int pad)
+{
+ unsigned char *s;
+ int l;
+ int perc;
+ struct status *stat = &down->stat;
+ if (stat->state != S_TRANS || !test_percentage(stat)) return stracpy("");
+ s = init_str();
+ l = 0;
+ perc = download_meter(100, stat);
+ if (pad) {
+ if (perc < 10) add_chr_to_str(&s, &l, ' ');
+ if (perc < 100) add_chr_to_str(&s, &l, ' ');
+ }
+ add_num_to_str(&s, &l, perc);
+ add_chr_to_str(&s, &l, '%');
+ return s;
+}
+
+void download_window_function(struct dialog_data *dlg)
+{
+ struct download *down = dlg->dlg->udata;
+ struct terminal *term = dlg->win->term;
+ int max = 0, min = 0;
+ int w, x, y;
+ int t = 0;
+ int show_percentage = 0;
+ unsigned char *m, *u;
+ struct status *stat = &down->stat;
+ if (!F) redraw_below_window(dlg->win);
+ down->win = dlg->win;
+ if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
+ int l = 0;
+ m = init_str();
+ t = 1;
+ add_to_str(&m, &l, _(TEXT_(T_RECEIVED), term));
+ add_to_str(&m, &l, " ");
+ add_xnum_to_str(&m, &l, stat->prg->pos);
+ if (stat->prg->size >= 0)
+ add_to_str(&m, &l, " "), add_to_str(&m, &l, _(TEXT_(T_OF),term)), add_to_str(&m, &l, " "), add_xnum_to_str(&m, &l, stat->prg->size), add_to_str(&m, &l, " ");
+ add_to_str(&m, &l, "\n");
+ if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
+ add_to_str(&m, &l, _(TEXT_(T_AVERAGE_SPEED), term));
+ else add_to_str(&m, &l, _(TEXT_(T_SPEED), term));
+ add_to_str(&m, &l, " ");
+ add_xnum_to_str(&m, &l, (longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100));
+ add_to_str(&m, &l, "/s");
+ if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
+ add_to_str(&m, &l, ", "), add_to_str(&m, &l, _(TEXT_(T_CURRENT_SPEED), term)), add_to_str(&m, &l, " "),
+ add_xnum_to_str(&m, &l, stat->prg->cur_loaded / (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000)),
+ add_to_str(&m, &l, "/s");
+ add_to_str(&m, &l, "\n");
+ add_to_str(&m, &l, _(TEXT_(T_ELAPSED_TIME), term));
+ add_to_str(&m, &l, " ");
+ add_time_to_str(&m, &l, (uttime)stat->prg->elapsed / 1000);
+ if (stat->prg->size >= 0 && stat->prg->loaded > 0) {
+ add_to_str(&m, &l, ", ");
+ add_to_str(&m, &l, _(TEXT_(T_ESTIMATED_TIME), term));
+ add_to_str(&m, &l, " ");
+ /*add_time_to_str(&m, &l, stat->prg->elapsed / 1000 * stat->prg->size / stat->prg->loaded * 1000 - stat->prg->elapsed);*/
+ /*add_time_to_str(&m, &l, (stat->prg->size - stat->prg->pos) / ((longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100)));*/
+ add_time_to_str(&m, &l, (stat->prg->size - stat->prg->pos) / ((double)stat->prg->loaded * 1000 / stat->prg->elapsed));
+ }
+ } else m = stracpy(_(get_err_msg(stat->state), term));
+ show_percentage = t && test_percentage(stat);
+ u = stracpy(down->url);
+ if (strchr(u, POST_CHAR)) *strchr(u, POST_CHAR) = 0;
+ max_text_width(term, u, &max, AL_LEFT);
+ min_text_width(term, u, &min, AL_LEFT);
+ max_text_width(term, m, &max, AL_LEFT);
+ min_text_width(term, m, &min, AL_LEFT);
+ max_buttons_width(term, dlg->items, dlg->n, &max);
+ min_buttons_width(term, dlg->items, dlg->n, &min);
+ w = dlg->win->term->x * 9 / 10 - 2 * DIALOG_LB;
+ if (w < min) w = min;
+ if (!dlg->win->term->spec->braille && w > dlg->win->term->x - 2 * DIALOG_LB) w = dlg->win->term->x - 2 * DIALOG_LB;
+ if (show_percentage) {
+ if (w < DOWN_DLG_MIN) w = DOWN_DLG_MIN;
+ } else {
+ if (w > max) w = max;
+ }
+ if (w < 1) w = 1;
+ y = 0;
+ dlg_format_text(dlg, NULL, u, 0, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
+ y += gf_val(1, G_BFU_FONT_SIZE);
+ if (show_percentage) y += gf_val(2, 2 * G_BFU_FONT_SIZE);
+ dlg_format_text(dlg, NULL, m, 0, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
+ y += gf_val(1, G_BFU_FONT_SIZE);
+ dlg_format_buttons(dlg, NULL, dlg->items, dlg->n, 0, &y, w, NULL, AL_CENTER);
+ dlg->xw = w + 2 * DIALOG_LB;
+ dlg->yw = y + 2 * DIALOG_TB;
+ center_dlg(dlg);
+ draw_dlg(dlg);
+ y = dlg->y + DIALOG_TB + gf_val(1, G_BFU_FONT_SIZE);
+ x = dlg->x + DIALOG_LB;
+ dlg_format_text(dlg, term, u, x, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
+ if (show_percentage) {
+ if (!F) {
+ unsigned char *q;
+ int p = w - 6;
+ if (term->spec->braille && p > 39 - 6) p = 39 - 6;
+ y++;
+ set_only_char(term, x, y, '[', 0);
+ set_only_char(term, x + p + 1, y, ']', 0);
+ fill_area(term, x + 1, y, download_meter(p, stat), 1, CHAR_DIALOG_METER, COLOR_DIALOG_METER);
+ q = download_percentage(down, 1);
+ print_text(term, x + p + 2, y, strlen(q), q, COLOR_DIALOG_TEXT);
+ mem_free(q);
+ y++;
+#ifdef G
+ } else {
+ unsigned char *q;
+ int p, s, ss, m;
+ y += G_BFU_FONT_SIZE;
+ q = download_percentage(down, 1);
+ extend_str(&q, 1);
+ memmove(q + 1, q, strlen(q) + 1);
+ q[0] = ']';
+ s = g_text_width(bfu_style_bw_mono, "[");
+ ss = g_text_width(bfu_style_bw_mono, q);
+ p = w - s - ss;
+ if (p < 0) p = 0;
+ m = download_meter(p, stat);
+ g_print_text(drv, term->dev, x, y, bfu_style_bw_mono, "[", NULL);
+ drv->fill_area(term->dev, x + s, y, x + s + m, y + G_BFU_FONT_SIZE, bfu_fg_color);
+ drv->fill_area(term->dev, x + s + m, y, x + s + p, y + G_BFU_FONT_SIZE, bfu_bg_color);
+ g_print_text(drv, term->dev, x + w - ss, y, bfu_style_bw_mono, q, NULL);
+ if (dlg->s) exclude_from_set(&dlg->s, x, y, x + w, y + G_BFU_FONT_SIZE);
+ mem_free(q);
+ y += G_BFU_FONT_SIZE;
+#endif
+ }
+ }
+ y += gf_val(1, G_BFU_FONT_SIZE);
+ dlg_format_text(dlg, term, m, x, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
+ y += gf_val(1, G_BFU_FONT_SIZE);
+ dlg_format_buttons(dlg, term, dlg->items, dlg->n, x, &y, w, NULL, AL_CENTER);
+ mem_free(u);
+ mem_free(m);
+}
+
+void display_download(struct terminal *term, struct download *down, struct session *ses)
+{
+ struct dialog *dlg;
+ struct download *dd;
+ foreach(dd, downloads) if (dd == down) goto found;
+ return;
+ found:
+ dlg = mem_calloc(sizeof(struct dialog) + 4 * sizeof(struct dialog_item));
+ undisplay_download(down);
+ down->ses = ses;
+ dlg->title = TEXT_(T_DOWNLOAD);
+ dlg->fn = download_window_function;
+ dlg->abort = download_abort_function;
+ dlg->udata = down;
+ dlg->align = AL_CENTER;
+ dlg->items[0].type = D_BUTTON;
+ dlg->items[0].gid = B_ENTER | B_ESC;
+ dlg->items[0].fn = dlg_undisplay_download;
+ dlg->items[0].text = TEXT_(T_BACKGROUND);
+ dlg->items[1].type = D_BUTTON;
+ dlg->items[1].gid = 0;
+ dlg->items[1].fn = dlg_abort_download;
+ dlg->items[1].text = TEXT_(T_ABORT);
+ if (!down->prog) {
+ dlg->items[2].type = D_BUTTON;
+ dlg->items[2].gid = 0;
+ dlg->items[2].fn = dlg_abort_and_delete_download;
+ dlg->items[2].text = TEXT_(T_ABORT_AND_DELETE_FILE);
+ dlg->items[3].type = D_END;
+ } else {
+ dlg->items[2].type = D_END;
+ }
+ do_dialog(term, dlg, getml(dlg, NULL));
+}
+
+time_t parse_http_date(unsigned char *date) /* this functions is bad !!! */
+{
+ static unsigned char *months[12] =
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ time_t t = 0;
+ /* Mon, 03 Jan 2000 21:29:33 GMT */
+ int y;
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+
+ date = strchr(date, ' ');
+ if (!date) return 0;
+ date++;
+ if (*date >= '0' && *date <= '9') {
+ /* Sun, 06 Nov 1994 08:49:37 GMT */
+ /* Sunday, 06-Nov-94 08:49:37 GMT */
+ y = 0;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ tm.tm_mday = (date[0] - '0') * 10 + date[1] - '0';
+ date += 2;
+ if (*date != ' ' && *date != '-') return 0;
+ date += 1;
+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++)
+ if (!casecmp(date, months[tm.tm_mon], 3)) goto f1;
+ return 0;
+ f1:
+ date += 3;
+ if (*date == ' ') {
+ /* Sun, 06 Nov 1994 08:49:37 GMT */
+ date++;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ if (date[2] < '0' || date[2] > '9') return 0;
+ if (date[3] < '0' || date[3] > '9') return 0;
+ tm.tm_year = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0' - 1900;
+ date += 4;
+ } else if (*date == '-') {
+ /* Sunday, 06-Nov-94 08:49:37 GMT */
+ date++;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ tm.tm_year = (date[0] >= '7' ? 1900 : 2000) + (date[0] - '0') * 10 + date[1] - '0' - 1900;
+ date += 2;
+ } else return 0;
+ if (*date != ' ') return 0;
+ date++;
+ } else {
+ /* Sun Nov 6 08:49:37 1994 */
+ y = 1;
+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++)
+ if (!casecmp(date, months[tm.tm_mon], 3)) goto f2;
+ return 0;
+ f2:
+ date += 3;
+ while (*date == ' ') date++;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ tm.tm_mday = date[0] - '0';
+ date++;
+ if (*date != ' ') {
+ if (date[0] < '0' || date[0] > '9') return 0;
+ tm.tm_mday = tm.tm_mday * 10 + date[0] - '0';
+ date++;
+ }
+ if (*date != ' ') return 0;
+ date++;
+ }
+
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ tm.tm_hour = (date[0] - '0') * 10 + date[1] - '0';
+ date += 2;
+ if (*date != ':') return 0;
+ date++;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ tm.tm_min = (date[0] - '0') * 10 + date[1] - '0';
+ date += 2;
+ if (*date != ':') return 0;
+ date++;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ tm.tm_sec = (date[0] - '0') * 10 + date[1] - '0';
+ date += 2;
+ if (y) {
+ if (*date != ' ') return 0;
+ date++;
+ if (date[0] < '0' || date[0] > '9') return 0;
+ if (date[1] < '0' || date[1] > '9') return 0;
+ if (date[2] < '0' || date[2] > '9') return 0;
+ if (date[3] < '0' || date[3] > '9') return 0;
+ tm.tm_year = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0' - 1900;
+ date += 4;
+ }
+ if (*date != ' ' && *date) return 0;
+
+ t = mktime(&tm);
+ if (t == (time_t) -1) return 0;
+ return t;
+}
+
+static int download_write(struct download *down, void *ptr, off_t to_write)
+{
+ int w;
+ int err;
+ if (to_write != (int)to_write || (int)to_write < 0) to_write = MAXINT;
+ try_write_again:
+ w = hard_write(down->handle, ptr, to_write);
+ if (w >= 0) err = 0;
+ else err = errno;
+ if (w <= -!to_write) {
+#ifdef EFBIG
+ if (err == EFBIG && !down->prog) {
+ if (to_write > 1) {
+ to_write >>= 1;
+ goto try_write_again;
+ }
+ if (down->last_pos == down->file_shift) goto no_e2big;
+ close_download_file(down);
+ increase_download_file(&down->file);
+ if ((down->handle = create_download_file(get_download_ses(down), down->cwd, down->file, 0, down->last_pos - down->file_shift)) < 0) return -1;
+ down->file_shift = down->last_pos;
+ goto try_write_again;
+ no_e2big:;
+ }
+#endif
+ if (get_download_ses(down)) {
+ unsigned char *emsg = stracpy(err ? strerror(err) : "Zero returned");
+ unsigned char *msg = stracpy(down->file);
+ msg_box(get_download_ses(down)->term, getml(msg, emsg, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_COULD_NOT_WRITE_TO_FILE), " ", msg, ": ", emsg, NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ }
+ return -1;
+ }
+ down->last_pos += w;
+ down->downloaded_something = 1;
+ return 0;
+}
+
+static void download_data(struct status *stat, struct download *down)
+{
+ struct cache_entry *ce;
+ struct fragment *frag;
+ int rs;
+ if (!(ce = stat->ce)) goto end_store;
+ if (stat->state >= S_WAIT && stat->state < S_TRANS) goto end_store;
+ if (!down->remotetime && ce->last_modified) down->remotetime = parse_http_date(ce->last_modified);
+ if (!down->downloaded_something) {
+ unsigned char *enc = get_content_encoding(ce->head, ce->url);
+ if (enc) {
+ if (!encoding_2_extension(enc)) down->decompress = 1;
+ mem_free(enc);
+ }
+ if (ce->redirect) {
+ if (down->redirect_cnt++ < MAX_REDIRECTS) {
+ unsigned char *u, *p, *pos;
+ unsigned char *prev_down_url;
+ int cache;
+ if (stat->state >= 0) change_connection(&down->stat, NULL, PRI_CANCEL);
+ u = join_urls(down->url, ce->redirect);
+ u = translate_hashbang(u);
+ if ((pos = extract_position(u))) mem_free(pos);
+ if (!http_options.bug_302_redirect) if (!ce->redirect_get && (p = strchr(down->url, POST_CHAR))) add_to_strn(&u, p);
+ prev_down_url = down->url;
+ down->url = u;
+ down->stat.state = S_WAIT_REDIR;
+ if (down->win) {
+ struct event ev = { EV_REDRAW, 0, 0, 0 };
+ ev.x = down->win->term->x;
+ ev.y = down->win->term->y;
+ down->win->handler(down->win, &ev, 0);
+ }
+ cache = NC_CACHE;
+ if (!strcmp(down->url, prev_down_url) || down->redirect_cnt >= MAX_CACHED_REDIRECTS) cache = NC_RELOAD;
+ mem_free(prev_down_url);
+ load_url(down->url, NULL, &down->stat, PRI_DOWNLOAD, cache, 1, down->last_pos);
+ return;
+ } else {
+ if (stat->state >= 0) change_connection(&down->stat, NULL, PRI_CANCEL);
+ stat->state = S_CYCLIC_REDIRECT;
+ goto end_store;
+ }
+ }
+ }
+ if (!down->decompress) foreach(frag, ce->frag) while (frag->offset <= down->last_pos && frag->offset + frag->length > down->last_pos) {
+#ifdef HAVE_OPEN_PREALLOC
+ if (!down->last_pos && !strcmp(down->file, down->orig_file) && (!down->stat.prg || down->stat.prg->size > 0)) {
+ struct stat st;
+ int rs;
+ EINTRLOOP(rs, fstat(down->handle, &st));
+ if (rs || !S_ISREG(st.st_mode)) goto skip_prealloc;
+ close_download_file(down);
+ delete_download_file(down);
+ if ((down->handle = create_download_file(get_download_ses(down), down->cwd, down->file, !down->prog ? CDF_EXCL : CDF_RESTRICT_PERMISSION | CDF_EXCL, down->stat.prg ? down->stat.prg->size : ce->length)) < 0) goto det_abt;
+ skip_prealloc:;
+ }
+#endif
+ if (download_write(down, frag->data + (down->last_pos - frag->offset), frag->length - (down->last_pos - frag->offset))) {
+ det_abt:
+ detach_connection(stat, down->last_pos);
+ abort_download(down);
+ return;
+ }
+ }
+ if (!down->decompress) detach_connection(stat, down->last_pos);
+ end_store:;
+ if (stat->state < 0) {
+ if (down->decompress) {
+ struct session *ses = get_download_ses(down);
+ unsigned char *start, *end;
+ int err;
+ get_file_by_term(ses ? ses->term : NULL, ce, &start, &end, &err);
+ if (err) goto det_abt;
+ while (down->last_pos < end - start) {
+ if (download_write(down, start + down->last_pos, end - start - down->last_pos)) goto det_abt;
+ }
+ }
+ if (stat->state != S__OK) {
+ unsigned char *t = get_err_msg(stat->state);
+ if (t) {
+ unsigned char *tt = stracpy(down->url);
+ if (strchr(tt, POST_CHAR)) *strchr(tt, POST_CHAR) = 0;
+ msg_box(get_download_ses(down)->term, getml(tt, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_ERROR_DOWNLOADING), " ", tt, ":\n\n", t, NULL, get_download_ses(down), 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC/*, TEXT_(T_RETRY), NULL, 0 !!! FIXME: retry */);
+ }
+ } else {
+ if (down->prog) {
+ close_download_file(down);
+ exec_on_terminal(get_download_ses(down)->term, down->prog, down->orig_file, !!down->prog_flag_block);
+ mem_free(down->prog), down->prog = NULL;
+ } else if (down->remotetime && download_utime) {
+ struct utimbuf foo;
+ unsigned char *file = stracpy(down->orig_file);
+ unsigned char *wd = get_cwd();
+ set_cwd(down->cwd);
+ foo.actime = foo.modtime = down->remotetime;
+ while (1) {
+ unsigned char *f = translate_download_file(file);
+ EINTRLOOP(rs, utime(f, &foo));
+ mem_free(f);
+ if (!strcmp(file, down->file)) break;
+ increase_download_file(&file);
+ }
+ mem_free(file);
+ if (wd) set_cwd(wd), mem_free(wd);
+ }
+ }
+ abort_download(down);
+ return;
+ }
+ if (down->win) {
+ struct event ev = { EV_REDRAW, 0, 0, 0 };
+ ev.x = down->win->term->x;
+ ev.y = down->win->term->y;
+ down->win->handler(down->win, &ev, 0);
+ }
+}
+
+unsigned char *translate_download_file(unsigned char *fi)
+{
+ unsigned char *file = stracpy("");
+ unsigned char *h;
+ if (fi[0] == '~' && dir_sep(fi[1]) && (h = getenv("HOME"))) {
+ add_to_strn(&file, h);
+ fi++;
+ }
+ add_to_strn(&file, fi);
+ return file;
+}
+
+int create_download_file(struct session *ses, unsigned char *cwd, unsigned char *fi, int mode, off_t siz)
+{
+ unsigned char *wd;
+ unsigned char *file;
+ int h;
+#ifdef NO_FILE_SECURITY
+ int perm = 0666;
+#else
+ int perm = mode & CDF_RESTRICT_PERMISSION ? 0600 : 0666;
+#endif
+ wd = get_cwd();
+ set_cwd(cwd);
+ file = translate_download_file(fi);
+#ifdef HAVE_OPEN_PREALLOC
+ if (siz && !(mode & CDF_NOTRUNC)) {
+ h = open_prealloc(file, O_CREAT | O_NOCTTY | O_WRONLY | O_TRUNC | (mode & CDF_EXCL ? O_EXCL : 0), perm, siz);
+ } else
+#endif
+ {
+ EINTRLOOP(h, open(file, O_CREAT | O_NOCTTY | O_WRONLY | (mode & CDF_NOTRUNC ? 0 : O_TRUNC) | (mode & CDF_EXCL ? O_EXCL : 0), perm));
+ }
+ if (h == -1) {
+ unsigned char *msg, *msge;
+ int errn = errno;
+ if (errn == EEXIST && mode & CDF_NO_POPUP_ON_EEXIST) {
+ h = -2;
+ goto x;
+ }
+ if (!ses) goto x;
+ msg = stracpy(file);
+ msge = stracpy(strerror(errn));
+ msg_box(ses->term, getml(msg, msge, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_COULD_NOT_CREATE_FILE), " ", msg, ": ", msge, NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ goto x;
+ }
+ set_bin(h);
+ x:
+ mem_free(file);
+ if (wd) set_cwd(wd), mem_free(wd);
+ return h;
+}
+
+static int create_or_append_download_file(struct session *ses, unsigned char *cwd, unsigned char *fi, int mode, int *hp, unsigned char **xl_file, off_t *last_pos, off_t *file_shift)
+{
+ int rs;
+ int down_flags = mode == DOWNLOAD_CONTINUE ? CDF_NOTRUNC : mode == DOWNLOAD_OVERWRITE ? 0 : CDF_EXCL;
+
+ *xl_file = stracpy(fi);
+ *last_pos = 0;
+ *file_shift = 0;
+
+ retry_next_file:
+ test_abort_downloads_to_file(*xl_file, ses->term->cwd, 1);
+
+ if ((*hp = create_download_file(ses, ses->term->cwd, *xl_file, down_flags, 0)) < 0)
+ goto err_free;
+
+ if (mode == DOWNLOAD_CONTINUE) {
+ off_t ls;
+ unsigned char *f;
+ struct stat st;
+
+ EINTRLOOP(rs, fstat(*hp, &st));
+ if (rs || !S_ISREG(st.st_mode))
+ goto ret_0;
+
+ EINTRLOOP(ls, lseek(*hp, 0, SEEK_END));
+ if (ls == (off_t)-1) {
+ unsigned char *emsg = stracpy(strerror(errno));
+ unsigned char *msg = stracpy(*xl_file);
+ msg_box(ses->term, getml(msg, emsg, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_ERROR_CALLING_LSEEK_ON_FILE), " ", msg, ": ", emsg, NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ goto err_close;
+ }
+ if ((off_t)(0UL + *last_pos + ls) < 0 ||
+ (off_t)(0UL + *last_pos + ls) < *last_pos) {
+ unsigned char *msg1 = stracpy(fi);
+ unsigned char *msg2 = stracpy(*xl_file);
+ msg_box(ses->term, getml(msg1, msg2, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_TOO_LARGE_FILE_SEQUENCE), " ", msg1, " - ", msg2, NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ goto err_close;
+ }
+ *last_pos += ls;
+
+ f = stracpy(*xl_file);
+ increase_download_file(&f);
+ EINTRLOOP(rs, stat(f, &st));
+ if (rs || !S_ISREG(st.st_mode)) {
+ mem_free(f);
+ goto ret_0;
+ }
+ EINTRLOOP(rs, close(*hp));
+ mem_free(*xl_file);
+ *xl_file = f;
+ *file_shift = *last_pos;
+ goto retry_next_file;
+ }
+
+ ret_0:
+ return 0;
+
+ err_close:
+ EINTRLOOP(rs, close(*hp));
+ err_free:
+ mem_free(*xl_file);
+ return -1;
+}
+
+static void increase_download_file(unsigned char **f)
+{
+ unsigned char *p = NULL, *pp = *f;
+ unsigned char *q;
+ while ((pp = strstr(pp, ".part-"))) p = pp += 6;
+ if (!p || !*p) {
+ no_suffix:
+ add_to_strn(f, ".part-2");
+ return;
+ }
+ for (q = p; *q; q++) if (*q < '0' || *q > '9') goto no_suffix;
+ for (q--; q >= p; q--) {
+ if (*q < '9') {
+ (*q)++;
+ return;
+ }
+ *q = '0';
+ }
+ *p = '1';
+ add_to_strn(f, "0");
+}
+
+static unsigned char *get_temp_name(unsigned char *url, unsigned char *head)
+{
+ int nl;
+ unsigned char *name, *fn, *fnx;
+ unsigned char *nm;
+ unsigned char *directory = NULL;
+#ifdef WIN32
+ directory = getenv("TMP");
+ if (!directory) directory = getenv("TEMP");
+#endif
+ nm = tempnam(directory, "links");
+ if (!nm) return NULL;
+#ifdef OS2
+ if (strlen(nm) > 4 && !strcasecmp(nm + strlen(nm) - 4, ".tmp")) nm[strlen(nm) - 4] = 0;
+#endif
+ name = init_str();
+ nl = 0;
+ add_to_str(&name, &nl, nm);
+ free(nm);
+ fn = get_filename_from_url(url, head, 1);
+#ifndef OS2
+ fnx = strchr(fn, '.');
+#else
+ fnx = strrchr(fn, '.');
+#endif
+ if (fnx) {
+ unsigned char *s;
+#ifdef OS2
+ if (strlen(fnx) > 4) fnx[4] = 0;
+#endif
+ s = stracpy(fnx);
+ check_shell_security(&s);
+ add_to_str(&name, &nl, s);
+ mem_free(s);
+ }
+ mem_free(fn);
+ return name;
+}
+
+unsigned char *subst_file(unsigned char *prog, unsigned char *file, int cyg_subst)
+{
+ unsigned char *orig_prog = prog;
+ unsigned char *nn;
+ unsigned char *n = init_str();
+ int l = 0;
+ while (*prog) {
+ int p;
+ for (p = 0; prog[p] && prog[p] != '%'; p++)
+ ;
+ add_bytes_to_str(&n, &l, prog, p);
+ prog += p;
+ if (*prog == '%') {
+ if (cyg_subst) {
+ unsigned char *conv = os_conv_to_external_path(file, orig_prog);
+ add_to_str(&n, &l, conv);
+ mem_free(conv);
+ } else {
+ add_to_str(&n, &l, file);
+ }
+ prog++;
+ }
+ }
+ nn = os_fixup_external_program(n);
+ mem_free(n);
+ return nn;
+}
+
+void start_download(struct session *ses, unsigned char *file, int mode)
+{
+ struct download *down;
+ int h;
+ unsigned char *url = ses->dn_url;
+ unsigned char *pos;
+ unsigned char *xl_file;
+ off_t last_pos = 0, file_shift = 0;
+
+ if (!url) return;
+ if ((pos = extract_position(url))) mem_free(pos);
+
+ if (create_or_append_download_file(ses, ses->term->cwd, file, mode, &h, &xl_file, &last_pos, &file_shift) < 0) return;
+
+ down = mem_calloc(sizeof(struct download));
+ down->url = stracpy(url);
+ down->stat.end = (void (*)(struct status *, void *))download_data;
+ down->stat.data = down;
+ down->decompress = 0;
+ down->last_pos = last_pos;
+ down->file_shift = file_shift;
+ down->cwd = stracpy(ses->term->cwd);
+ down->orig_file = stracpy(file);
+ down->file = xl_file;
+ down->handle = h;
+ down->ses = ses;
+ down->remotetime = 0;
+ add_to_list(downloads, down);
+ load_url(url, NULL, &down->stat, PRI_DOWNLOAD, NC_CACHE, 1, down->last_pos);
+ display_download(ses->term, down, ses);
+}
+
+void abort_all_downloads(void)
+{
+ while (!list_empty(downloads)) abort_download(downloads.next);
+}
+
+int f_is_finished(struct f_data *f)
+{
+ struct additional_file *af;
+ if (!f || f->rq->state >= 0) return 0;
+ if (f->fd && f->fd->rq && f->fd->rq->state >= 0) return 0;
+ if (f->af) foreach(af, f->af->af) if (!af->rq || af->rq->state >= 0) return 0;
+ return 1;
+}
+
+static int f_is_cacheable(struct f_data *f)
+{
+ if (!f || f->rq->state >= 0) return 0;
+ if (f->fd && f->fd->rq && f->fd->rq->state >= 0) return 0;
+ return 1;
+}
+
+static int f_need_reparse(struct f_data *f)
+{
+ struct additional_file *af;
+ if (!f || f->rq->state >= 0) return 1;
+ if (f->af) foreach(af, f->af->af) if (af->need_reparse > 0) return 1;
+ return 0;
+}
+
+static struct f_data *format_html(struct f_data_c *fd, struct object_request *rq, unsigned char *url, struct document_options *opt, int *cch)
+{
+ struct f_data *f;
+ pr(
+ if (cch) *cch = 0;
+ if (!rq->ce || !(f = init_formatted(opt))) goto nul;
+ f->fd = fd;
+ f->ses = fd->ses;
+ f->time_to_get = -(uttime)get_time();
+ clone_object(rq, &f->rq);
+ if (f->rq->ce) {
+ unsigned char *start; unsigned char *end;
+ int stl = -1;
+ struct additional_file *af;
+
+ if (fd->af) foreach(af, fd->af->af) if (af->need_reparse > 0) af->need_reparse = 0;
+
+ get_file(rq, &start, &end);
+ if (jsint_get_source(fd, &start, &end)) f->uncacheable = 1;
+ if (opt->plain == 2) {
+ start = init_str();
+ stl = 0;
+ add_to_str(&start, &stl, "<img src=\"");
+ add_to_str(&start, &stl, f->rq->ce->url);
+ add_to_str(&start, &stl, "\">");
+ end = start + stl;
+ }
+ really_format_html(f->rq->ce, start, end, f, fd->ses ? fd != fd->ses->screen : 0);
+ if (stl != -1) mem_free(start);
+ f->use_tag = f->rq->ce->count;
+ if (f->af) foreach(af, f->af->af) {
+ if (af->rq && af->rq->ce) {
+ af->use_tag = af->rq->ce->count;
+ af->use_tag2 = af->rq->ce->count2;
+ } else {
+ af->use_tag = 0;
+ af->use_tag2 = 0;
+ }
+ }
+ } else f->use_tag = 0;
+ f->time_to_get += (uttime)get_time();
+ ) nul:return NULL;
+ return f;
+}
+
+static void count_frames(struct f_data_c *fd, int *i)
+{
+ struct f_data_c *sub;
+ if (!fd) return;
+ if (fd->f_data) (*i)++;
+ foreach(sub, fd->subframes) count_frames(sub, i);
+}
+
+unsigned long formatted_info(int type)
+{
+ int i = 0;
+ struct session *ses;
+ struct f_data *ce;
+ switch (type) {
+ case CI_FILES:
+ foreach(ses, sessions)
+ foreach(ce, ses->format_cache) i++;
+ /* fall through */
+ case CI_LOCKED:
+ foreach(ses, sessions)
+ count_frames(ses->screen, &i);
+ return i;
+ default:
+ internal("formatted_info: bad request");
+ }
+ return 0;
+}
+
+static void f_data_attach(struct f_data_c *fd, struct f_data *f)
+{
+ struct additional_file *af;
+ f->rq->upcall = (void (*)(struct object_request *, void *))fd_loaded;
+ f->rq->data = fd;
+ free_additional_files(&fd->af);
+ fd->af = f->af;
+ if (f->af) {
+ f->af->refcount++;
+ foreachback(af, f->af->af) {
+ if (af->rq) {
+ af->rq->upcall = (void (*)(struct object_request *, void *))fd_loaded;
+ af->rq->data = fd;
+ } else {
+ request_object(fd->ses->term, af->url, f->rq->url, PRI_IMG, NC_CACHE, f->rq->upcall, f->rq->data, &af->rq);
+ }
+ }
+ }
+}
+
+static inline int is_format_cache_entry_uptodate(struct f_data *f)
+{
+ struct cache_entry *ce = f->rq->ce;
+ struct additional_file *af;
+ if (!ce || ce->count != f->use_tag) return 0;
+ if (f->af) foreach(af, f->af->af) {
+ struct cache_entry *ce = af->rq ? af->rq->ce : NULL;
+ tcount tag = ce ? ce->count : 0;
+ tcount tag2 = ce ? ce->count2 : 0;
+ if (af->need_reparse > 0) if (tag != af->use_tag) return 0;
+ if (af->unknown_image_size) if (tag2 != af->use_tag2) return 0;
+ }
+ return 1;
+}
+
+static void detach_f_data(struct f_data **ff)
+{
+ struct f_data *f = *ff;
+ struct f_data_c *fd;
+ if (!f) return;
+ fd = f->fd;
+ *ff = NULL;
+#ifdef G
+ f->hlt_pos = -1;
+ f->hlt_len = 0;
+ f->start_highlight_x = -1;
+ f->start_highlight_y = -1;
+#endif
+ if (f->frame_desc_link) {
+ destroy_formatted(f);
+ return;
+ }
+ f->fd = NULL;
+#ifdef G
+ f->locked_on = NULL;
+ free_list(f->image_refresh);
+#endif
+ if (f->uncacheable || !f_is_cacheable(f) || !is_format_cache_entry_uptodate(f) || !f->ses) {
+ destroy_formatted(f);
+ } else {
+ add_to_list(f->ses->format_cache, f);
+ copy_additional_files(&fd->af); /* break structure sharing */
+ }
+}
+
+static int shrink_format_cache(int u)
+{
+ static int sc = 0;
+ int scc;
+ int r = 0;
+ struct f_data *f;
+ int c = 0;
+ struct session *ses;
+ foreach(ses, sessions) foreach(f, ses->format_cache) {
+ if (u == SH_FREE_ALL || !is_format_cache_entry_uptodate(f)) {
+ struct f_data *ff = f;
+ f = f->prev;
+ del_from_list(ff);
+ destroy_formatted(ff);
+ r |= ST_SOMETHING_FREED;
+ } else c++;
+ }
+ if (c > max_format_cache_entries || (c && u == SH_FREE_SOMETHING)) {
+ int sc_cycle = 0;
+ unsigned char freed_in_cycle = 0;
+ a:
+ scc = sc++;
+ foreach (ses, sessions) if (!scc--) {
+ foreachback(f, ses->format_cache) {
+ struct f_data *ff = f;
+ f = f->next;
+ del_from_list(ff);
+ destroy_formatted(ff);
+ r |= ST_SOMETHING_FREED;
+ if (--c <= max_format_cache_entries ||
+ u == SH_FREE_SOMETHING) goto ret;
+ freed_in_cycle = 1;
+ goto a;
+ }
+ goto a;
+ }
+ sc = 0;
+ sc_cycle++;
+ if (sc_cycle >= 2 && !freed_in_cycle)
+ goto ret;
+ freed_in_cycle = 0;
+ goto a;
+ }
+ ret:
+ return r | (!c ? ST_CACHE_EMPTY : 0);
+}
+
+void init_fcache(void)
+{
+ register_cache_upcall(shrink_format_cache, "format");
+}
+
+static void calculate_scrollbars(struct f_data_c *fd, struct f_data *f)
+{
+ fd->hsb = 0, fd->vsb = 0;
+ fd->hsbsize = 0, fd->vsbsize = 0;
+ if (!f)
+ return;
+ if (f->opt.scrolling == SCROLLING_YES) {
+ fd->hsb = 1, fd->vsb = 1;
+ } else if (f->opt.scrolling == SCROLLING_AUTO) {
+ x:
+ if (!fd->hsb && f->x > fd->xw - fd->vsb * G_SCROLL_BAR_WIDTH) {
+ fd->hsb = 1;
+ goto x;
+ }
+ if (!fd->vsb && f->y > fd->yw - fd->hsb * G_SCROLL_BAR_WIDTH) {
+ fd->vsb = 1;
+ goto x;
+ }
+ }
+ if (fd->hsb) fd->hsbsize = fd->xw - fd->vsb * G_SCROLL_BAR_WIDTH;
+ if (fd->vsb) fd->vsbsize = fd->yw - fd->hsb * G_SCROLL_BAR_WIDTH;
+ if (fd->hsbsize < 0) fd->hsb = 0;
+ if (fd->vsbsize < 0) fd->vsb = 0;
+}
+
+struct f_data *cached_format_html(struct f_data_c *fd, struct object_request *rq, unsigned char *url, struct document_options *opt, int *cch)
+{
+ struct f_data *f;
+ /*if (F) opt->tables = 0;*/
+ if (fd->marginwidth != -1) {
+ int marg = (fd->marginwidth + G_HTML_MARGIN - 1) / G_HTML_MARGIN;
+ if (marg >= 0 && marg < 9) opt->margin = marg;
+ }
+ if (opt->plain == 2) opt->margin = 0, opt->display_images = 1;
+ pr(
+ if (!jsint_get_source(fd, NULL, NULL) && fd->ses) {
+ if (fd->f_data && !strcmp(fd->f_data->rq->url, url) && !compare_opt(&fd->f_data->opt, opt) && is_format_cache_entry_uptodate(fd->f_data)) {
+ f = fd->f_data;
+ xpr();
+ goto ret_f;
+ }
+ foreach(f, fd->ses->format_cache) {
+ if (!strcmp(f->rq->url, url) && !compare_opt(&f->opt, opt)) {
+ if (!is_format_cache_entry_uptodate(f)) {
+ struct f_data *ff = f;
+ f = f->prev;
+ del_from_list(ff);
+ destroy_formatted(ff);
+ continue;
+ }
+ detach_f_data(&fd->f_data);
+ del_from_list(f);
+ f->fd = fd;
+ if (cch) *cch = 1;
+ f_data_attach(fd, f);
+ xpr();
+ goto ret_f;
+ }
+ }
+ }) {};
+ detach_f_data(&fd->f_data);
+ f = format_html(fd, rq, url, opt, cch);
+ if (f) f->fd = fd;
+ shrink_memory(SH_CHECK_QUOTA);
+ret_f:
+ calculate_scrollbars(fd, f);
+ return f;
+}
+
+static void create_new_frames(struct f_data_c *fd, struct frameset_desc *fs, struct document_options *o)
+{
+ struct location *loc;
+ struct frame_desc *frm;
+ int c_loc;
+ int i;
+ int x, y;
+ int xp, yp;
+
+ i = 0;
+#ifdef JS
+ if (fd->onload_frameset_code)mem_free(fd->onload_frameset_code);
+ fd->onload_frameset_code=stracpy(fs->onload_code);
+#endif
+ foreach(loc, fd->loc->subframes) i++;
+ if (i != fs->n) {
+ while (!list_empty(fd->loc->subframes)) destroy_location(fd->loc->subframes.next);
+ c_loc = 1;
+ } else {
+ c_loc = 0;
+ loc = fd->loc->subframes.next;
+ }
+
+ yp = fd->yp;
+ frm = &fs->f[0];
+ for (y = 0; y < fs->y; y++) {
+ xp = fd->xp;
+ for (x = 0; x < fs->x; x++) {
+ struct f_data_c *nfdc;
+ if (!(nfdc = create_f_data_c(fd->ses, fd))) return;
+ if (c_loc) {
+ struct list_head *l = (struct list_head *)fd->loc->subframes.prev;
+ loc = new_location();
+ add_to_list(*l, loc);
+ loc->parent = fd->loc;
+ loc->name = stracpy(frm->name);
+ if ((loc->url = stracpy(frm->url)))
+ nfdc->goto_position = extract_position(loc->url);
+ }
+ nfdc->xp = xp; nfdc->yp = yp;
+ nfdc->xw = frm->xw;
+ nfdc->yw = frm->yw;
+ nfdc->scrolling = frm->scrolling;
+ nfdc->loc = loc;
+ nfdc->vs = loc->vs;
+ if (frm->marginwidth != -1) nfdc->marginwidth = frm->marginwidth;
+ else nfdc->marginwidth = fd->marginwidth;
+ if (frm->marginheight != -1) nfdc->marginheight = frm->marginheight;
+ else nfdc->marginheight = fd->marginheight;
+ /*debug("frame: %d %d, %d %d\n", nfdc->xp, nfdc->yp, nfdc->xw, nfdc->yw);*/
+ {
+ struct list_head *l = (struct list_head *)fd->subframes.prev;
+ add_to_list(*l, nfdc);
+ }
+ if (frm->subframe) {
+ create_new_frames(nfdc, frm->subframe, o);
+ /*nfdc->f_data = init_formatted(&fd->f_data->opt);*/
+ nfdc->f_data = init_formatted(o);
+ nfdc->f_data->frame_desc = copy_frameset_desc(frm->subframe);
+ nfdc->f_data->frame_desc_link = 1;
+ } else {
+ if (fd->depth < HTML_MAX_FRAME_DEPTH && loc->url && *loc->url)
+ request_object(fd->ses->term, loc->url, fd->loc->url, PRI_FRAME, NC_CACHE, (void (*)(struct object_request *, void *))fd_loaded, nfdc, &nfdc->rq);
+ }
+ xp += frm->xw + gf_val(1, 0);
+ frm++;
+ if (!c_loc) loc = loc->next;
+ }
+ yp += (frm - 1)->yw + gf_val(1, 0);
+ }
+}
+
+static void html_interpret(struct f_data_c *fd)
+{
+ int i;
+ int oxw; int oyw; int oxp; int oyp;
+ struct f_data_c *sf;
+ int cch;
+ /*int first = !fd->f_data;*/
+ struct document_options o;
+ struct js_event_spec *doc_js;
+ struct js_event_spec **link_js;
+ int nlink_js;
+ if (!fd->loc) goto d;
+ if (fd->f_data) {
+ oxw = fd->f_data->opt.xw;
+ oyw = fd->f_data->opt.yw;
+ oxp = fd->f_data->opt.xp;
+ oyp = fd->f_data->opt.yp;
+ } else {
+ oxw = oyw = oxp = oyp = -1;
+ }
+ memset(&o, 0, sizeof(struct document_options));
+ ds2do(&fd->ses->ds, &o, F || fd->ses->term->spec->col);
+ if (fd->parent && fd->parent->f_data && !o.hard_assume) {
+ o.assume_cp = fd->parent->f_data->cp;
+ }
+#ifdef JS
+ o.js_enable = js_enable;
+#else
+ o.js_enable=0;
+#endif
+#ifdef G
+ o.aspect_on=aspect_on;
+ o.bfu_aspect=bfu_aspect;
+#else
+ o.aspect_on=0;
+ o.bfu_aspect=0;
+#endif
+ o.plain = fd->vs->plain;
+ if (o.plain == 1) {
+ o.xp = 0;
+ o.yp = 0;
+ o.xw = MAXINT;
+ o.yw = MAXINT;
+ } else {
+ o.xp = fd->xp;
+ o.yp = fd->yp;
+ o.xw = fd->xw;
+ o.yw = fd->yw;
+ }
+ o.scrolling = fd->scrolling;
+ if (fd->ses->term->spec) {
+ if (!F) {
+ if (!fd->ses->ds.t_ignore_document_color)
+ o.col = fd->ses->term->spec->col;
+ else
+ o.col = 0;
+ }
+#ifdef G
+ else {
+ if (!fd->ses->ds.g_ignore_document_color)
+ o.col = 2;
+ else
+ o.col = 0;
+ }
+#endif
+ o.cp = fd->ses->term->spec->charset;
+ o.braille = fd->ses->term->spec->braille;
+ } else {
+ o.col = 3;
+ o.cp = 0;
+ o.braille = 0;
+ }
+ if (!(o.framename = fd->loc->name)) o.framename = NULL;
+ doc_js = NULL;
+ link_js = DUMMY;
+ nlink_js = 0;
+ if (fd->f_data) {
+ copy_js_event_spec(&doc_js, fd->f_data->js_event);
+ if (fd->f_data->nlinks > fd->f_data->nlink_events) nlink_js = fd->f_data->nlinks; else nlink_js = fd->f_data->nlink_events;
+ if ((unsigned)nlink_js > MAXINT / sizeof(struct js_event_spec *)) overalloc();
+ link_js = mem_alloc(nlink_js * sizeof(struct js_event_spec *));
+ for (i = 0; i < fd->f_data->nlinks; i++) copy_js_event_spec(&link_js[i], fd->f_data->links[i].js_event);
+ for (; i < fd->f_data->nlink_events; i++) copy_js_event_spec(&link_js[i], fd->f_data->link_events[i]);
+ }
+ if (!(fd->f_data = cached_format_html(fd, fd->rq, fd->rq->url, &o, &cch))) {
+ for (i = 0; i < nlink_js; i++) free_js_event_spec(link_js[i]);
+ mem_free(link_js);
+ free_js_event_spec(doc_js);
+ goto d;
+ }
+ if (join_js_event_spec(&fd->f_data->js_event, doc_js)) fd->f_data->uncacheable = 1;
+ for (i = 0; i < fd->f_data->nlink_events; i++) free_js_event_spec(fd->f_data->link_events[i]);
+ mem_free(fd->f_data->link_events);
+ fd->f_data->link_events = link_js;
+ fd->f_data->nlink_events = nlink_js;
+ for (i = 0; i < fd->f_data->nlinks && i < nlink_js; i++) if (join_js_event_spec(&fd->f_data->links[i].js_event, link_js[i])) fd->f_data->uncacheable = 1;
+ free_js_event_spec(doc_js);
+
+ /* erase frames if changed */
+ i = 0;
+ foreach(sf, fd->subframes) i++;
+ if (i != (fd->f_data->frame_desc ? fd->f_data->frame_desc->n : 0) && (f_is_finished(fd->f_data) || !f_need_reparse(fd->f_data))) {
+ rd:
+ foreach(sf, fd->subframes) reinit_f_data_c(sf);
+ free_list(fd->subframes);
+
+ /* create new frames */
+ if (fd->f_data->frame_desc) create_new_frames(fd, fd->f_data->frame_desc, &fd->f_data->opt);
+ } else {
+ if (fd->f_data->frame_desc && fd->f_data->rq->state < 0) {
+ if (fd->f_data->opt.xw != oxw ||
+ fd->f_data->opt.yw != oyw ||
+ fd->f_data->opt.xp != oxp ||
+ fd->f_data->opt.yp != oyp) goto rd;
+ }
+ }
+
+ d:;
+ /*draw_fd(fd);*/
+}
+
+void html_interpret_recursive(struct f_data_c *f)
+{
+ struct f_data_c *fd;
+ if (f->rq) html_interpret(f);
+ foreach(fd, f->subframes) html_interpret_recursive(fd);
+}
+
+/* You get a struct_additionl_file. never mem_free it. When you stop
+ * using it, just forget the pointer.
+ */
+struct additional_file *request_additional_file(struct f_data *f, unsigned char *url_)
+{
+ struct additional_file *af;
+ unsigned char *u, *url;
+ url = stracpy(url_);
+ if ((u = extract_position(url))) mem_free(u);
+ if (!f->af) {
+ if (!(f->af = f->fd->af)) {
+ f->af = f->fd->af = mem_alloc(sizeof(struct additional_files));
+ f->af->refcount = 1;
+ init_list(f->af->af);
+ }
+ f->af->refcount++;
+ }
+ foreach(af, f->af->af) if (!strcmp(af->url, url)) {
+ mem_free(url);
+ return af;
+ }
+ af = mem_alloc(sizeof(struct additional_file) + strlen(url));
+ af->use_tag = 0;
+ af->use_tag2 = 0;
+ strcpy(af->url, url);
+ request_object(f->ses->term, url, f->rq->url, PRI_IMG, NC_CACHE, f->rq->upcall, f->rq->data, &af->rq);
+ af->need_reparse = 0;
+ af->unknown_image_size = 0;
+ add_to_list(f->af->af, af);
+ mem_free(url);
+ return af;
+}
+
+static void copy_additional_files(struct additional_files **a)
+{
+ struct additional_files *afs;
+ struct additional_file *af;
+ if (!*a || (*a)->refcount == 1) return;
+ (*a)->refcount--;
+ afs = mem_alloc(sizeof(struct additional_files));
+ afs->refcount = 1;
+ init_list(afs->af);
+ foreachback(af, (*a)->af) {
+ struct additional_file *afc = mem_alloc(sizeof(struct additional_file) + strlen(af->url));
+ memcpy(afc, af, sizeof(struct additional_file) + strlen(af->url));
+ if (af->rq) clone_object(af->rq, &afc->rq);
+ add_to_list(afs->af, afc);
+ }
+ *a = afs;
+}
+
+#ifdef G
+
+static void image_timer(struct f_data_c *fd)
+{
+ struct image_refresh *ir;
+ struct list_head new;
+ struct list_head *newp = do_not_optimize_here(&new);
+ init_list(*newp);
+ fd->image_timer = -1;
+ if (!fd->f_data) return;
+ foreach (ir, fd->f_data->image_refresh) {
+ if (ir->t > G_IMG_REFRESH) ir->t -= G_IMG_REFRESH;
+ else {
+ struct image_refresh *irr = ir->prev;
+ del_from_list(ir);
+ add_to_list(*newp, ir);
+ ir = irr;
+ }
+ }
+ foreach (ir, *newp) {
+ /*fprintf(stderr, "DRAW: %p\n", ir->img);*/
+ draw_one_object(fd, ir->img);
+ }
+ free_list(*newp);
+ if (fd->image_timer == -1 && !list_empty(fd->f_data->image_refresh)) fd->image_timer = install_timer(G_IMG_REFRESH, (void (*)(void *))image_timer, fd);
+}
+
+void refresh_image(struct f_data_c *fd, struct g_object *img, ttime tm)
+{
+ struct image_refresh *ir;
+ if (!fd->f_data) return;
+ foreach (ir, fd->f_data->image_refresh) if (ir->img == img) {
+ if (ir->t > tm) ir->t = tm;
+ return;
+ }
+ ir = mem_alloc(sizeof(struct image_refresh));
+ ir->img = img;
+ ir->t = tm;
+ add_to_list(fd->f_data->image_refresh, ir);
+ if (fd->image_timer == -1) fd->image_timer = install_timer(G_IMG_REFRESH, (void (*)(void *))image_timer, fd);
+}
+
+#endif
+
+void reinit_f_data_c(struct f_data_c *fd)
+{
+ struct additional_file *af;
+ struct f_data_c *fd1;
+#ifdef G
+ if (F)
+ if (fd == current_frame(fd->ses))
+ fd->ses->locked_link = 0;
+#endif
+ jsint_destroy(fd);
+ foreach(fd1, fd->subframes) {
+ if (fd->ses->wtd_target_base == fd1) fd->ses->wtd_target_base = NULL;
+ reinit_f_data_c(fd1);
+ if (fd->ses->wtd_target_base == fd1) fd->ses->wtd_target_base = fd;
+#ifdef JS
+ if (fd->ses->defered_target_base == fd1) fd->ses->defered_target_base = fd;
+#endif
+ }
+ free_list(fd->subframes);
+#ifdef JS
+ if (fd->onload_frameset_code)mem_free(fd->onload_frameset_code),fd->onload_frameset_code=NULL;
+#endif
+ fd->loc = NULL;
+ if (fd->f_data && fd->f_data->rq) fd->f_data->rq->upcall = NULL;
+ if (fd->f_data && fd->f_data->af) foreach(af, fd->f_data->af->af) if (af->rq) {
+ af->rq->upcall = NULL;
+ if (af->rq->state != O_OK) release_object(&af->rq);
+ }
+ if (fd->af) foreach(af, fd->af->af) if (af->rq) af->rq->upcall = NULL;
+ free_additional_files(&fd->af);
+ detach_f_data(&fd->f_data);
+ release_object(&fd->rq);
+ if (fd->link_bg) mem_free(fd->link_bg), fd->link_bg = NULL;
+ fd->link_bg_n = 0;
+ if (fd->goto_position) mem_free(fd->goto_position), fd->goto_position = NULL;
+ if (fd->went_to_position) mem_free(fd->went_to_position), fd->went_to_position = NULL;
+ fd->next_update = get_time();
+ fd->done = 0;
+ fd->parsed_done = 0;
+ if (fd->image_timer != -1) kill_timer(fd->image_timer), fd->image_timer = -1;
+ if (fd->refresh_timer != -1) kill_timer(fd->refresh_timer), fd->refresh_timer = -1;
+}
+
+struct f_data_c *create_f_data_c(struct session *ses, struct f_data_c *parent)
+{
+ static long id = 1;
+ struct f_data_c *fd;
+ fd = mem_calloc(sizeof(struct f_data_c));
+ fd->parent = parent;
+ fd->ses = ses;
+ fd->depth = parent ? parent->depth + 1 : 1;
+ init_list(fd->subframes);
+ fd->next_update = get_time();
+ fd->done = 0;
+ fd->parsed_done = 0;
+ fd->script_t = 0;
+ fd->id = id++;
+ fd->marginwidth = fd->marginheight = -1;
+ fd->image_timer = -1;
+ fd->refresh_timer = -1;
+ fd->scrolling = SCROLLING_AUTO;
+ return fd;
+}
+
+static int plain_type(struct session *ses, struct object_request *rq, unsigned char **p)
+{
+ struct cache_entry *ce;
+ unsigned char *ct;
+ int r = 0;
+ if (p) *p = NULL;
+ if (!rq || !(ce = rq->ce)) {
+ r = 1;
+ goto f;
+ }
+ if (!(ct = get_content_type(ce->head, ce->url))) goto f;
+ if (is_html_type(ct)) goto ff;
+ r = 1;
+ if (!strcasecmp(ct, "text/plain") || !strcasecmp(ct, "file/txt")) goto ff;
+ r = 2;
+ if (F && known_image_type(ct)) goto ff;
+ r = -1;
+
+ ff:
+ if (!p) mem_free(ct);
+ else *p = ct;
+ f:
+ return r;
+}
+
+static void refresh_timer(struct f_data_c *fd)
+{
+ if (fd->ses->rq) {
+ fd->refresh_timer = install_timer(500, (void (*)(void *))refresh_timer, fd);
+ return;
+ }
+ fd->refresh_timer = -1;
+ if (fd->f_data && fd->f_data->refresh) {
+ fd->refresh_timer = install_timer(fd->f_data->refresh_seconds * 1000, (void (*)(void *))refresh_timer, fd);
+ goto_url_f(fd->ses, NULL, fd->f_data->refresh, "_self", fd, -1, 0, 0, 1);
+ }
+}
+
+#ifdef JS
+static int frame_and_all_subframes_loaded(struct f_data_c *fd)
+{
+ struct f_data_c *f;
+ int loaded=fd->done||fd->rq==NULL;
+
+ if (loaded) /* this frame is loaded */
+ foreach(f,fd->subframes)
+ {
+ loaded=frame_and_all_subframes_loaded(f);
+ if (!loaded)break;
+ }
+ return loaded;
+}
+#endif
+
+void fd_loaded(struct object_request *rq, struct f_data_c *fd)
+{
+ int first = !fd->f_data;
+ if (fd->done) {
+ if (f_is_finished(fd->f_data)) goto priint;
+ else fd->done = 0, fd->parsed_done = 1;
+ }
+ if (fd->parsed_done && f_need_reparse(fd->f_data)) fd->parsed_done = 0;
+ if (fd->vs->plain == -1 && rq->state != O_WAITING) {
+ fd->vs->plain = plain_type(fd->ses, fd->rq, NULL);
+ }
+ if (fd->rq->state < 0 && (f_is_finished(fd->f_data) || !fd->f_data)) {
+ if (!fd->parsed_done) {
+ html_interpret(fd);
+ if (fd->went_to_position) {
+ if (!fd->goto_position) fd->goto_position = fd->went_to_position, fd->went_to_position = NULL;
+ else mem_free(fd->went_to_position), fd->went_to_position = NULL;
+ }
+ }
+ draw_fd(fd);
+ /* it may happen that html_interpret requests load of additional file */
+ if (!f_is_finished(fd->f_data)) goto more_data;
+fn:
+ if (fd->f_data->are_there_scripts) {
+ jsint_scan_script_tags(fd);
+ if (!f_is_finished(fd->f_data)) goto more_data;
+ }
+ fd->done = 1;
+ fd->parsed_done = 0;
+ if (fd->f_data->refresh) {
+ if (fd->refresh_timer != -1) kill_timer(fd->refresh_timer);
+ fd->refresh_timer = install_timer((uttime)fd->f_data->refresh_seconds * 1000, (void (*)(void *))refresh_timer, fd);
+ }
+#ifdef JS
+ jsint_run_queue(fd);
+#endif
+ } else if ((ttime)((uttime)get_time() - (uttime)fd->next_update) >= 0 || (rq == fd->rq && rq->state < 0)) {
+ ttime t;
+ if (!fd->parsed_done) {
+ html_interpret(fd);
+ if (fd->rq->state < 0 && !f_need_reparse(fd->f_data)) {
+ if (fd->went_to_position) {
+ if (!fd->goto_position) fd->goto_position = fd->went_to_position, fd->went_to_position = NULL;
+ else mem_free(fd->went_to_position), fd->went_to_position = NULL;
+ }
+ fd->parsed_done = 1;
+ }
+ }
+ draw_fd(fd);
+ if (fd->rq->state < 0 && f_is_finished(fd->f_data)) goto fn;
+more_data:
+ t = fd->f_data ? ((uttime)(fd->parsed_done ? 0 : fd->f_data->time_to_get * DISPLAY_TIME) + (uttime)fd->f_data->time_to_draw * IMG_DISPLAY_TIME) : 0;
+ if (t < DISPLAY_TIME_MIN) t = DISPLAY_TIME_MIN;
+ if (first && t > DISPLAY_TIME_MAX_FIRST) t = DISPLAY_TIME_MAX_FIRST;
+ fd->next_update = (uttime)get_time() + t;
+ } else {
+ change_screen_status(fd->ses);
+ print_screen_status(fd->ses);
+ }
+priint:
+ /* process onload handler of a frameset */
+#ifdef JS
+ {
+ int all_loaded;
+
+ /* go to parent and test if all subframes are loaded, if yes, call onload handler */
+
+ if (!fd->parent) goto hell; /* this frame has no parent, skip */
+ if (!fd->parent->onload_frameset_code)goto hell; /* no onload handler, skip all this */
+ all_loaded=frame_and_all_subframes_loaded(fd->parent);
+ if (!all_loaded) goto hell;
+ /* parent has all subframes loaded */
+ jsint_execute_code(fd->parent,fd->parent->onload_frameset_code,strlen(fd->parent->onload_frameset_code),-1,-1,-1, NULL);
+ mem_free(fd->parent->onload_frameset_code), fd->parent->onload_frameset_code=NULL;
+ hell:;
+ }
+#endif
+ if (rq && (rq->state == O_FAILED || rq->state == O_INCOMPLETE) && (fd->rq == rq || fd->ses->rq == rq)) print_error_dialog(fd->ses, &rq->stat, rq->url);
+#ifdef LINKS_TESTMODE_DOCUMENT_AUTO_EXIT
+ if (f_is_finished(fd->f_data))
+ terminate_loop = 1;
+#endif
+}
+
+static unsigned location_id = 0;
+
+static struct location *new_location(void)
+{
+ struct location *loc;
+ loc = mem_calloc(sizeof(struct location));
+ loc->location_id = ++location_id;
+ init_list(loc->subframes);
+ loc->vs = create_vs();
+ return loc;
+}
+
+static struct location *alloc_ses_location(struct session *ses)
+{
+ struct location *loc;
+ if (!(loc = new_location())) return NULL;
+ add_to_list(ses->history, loc);
+ return loc;
+}
+
+static void subst_location(struct f_data_c *fd, struct location *old, struct location *new)
+{
+ struct f_data_c *f;
+ foreach(f, fd->subframes) subst_location(f, old, new);
+ if (fd->loc == old) fd->loc = new;
+}
+
+static struct location *copy_sublocations(struct session *ses, struct location *d, struct location *s, struct location *x)
+{
+ struct location *dl, *sl, *y, *z;
+ d->name = stracpy(s->name);
+ if (s == x) return d;
+ d->url = stracpy(s->url);
+ d->prev_url = stracpy(s->prev_url);
+ destroy_vs(d->vs);
+ d->vs = s->vs; s->vs->refcount++;
+ subst_location(ses->screen, s, d);
+ y = NULL;
+ foreach(sl, s->subframes) {
+ if ((dl = new_location())) {
+ struct list_head *l = (struct list_head *)d->subframes.prev;
+ add_to_list(*l, dl);
+ dl->parent = d;
+ z = copy_sublocations(ses, dl, sl, x);
+ if (z && y) internal("copy_sublocations: crossed references");
+ if (z) y = z;
+ }
+ }
+ return y;
+}
+
+static struct location *copy_location(struct session *ses, struct location *loc)
+{
+ struct location *l2, *l1, *nl;
+ l1 = cur_loc(ses);
+ if (!(l2 = alloc_ses_location(ses))) return NULL;
+ if (!(nl = copy_sublocations(ses, l2, l1, loc))) internal("copy_location: sublocation not found");
+ return nl;
+}
+
+static struct f_data_c *new_main_location(struct session *ses)
+{
+ struct location *loc;
+ if (!(loc = alloc_ses_location(ses))) return NULL;
+ reinit_f_data_c(ses->screen);
+ ses->screen->loc = loc;
+ ses->screen->vs = loc->vs;
+ if (ses->wanted_framename) loc->name=ses->wanted_framename, ses->wanted_framename=NULL;
+ return ses->screen;
+}
+
+static struct f_data_c *copy_location_and_replace_frame(struct session *ses, struct f_data_c *fd)
+{
+ struct location *loc;
+ if (!(loc = copy_location(ses, fd->loc))) return NULL;
+ reinit_f_data_c(fd);
+ fd->loc = loc;
+ fd->vs = loc->vs;
+ return fd;
+}
+
+/* vrati frame prislusici danemu targetu
+ pokud takovy frame nenajde, vraci NULL
+ */
+struct f_data_c *find_frame(struct session *ses, unsigned char *target, struct f_data_c *base)
+{
+ struct f_data_c *f, *ff;
+ if (!base) base = ses->screen;
+ if (!target || !*target) return base;
+ if (!strcasecmp(target, "_blank"))
+ return NULL; /* open in new window */
+ if (!strcasecmp(target, "_top"))
+ return ses->screen;
+ if (!strcasecmp(target, "_self")) return base;
+ if (!strcasecmp(target, "_parent")) {
+ for (ff = base->parent; ff && !ff->rq; ff = ff->parent)
+ ;
+ return ff ? ff : ses->screen;
+ }
+ f = ses->screen;
+ if (f->loc && f->loc->name && !strcasecmp(f->loc->name, target)) return f;
+ d:
+ foreach(ff, f->subframes) if (ff->loc && ff->loc->name && !strcasecmp(ff->loc->name, target)) return ff;
+ if (!list_empty(f->subframes)) {
+ f = f->subframes.next;
+ goto d;
+ }
+ u:
+ if (!f->parent) return NULL;
+ if (f->next == (void *)&f->parent->subframes) {
+ f = f->parent;
+ goto u;
+ }
+ f = f->next;
+ goto d;
+}
+
+static void destroy_location(struct location *loc)
+{
+ while (loc->subframes.next != &loc->subframes) destroy_location(loc->subframes.next);
+ del_from_list(loc);
+ if (loc->name) mem_free(loc->name);
+ if (loc->url) mem_free(loc->url);
+ if (loc->prev_url) mem_free(loc->prev_url);
+ destroy_vs(loc->vs);
+ mem_free(loc);
+}
+
+static void clear_forward_history(struct session *ses)
+{
+ while (!list_empty(ses->forward_history)) destroy_location(ses->forward_history.next);
+}
+
+static void ses_go_forward(struct session *ses, int plain, int refresh)
+{
+ struct location *cl;
+ struct f_data_c *fd;
+ clear_forward_history(ses);
+ if (ses->search_word) mem_free(ses->search_word), ses->search_word = NULL;
+ if (ses->default_status){mem_free(ses->default_status);ses->default_status=NULL;} /* smazeme default status, aby neopruzoval na jinych strankach */
+ if ((fd = find_frame(ses, ses->wtd_target, ses->wtd_target_base))&&fd!=ses->screen) {
+ cl = NULL;
+ if (refresh && fd->loc && !strcmp(fd->loc->url, ses->rq->url)) cl = cur_loc(ses);
+ fd = copy_location_and_replace_frame(ses, fd);
+ if (cl) destroy_location(cl);
+ } else fd = new_main_location(ses);
+ if (!fd) return;
+ fd->vs->plain = plain;
+ ses->wtd = NULL;
+ fd->rq = ses->rq; ses->rq = NULL;
+ fd->goto_position = ses->goto_position; ses->goto_position = NULL;
+ fd->loc->url = stracpy(fd->rq->url);
+ fd->loc->prev_url = stracpy(fd->rq->prev_url);
+ fd->rq->upcall = (void (*)(struct object_request *, void *))fd_loaded;
+ fd->rq->data = fd;
+ fd->rq->upcall(fd->rq, fd);
+ draw_formatted(ses);
+}
+
+static void ses_go_backward(struct session *ses)
+{
+ int n;
+ struct location *loc;
+ if (ses->search_word) mem_free(ses->search_word), ses->search_word = NULL;
+ if (ses->default_status){mem_free(ses->default_status);ses->default_status=NULL;} /* smazeme default status, aby neopruzoval na jinych strankach */
+ reinit_f_data_c(ses->screen);
+ if (!ses->wtd_num_steps) internal("ses_go_backward: wtd_num_steps is zero");
+ if (ses->wtd_num_steps > 0) {
+ n = ses->wtd_num_steps;
+ foreach(loc, ses->history) {
+ if (!n--) goto have_back_loc;
+ }
+ internal("ses_go_backward: session history disappeared");
+ return;
+ have_back_loc:
+ for (n = 0; n < ses->wtd_num_steps; n++) {
+ loc = cur_loc(ses);
+ del_from_list(loc);
+ add_to_list(ses->forward_history, loc);
+ }
+ } else {
+ n = ses->wtd_num_steps;
+ foreach(loc, ses->forward_history) {
+ if (!++n) goto have_fwd_loc;
+ }
+ internal("ses_go_backward: session forward history disappeared");
+ return;
+ have_fwd_loc:
+ for (n = 0; n < -ses->wtd_num_steps; n++) {
+ loc = (struct location *)ses->forward_history.next;
+ del_from_list(loc);
+ add_to_list(ses->history, loc);
+ }
+ }
+#ifdef G
+ ses->locked_link = 0;
+#endif
+ ses->screen->loc = cur_loc(ses);
+ ses->screen->vs = ses->screen->loc->vs;
+ ses->wtd = NULL;
+ ses->screen->rq = ses->rq; ses->rq = NULL;
+ ses->screen->rq->upcall = (void (*)(struct object_request *, void *))fd_loaded;
+ ses->screen->rq->data = ses->screen;
+ ses->screen->rq->upcall(ses->screen->rq, ses->screen);
+
+}
+
+static void tp_cancel(struct session *ses)
+{
+ release_object(&ses->tq);
+}
+
+static void continue_download(struct session *ses, unsigned char *file, int mode)
+{
+ unsigned char *enc;
+ struct download *down;
+ int h;
+ int namecount = 0;
+ unsigned char *url = ses->tq->url;
+ unsigned char *xl_file;
+ off_t last_pos = 0, file_shift = 0;
+
+ if (ses->tq_prog) {
+ if (ses->tq_prog_flag_direct && ses->tq->state != O_OK && !strchr(url, POST_CHAR) && !check_shell_url(url)) {
+ unsigned char *prog = subst_file(ses->tq_prog, url, 0);
+ exec_on_terminal(ses->term, prog, "", !!ses->tq_prog_flag_block);
+ mem_free(prog);
+ tp_cancel(ses);
+ abort_background_connections();
+ return;
+ }
+ new_name:
+ if (!(file = get_temp_name(url, ses->tq->ce ? ses->tq->ce->head : NULL))) {
+ tp_cancel(ses);
+ return;
+ }
+ if ((h = create_download_file(ses, ses->term->cwd, file, CDF_RESTRICT_PERMISSION | CDF_EXCL | CDF_NO_POPUP_ON_EEXIST, 0)) < 0) {
+ if (h == -2 && ses->tq_prog) {
+ if (++namecount < DOWNLOAD_NAME_TRIES) {
+ mem_free(file);
+ goto new_name;
+ }
+ msg_box(ses->term, NULL, TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_COULD_NOT_CREATE_TEMPORARY_FILE), NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ }
+ mem_free(file);
+ tp_cancel(ses);
+ return;
+ }
+ xl_file = stracpy(file);
+ } else {
+ if (create_or_append_download_file(ses, ses->term->cwd, file, mode, &h, &xl_file, &last_pos, &file_shift)) {
+ tp_cancel(ses);
+ return;
+ }
+ }
+ down = mem_calloc(sizeof(struct download));
+ down->url = stracpy(url);
+ down->stat.end = (void (*)(struct status *, void *))download_data;
+ down->stat.data = down;
+ down->decompress = 0;
+ down->last_pos = last_pos;
+ down->file_shift = file_shift;
+ down->cwd = stracpy(ses->term->cwd);
+ down->orig_file = stracpy(file);
+ down->file = xl_file;
+ down->handle = h;
+ down->ses = ses;
+ down->remotetime = 0;
+ if (ses->tq_prog) {
+ down->prog = subst_file(ses->tq_prog, file, 1);
+ mem_free(file);
+ mem_free(ses->tq_prog);
+ ses->tq_prog = NULL;
+ }
+ down->prog_flag_block = ses->tq_prog_flag_block;
+
+ enc = ses->tq->ce ? get_content_encoding(ses->tq->ce->head, ses->tq->ce->url) : NULL;
+ if (enc) {
+ if (encoding_2_extension(enc)) {
+ if (down->prog) down->decompress = 1;
+ } else {
+ down->decompress = 1;
+ }
+ mem_free(enc);
+ }
+
+ add_to_list(downloads, down);
+ release_object_get_stat(&ses->tq, &down->stat, PRI_DOWNLOAD);
+ display_download(ses->term, down, ses);
+}
+
+
+static void tp_save(struct session *ses)
+{
+ if (ses->tq_prog) mem_free(ses->tq_prog), ses->tq_prog = NULL;
+ query_file(ses, ses->tq->url, ses->tq->ce ? ses->tq->ce->head : NULL, &continue_download, tp_cancel, DOWNLOAD_CONTINUE);
+}
+
+static void tp_open(struct session *ses)
+{
+ continue_download(ses, "", DOWNLOAD_DEFAULT);
+}
+
+static int ses_abort_1st_state_loading(struct session *ses)
+{
+ int r = !!ses->rq;
+ release_object(&ses->rq);
+ ses->wtd = NULL;
+ if (ses->wtd_target) mem_free(ses->wtd_target), ses->wtd_target = NULL;
+ ses->wtd_target_base = NULL;
+ if (ses->goto_position) mem_free(ses->goto_position), ses->goto_position = NULL;
+ change_screen_status(ses);
+ print_screen_status(ses);
+ return r;
+}
+
+static void tp_display(struct session *ses)
+{
+ ses_abort_1st_state_loading(ses);
+ ses->rq = ses->tq;
+ ses->tq = NULL;
+ ses_go_forward(ses, 1, 0);
+}
+
+static int direct_download_possible(struct object_request *rq, struct assoc *a)
+{
+ unsigned char *proto = get_protocol_name(rq->url);
+ int ret = 0;
+ if (!proto) return 0;
+ if (a->accept_http && !strcasecmp(proto, "http")) ret = 1;
+ if (a->accept_ftp && !strcasecmp(proto, "ftp")) ret = 1;
+ mem_free(proto);
+ if (proxies.only_proxies) ret = 0;
+ return ret;
+}
+
+static int prog_sel_save(struct dialog_data *dlg, struct dialog_item_data *idata)
+{
+ struct session *ses = (struct session *)dlg->dlg->udata2;
+
+ tp_save(ses);
+
+ cancel_dialog(dlg, idata);
+ return 0;
+}
+
+static int prog_sel_display(struct dialog_data *dlg, struct dialog_item_data *idata)
+{
+ struct session *ses = (struct session *)dlg->dlg->udata2;
+
+ tp_display(ses);
+
+ cancel_dialog(dlg, idata);
+ return 0;
+}
+
+static int prog_sel_cancel(struct dialog_data *dlg, struct dialog_item_data *idata)
+{
+ struct session *ses = (struct session *)dlg->dlg->udata2;
+
+ tp_cancel(ses);
+
+ cancel_dialog(dlg, idata);
+ return 0;
+}
+
+static int prog_sel_open(struct dialog_data *dlg, struct dialog_item_data *idata)
+{
+ struct assoc *a = (struct assoc*)idata->item->udata;
+ struct session *ses = (struct session *)dlg->dlg->udata2;
+
+ if (!a) internal("This should not happen.\n");
+ ses->tq_prog = stracpy(a->prog), ses->tq_prog_flag_block = a->block, ses->tq_prog_flag_direct = direct_download_possible(ses->tq, a);
+ tp_open(ses);
+ cancel_dialog(dlg,idata);
+ return 0;
+}
+
+static void type_query_multiple_programs(struct session *ses, unsigned char *ct, struct assoc *a, int n)
+{
+ int i;
+ struct dialog *d;
+ struct memory_list *ml;
+ unsigned char **text_array;
+
+ text_array = mem_alloc(6 * sizeof(unsigned char *));
+ text_array[0] = TEXT_(T_CONTENT_TYPE_IS);
+ text_array[1] = " ";
+ text_array[2] = ct;
+ text_array[3] = ".\n";
+ text_array[4] = !anonymous ? TEXT_(T_DO_YOU_WANT_TO_OPEN_SAVE_OR_DISPLAY_THIS_FILE) : TEXT_(T_DO_YOU_WANT_TO_OPEN_OR_DISPLAY_THIS_FILE);
+ text_array[5] = NULL;
+
+ if ((unsigned)n > (MAXINT - sizeof(struct dialog)) / sizeof(struct dialog_item) - 4) overalloc();
+ d = mem_calloc(sizeof(struct dialog) + (n + 2 + (!anonymous)) * sizeof(struct dialog_item));
+ d->title = TEXT_(T_WHAT_TO_DO);
+ d->fn = msg_box_fn;
+ d->udata = text_array;
+ d->udata2 = ses;
+ d->align = AL_CENTER;
+ ml = getml(d, a, ct, text_array, NULL);
+
+ for (i = 0; i < n; i++) {
+ unsigned char *bla = stracpy(_(TEXT_(T_OPEN_WITH),ses->term));
+ add_to_strn(&bla, " ");
+ add_to_strn(&bla, a[i].label);
+
+ d->items[i].type = D_BUTTON;
+ d->items[i].fn = prog_sel_open;
+ d->items[i].udata = a + i;
+ d->items[i].text = bla;
+ a[i].prog = stracpy(a[i].prog);
+ add_to_ml(&ml, bla, a[i].prog, NULL);
+ }
+ if (!anonymous) {
+ d->items[i].type = D_BUTTON;
+ d->items[i].fn = prog_sel_save;
+ d->items[i].text = TEXT_(T_SAVE);
+ i++;
+ }
+ d->items[i].type = D_BUTTON;
+ d->items[i].fn = prog_sel_display;
+ d->items[i].text = TEXT_(T_DISPLAY);
+ i++;
+ d->items[i].type = D_BUTTON;
+ d->items[i].fn = prog_sel_cancel;
+ d->items[i].gid = B_ESC;
+ d->items[i].text = TEXT_(T_CANCEL);
+ i++;
+ d->items[i].type = D_END;
+ do_dialog(ses->term, d, ml);
+}
+
+
+
+/* deallocates a */
+static void type_query(struct session *ses, unsigned char *ct, struct assoc *a, int n)
+{
+ unsigned char *m1;
+ unsigned char *m2;
+ if (!ct) ct = stracpy("unknown");
+ if (ses->tq_prog) mem_free(ses->tq_prog), ses->tq_prog = NULL;
+
+ if (n > 1) {
+ type_query_multiple_programs(ses, ct, a, n);
+ return;
+ }
+
+ if (a) ses->tq_prog = stracpy(a[0].prog), ses->tq_prog_flag_block = a[0].block, ses->tq_prog_flag_direct = direct_download_possible(ses->tq, a);
+ if (a && !a[0].ask) {
+ tp_open(ses);
+ if (n) mem_free(a);
+ mem_free(ct);
+ return;
+ }
+ m1 = stracpy(ct);
+ if (!a) {
+ if (!anonymous) msg_box(ses->term, getml(m1, NULL), TEXT_(T_UNKNOWN_TYPE), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTENT_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_SAVE_OR_DISLPAY_THIS_FILE), NULL, ses, 3, TEXT_(T_SAVE), tp_save, B_ENTER, TEXT_(T_DISPLAY), tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
+ else msg_box(ses->term, getml(m1, NULL), TEXT_(T_UNKNOWN_TYPE), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTENT_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_SAVE_OR_DISLPAY_THIS_FILE), NULL, ses, 2, TEXT_(T_DISPLAY), tp_display, B_ENTER, TEXT_(T_CANCEL), tp_cancel, B_ESC);
+ } else {
+ m2 = stracpy(a[0].label ? a[0].label : (unsigned char *)"");
+ if (!anonymous) msg_box(ses->term, getml(m1, m2, NULL), TEXT_(T_WHAT_TO_DO), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTENT_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_OPEN_FILE_WITH), " ", m2, ", ", TEXT_(T_SAVE_IT_OR_DISPLAY_IT), NULL, ses, 4, TEXT_(T_OPEN), tp_open, B_ENTER, TEXT_(T_SAVE), tp_save, 0, TEXT_(T_DISPLAY), tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
+ else msg_box(ses->term, getml(m1, m2, NULL), TEXT_(T_WHAT_TO_DO), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTENT_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_OPEN_FILE_WITH), " ", m2, ", ", TEXT_(T_SAVE_IT_OR_DISPLAY_IT), NULL, ses, 3, TEXT_(T_OPEN), tp_open, B_ENTER, TEXT_(T_DISPLAY), tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
+ }
+ if (n) mem_free(a);
+ mem_free(ct);
+}
+
+static void ses_go_to_2nd_state(struct session *ses)
+{
+ struct assoc *a;
+ int n;
+ unsigned char *ct = NULL;
+ int r = plain_type(ses, ses->rq, &ct);
+ if (r == 0 || r == 1 || r == 2 || r == 3) goto go;
+ if (!(a = get_type_assoc(ses->term, ct, &n)) && strlen(ct) >= 4 && !casecmp(ct, "text", 4)) {
+ r = 1;
+ goto go;
+ }
+ if (ses->tq) {
+ ses_abort_1st_state_loading(ses);
+ if (n) mem_free(a);
+ if (ct) mem_free(ct);
+ return;
+ }
+ (ses->tq = ses->rq)->upcall = NULL;
+ ses->rq = NULL;
+ ses_abort_1st_state_loading(ses);
+ if (!n && ses->tq->ce && ses->tq->ce->head) {
+ unsigned char *file = get_filename_from_header(ses->tq->ce->head);
+ if (file) {
+ unsigned char *new_ct = get_content_type_by_extension(file);
+ mem_free(file);
+ if (new_ct) {
+ a = get_type_assoc(ses->term, new_ct, &n);
+ mem_free(new_ct);
+ }
+ }
+ }
+ type_query(ses, ct, a, n);
+ return;
+ go:
+ ses_go_forward(ses, r, ses->wtd_refresh);
+ if (ct) mem_free(ct);
+}
+
+static void ses_go_back_to_2nd_state(struct session *ses)
+{
+ ses_go_backward(ses);
+}
+
+static void ses_finished_1st_state(struct object_request *rq, struct session *ses)
+{
+ if (rq->state != O_WAITING) {
+ if (ses->wtd_refresh && ses->wtd_target_base && ses->wtd_target_base->refresh_timer != -1) {
+ kill_timer(ses->wtd_target_base->refresh_timer);
+ ses->wtd_target_base->refresh_timer = -1;
+ }
+ }
+ switch (rq->state) {
+ case O_WAITING:
+ change_screen_status(ses);
+ print_screen_status(ses);
+ break;
+ case O_FAILED:
+ print_error_dialog(ses, &rq->stat, rq->url);
+ ses_abort_1st_state_loading(ses);
+ break;
+ case O_LOADING:
+ case O_INCOMPLETE:
+ case O_OK:
+ if (!ses->goto_position && rq->goto_position) ses->goto_position = stracpy(rq->goto_position);
+ ses->wtd(ses);
+ break;
+ }
+}
+
+void ses_destroy_defered_jump(struct session *ses)
+{
+#ifdef JS
+ if (ses->defered_url) mem_free(ses->defered_url), ses->defered_url = NULL;
+ if (ses->defered_target) mem_free(ses->defered_target), ses->defered_target = NULL;
+ ses->defered_target_base = NULL;
+#endif
+}
+
+#ifdef JS
+/* test if there're any running scripts */
+static inline int any_running_scripts(struct f_data_c *fd)
+{
+ if (!fd->js) return 0;
+ return (fd->js->active) || (!list_empty(fd->js->queue));
+}
+#else
+static inline int any_running_scripts(struct f_data_c *fd)
+{
+ return 0;
+}
+#endif
+
+/* if from_goto_dialog is 1, set prev_url to NULL */
+void goto_url_f(struct session *ses, void (*state2)(struct session *), unsigned char *url, unsigned char *target, struct f_data_c *df, int data, int defer, int from_goto_dialog, int refresh)
+{
+ unsigned char *u, *pos;
+ unsigned char *prev_url;
+ void (*fn)(struct session *, unsigned char *);
+ if (!state2) state2 = ses_go_to_2nd_state;
+#ifdef JS
+ if (ses->defered_url && defer && any_running_scripts(ses->screen)) return;
+#endif
+ ses_destroy_defered_jump(ses);
+ if ((fn = get_external_protocol_function(url))) {
+ if (proxies.only_proxies && url_bypasses_socks(url)) {
+ msg_box(ses->term, NULL, TEXT_(T_ERROR), AL_CENTER, TEXT_(T_NO_PROXY), NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ return;
+ }
+ fn(ses, url);
+ return;
+ }
+ ses->reloadlevel = NC_CACHE;
+ if (!(u = translate_url(url, ses->term->cwd))) {
+ struct status stat = { NULL, NULL, NULL, NULL, S_BAD_URL, PRI_CANCEL, 0, NULL, NULL, NULL };
+ print_error_dialog(ses, &stat, url);
+ return;
+ }
+#ifdef JS
+ if (defer && any_running_scripts(ses->screen)) {
+ ses->defered_url = u;
+ ses->defered_target = stracpy(target);
+ ses->defered_target_base = df;
+ ses->defered_data = data;
+ ses->defered_seq = jsint_execute_seq++;
+ return;
+ }
+#endif
+ pos = extract_position(u);
+ if (ses->wtd == state2 && !strcmp(ses->rq->orig_url, u) && !xstrcmp(ses->wtd_target, target) && ses->wtd_target_base == df) {
+ mem_free(u);
+ if (ses->goto_position) mem_free(ses->goto_position);
+ ses->goto_position = pos;
+ return;
+ }
+ ses_abort_1st_state_loading(ses);
+ ses->wtd = state2;
+ ses->wtd_target = stracpy(target);
+ ses->wtd_target_base = df;
+ ses->wtd_refresh = refresh;
+ if (ses->goto_position) mem_free(ses->goto_position);
+ ses->goto_position = pos;
+ if (!from_goto_dialog && df && df->rq) prev_url = df->rq->url;
+ else prev_url = NULL; /* previous page is empty - this probably never happens, but for sure */
+ if (refresh && ses->wtd == ses_go_to_2nd_state) {
+ struct f_data_c *fr = find_frame(ses, ses->wtd_target, ses->wtd_target_base);
+ if (fr && fr->loc) if (!strcmp(fr->loc->url, u)) ses->reloadlevel = NC_RELOAD;
+ }
+ request_object(ses->term, u, prev_url, PRI_MAIN, ses->reloadlevel, (void (*)(struct object_request *, void *))ses_finished_1st_state, ses, &ses->rq);
+ mem_free(u);
+}
+
+/* this doesn't send referer */
+void goto_url(struct session *ses, unsigned char *url)
+{
+ goto_url_f(ses, NULL, url, NULL, NULL, -1, 0, 1, 0);
+}
+
+/* this one sends referer */
+void goto_url_not_from_dialog(struct session *ses, unsigned char *url)
+{
+ goto_url_f(ses, NULL, url, NULL, NULL, -1, 0, 0, 0);
+}
+
+static void ses_imgmap(struct session *ses)
+{
+ unsigned char *start, *end;
+ struct memory_list *ml;
+ struct menu_item *menu;
+ struct f_data_c *fd;
+ if (ses->rq->state != O_OK && ses->rq->state != O_INCOMPLETE) return;
+ if (!(fd = current_frame(ses)) || !fd->f_data) return;
+ if (get_file(ses->rq, &start, &end)) return;
+ d_opt = &fd->f_data->opt;
+ if (get_image_map(ses->rq->ce && ses->rq->ce->head ? ses->rq->ce->head : (unsigned char *)"", start, end, ses->goto_position, &menu, &ml, ses->imgmap_href_base, ses->imgmap_target_base, ses->term->spec->charset, ses->ds.assume_cp, ses->ds.hard_assume, 0)) {
+ ses_abort_1st_state_loading(ses);
+ return;
+ }
+ add_empty_window(ses->term, (void (*)(void *))freeml, ml);
+ do_menu(ses->term, menu, ses);
+ ses_abort_1st_state_loading(ses);
+}
+
+void goto_imgmap(struct session *ses, unsigned char *url, unsigned char *href, unsigned char *target)
+{
+ if (ses->imgmap_href_base) mem_free(ses->imgmap_href_base);
+ ses->imgmap_href_base = href;
+ if (ses->imgmap_target_base) mem_free(ses->imgmap_target_base);
+ ses->imgmap_target_base = target;
+ goto_url_f(ses, ses_imgmap, url, NULL, NULL, -1, 0, 0, 0);
+}
+
+void map_selected(struct terminal *term, struct link_def *ld, struct session *ses)
+{
+ int x = 0;
+ if (ld->onclick) {
+ struct f_data_c *fd = current_frame(ses);
+ jsint_execute_code(fd, ld->onclick, strlen(ld->onclick), -1, -1, -1, NULL);
+ x = 1;
+ }
+ if (ld->link) goto_url_f(ses, NULL, ld->link, ld->target, current_frame(ses), -1, x, 0, 0);
+}
+
+void go_back(struct session *ses, int num_steps)
+{
+ struct location *loc;
+ int n;
+ if (!num_steps) return;
+ ses->reloadlevel = NC_CACHE;
+ ses_destroy_defered_jump(ses);
+ if (ses_abort_1st_state_loading(ses)) {
+ change_screen_status(ses);
+ print_screen_status(ses);
+ return;
+ }
+ n = num_steps;
+ if (num_steps > 0) {
+ foreach(loc, ses->history) {
+ if (!n--) goto have_loc;
+ }
+ return;
+ } else {
+ foreach(loc, ses->forward_history) {
+ if (!++n) goto have_loc;
+ }
+ return;
+ }
+ have_loc:
+ ses->wtd = ses_go_back_to_2nd_state;
+ ses->wtd_num_steps = num_steps;
+ request_object(ses->term, loc->url, loc->prev_url, PRI_MAIN, NC_ALWAYS_CACHE, (void (*)(struct object_request *, void *))ses_finished_1st_state, ses, &ses->rq);
+}
+
+static void reload_frame(struct f_data_c *fd, int no_cache)
+{
+ unsigned char *u;
+ if (!list_empty(fd->subframes)) {
+ struct f_data_c *fdd;
+ foreach(fdd, fd->subframes) {
+ reload_frame(fdd, no_cache);
+ }
+ return;
+ }
+ if (!fd->rq) return;
+ if (fd->f_data && !f_is_finished(fd->f_data)) return;
+ u = stracpy(fd->rq->url);
+ release_object(&fd->rq);
+ if (fd->f_data) release_object(&fd->f_data->rq);
+ request_object(fd->ses->term, u, NULL, PRI_MAIN, no_cache, (void (*)(struct object_request *, void *))fd_loaded, fd, &fd->rq);
+ if (fd->f_data) clone_object(fd->rq, &fd->f_data->rq);
+ fd->next_update = get_time();
+ fd->done = 0;
+ fd->parsed_done = 0;
+ mem_free(u);
+ jsint_destroy(fd);
+}
+
+void reload(struct session *ses, int no_cache)
+{
+ ses_destroy_defered_jump(ses);
+ if (no_cache == -1) no_cache = ++ses->reloadlevel;
+ else ses->reloadlevel = no_cache;
+ reload_frame(ses->screen, no_cache);
+ /*request_object(ses->term, cur_loc(ses)->url, cur_loc(ses)->prev_url, PRI_MAIN, no_cache, (void (*)(struct object_request *, void *))ses_finished_1st_state, ses, &ses->rq);*/
+}
+
+static void set_doc_view(struct session *ses)
+{
+ ses->screen->xp = 0;
+ ses->screen->yp = gf_val(1, G_BFU_FONT_SIZE);
+ ses->screen->xw = ses->term->x;
+ if (ses->term->y < gf_val(2, 2 * G_BFU_FONT_SIZE)) ses->screen->yw = 0;
+ else ses->screen->yw = ses->term->y - gf_val(2, 2 * G_BFU_FONT_SIZE);
+}
+
+static struct session *create_session(struct window *win)
+{
+ static int session_id = 1;
+ struct terminal *term = win->term;
+ struct session *ses;
+ ses = mem_calloc(sizeof(struct session));
+ init_list(ses->history);
+ init_list(ses->forward_history);
+ ses->term = term;
+ ses->win = win;
+ ses->id = session_id++;
+ ses->screen = create_f_data_c(ses, NULL);
+ ses->screen->xp = 0; ses->screen->xw = term->x;
+ ses->screen->yp = gf_val(1, G_BFU_FONT_SIZE);
+ ses->screen->yw = term->y - gf_val(2, 2 * G_BFU_FONT_SIZE);
+ memcpy(&ses->ds, &dds, sizeof(struct document_setup));
+ init_list(ses->format_cache);
+ add_to_list(sessions, ses);
+ if (first_use) {
+ first_use = 0;
+ msg_box(term, NULL, TEXT_(T_WELCOME), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_WELCOME_TO_LINKS), "\n\n", TEXT_(T_BASIC_HELP), NULL, NULL, 1, TEXT_(T_OK), NULL, B_ENTER | B_ESC);
+ }
+ return ses;
+}
+
+/*vyrobi retezec znaku, ktery se posilaj skrz unix domain socket hlavni instanci
+ prohlizece
+ cp=cislo session odkud se ma kopirovat (kdyz se klikne na "open new window")
+ url=url kam se ma jit (v pripade kliknuti na "open link in new window" nebo pusteni linksu z prikazove radky s url)
+ framename=jmeno ramu, ktery se vytvori
+
+ vraci sekvenci bytu a delku
+ */
+void *create_session_info(int cp, unsigned char *url, unsigned char *framename, int *ll)
+{
+ size_t l = strlen(url);
+ size_t l1 = framename ? strlen(framename) : 0;
+ int *i;
+ if (l > MAXINT || l1 > MAXINT) overalloc();
+ if (framename && !strcmp(framename, "_blank")) l1 = 0;
+ *ll = 3 * sizeof(int) + l + l1;
+ i = mem_alloc(3 * sizeof(int) + l + l1);
+ i[0] = cp;
+ i[1] = l;
+ i[2] = l1;
+ memcpy(i + 3, url, l);
+ if (l1) memcpy((unsigned char*)(i + 3) + l, framename, l1);
+ return i;
+}
+
+/* dostane data z create_session_info a nainicializuje podle nich session
+ vraci -1 pokud jsou data vadna
+ */
+static int read_session_info(struct session *ses, void *data, int len)
+{
+ unsigned char *h;
+ int cpfrom, sz, sz1;
+ struct session *s;
+ if (len < 3 * (int)sizeof(int)) return -1;
+ cpfrom = *(int *)data;
+ sz = *((int *)data + 1);
+ sz1= *((int *)data + 2);
+ foreach(s, sessions) if (s->id == cpfrom) {
+ memcpy(&ses->ds, &s->ds, sizeof(struct document_setup));
+ if (!sz) {
+ if (!list_empty(s->history)) {
+ struct location *loc = s->history.next;
+ if (loc->url) goto_url(ses, loc->url);
+ }
+ return 0;
+ } else {
+ break;
+ }
+ }
+ if (sz1) {
+ unsigned char *tgt;
+ if (len<3*(int)sizeof(int)+sz+sz1) goto bla;
+ if ((unsigned)sz1 >= MAXINT) overalloc();
+ tgt=mem_alloc(sz1+1);
+ memcpy(tgt, (unsigned char*)((int*)data+3)+sz,sz1);
+ tgt[sz1]=0;
+ if (ses->wanted_framename) mem_free(ses->wanted_framename), ses->wanted_framename=NULL;
+ ses->wanted_framename=tgt;
+ }
+ bla:
+ if (sz) {
+ unsigned char *u, *uu;
+ if (len < 3 * (int)sizeof(int) + sz) return 0;
+ if ((unsigned)sz >= MAXINT) overalloc();
+ u = mem_alloc(sz + 1);
+ memcpy(u, (int *)data + 3, sz);
+ u[sz] = 0;
+ uu = decode_url(u);
+ goto_url(ses, uu);
+ mem_free(u);
+ mem_free(uu);
+ } else if ((h = getenv("WWW_HOME")) && *h) {
+ goto_url(ses, h);
+ }
+#if defined(HAVE_NETINET_DHCP_H) && defined(HAVE_DHCP_OPTION)
+ else {
+ unsigned char b[4];
+ if (dhcp_option(PF_INET, BOOTP_WWW_SERVERS, b, 4) == 4) {
+ unsigned char s[16];
+ sprintf(s, "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
+ goto_url(ses, s);
+ }
+ }
+#endif
+ return 0;
+}
+
+void destroy_session(struct session *ses)
+{
+ struct download *d;
+ foreach(d, downloads) if (d->ses == ses && d->prog) {
+ d = d->prev;
+ abort_download(d->next);
+ }
+ ses_abort_1st_state_loading(ses);
+ reinit_f_data_c(ses->screen);
+ mem_free(ses->screen);
+ while (!list_empty(ses->format_cache)) {
+ struct f_data *f = ses->format_cache.next;
+ del_from_list(f);
+ destroy_formatted(f);
+ }
+ while (!list_empty(ses->history)) destroy_location(ses->history.next);
+ clear_forward_history(ses);
+ if (ses->st) mem_free(ses->st);
+ if (ses->st_old) mem_free(ses->st_old);
+ if (ses->default_status)mem_free(ses->default_status);
+ if (ses->dn_url) mem_free(ses->dn_url);
+ if (ses->search_word) mem_free(ses->search_word);
+ if (ses->last_search_word) mem_free(ses->last_search_word);
+ if (ses->imgmap_href_base) mem_free(ses->imgmap_href_base);
+ if (ses->imgmap_target_base) mem_free(ses->imgmap_target_base);
+ if (ses->wanted_framename) mem_free(ses->wanted_framename);
+
+ release_object(&ses->tq);
+ if (ses->tq_prog) mem_free(ses->tq_prog);
+
+ ses_destroy_defered_jump(ses);
+
+ del_from_list(ses);
+}
+
+void win_func(struct window *win, struct event *ev, int fw)
+{
+ struct session *ses = win->data;
+ switch ((int)ev->ev) {
+ case EV_ABORT:
+ if (ses) destroy_session(ses);
+ break;
+ case EV_INIT:
+ if (!(ses = win->data = create_session(win)) || read_session_info(ses, (char *)ev->b + sizeof(int), *(int *)ev->b)) {
+ register_bottom_half((void (*)(void *))destroy_terminal, win->term);
+ return;
+ }
+ case EV_RESIZE:
+ if (ses->st_old) mem_free(ses->st_old), ses->st_old = NULL;
+ GF(set_window_pos(win, 0, 0, ev->x, ev->y));
+ set_doc_view(ses);
+ html_interpret_recursive(ses->screen);
+ draw_fd(ses->screen);
+ break;
+ case EV_REDRAW:
+ if (ses->st_old) mem_free(ses->st_old), ses->st_old = NULL;
+ draw_formatted(ses);
+ break;
+ case EV_MOUSE:
+#ifdef G
+ if (F) set_window_ptr(win, ev->x, ev->y);
+#endif
+ /* fall through ... */
+ case EV_KBD:
+ send_event(ses, ev);
+ break;
+ default:
+ error("ERROR: unknown event");
+ }
+}
+
+/*
+ Gets the url being viewed by this session. Writes it into str.
+ A maximum of str_size bytes (including null) will be written.
+*/
+unsigned char *get_current_url(struct session *ses, unsigned char *str, size_t str_size) {
+ unsigned char *here, *end_of_url;
+ size_t url_len = 0;
+
+ /* Not looking at anything */
+ if (list_empty(ses->history))
+ return NULL;
+
+ here = cur_loc(ses)->url;
+
+ /* Find the length of the url */
+ if ((end_of_url = strchr(here, POST_CHAR))) {
+ url_len = (size_t)(end_of_url - (unsigned char *)here);
+ } else {
+ url_len = strlen(here);
+ }
+
+ /* Ensure that the url size is not greater than str_size */
+ if (url_len >= str_size)
+ url_len = str_size - 1;
+
+ safe_strncpy(str, here, url_len + 1);
+
+ return str;
+}
+
+
+/*
+ Gets the title of the page being viewed by this session. Writes it into str.
+ A maximum of str_size bytes (including null) will be written.
+*/
+unsigned char *get_current_title(struct session *ses, unsigned char *str, size_t str_size) {
+ struct f_data_c *fd;
+ fd = (struct f_data_c *)current_frame(ses);
+
+ /* Ensure that the title is defined */
+ if (!fd || !fd->f_data)
+ return NULL;
+
+ return safe_strncpy(str, fd->f_data->title, str_size);
+}
+
+#if 0
+/*
+ Gets the url of the link currently selected. Writes it into str.
+ A maximum of str_size bytes (including null) will be written.
+*/
+unsigned char *get_current_link_url(struct session *ses, unsigned char *str, size_t str_size) {
+ struct f_data_c *fd;
+ struct link *l;
+
+ fd = (struct f_data_c *)current_frame(ses);
+ /* What the hell is an 'fd'? */
+ if (!fd)
+ return NULL;
+
+ /* Nothing selected? */
+ if (fd->vs->current_link == -1 || fd->vs->current_link >= fd->f_data->nlinks)
+ return NULL;
+
+ l = &fd->f_data->links[fd->vs->current_link];
+ /* Only write a link */
+ if (l->type != L_LINK)
+ return NULL;
+
+ return safe_strncpy(str, l->where, str_size);
+}
+#endif