~ubuntu-branches/ubuntu/feisty/elinks/feisty-updates

« back to all changes in this revision

Viewing changes to src/sched/download.c

  • Committer: Bazaar Package Importer
  • Author(s): Peter Gervai
  • Date: 2004-01-21 22:13:45 UTC
  • Revision ID: james.westby@ubuntu.com-20040121221345-ju33hai1yhhqt6kn
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Downloads managment */
 
2
/* $Id: download.c,v 1.210.2.1 2004/01/17 07:57:11 miciah Exp $ */
 
3
 
 
4
#ifdef HAVE_CONFIG_H
 
5
#include "config.h"
 
6
#endif
 
7
 
 
8
#include <errno.h>
 
9
#include <stdio.h>
 
10
#include <stdlib.h>
 
11
#include <string.h>
 
12
#ifdef HAVE_SYS_CYGWIN_H
 
13
#include <sys/cygwin.h>
 
14
#endif
 
15
#include <sys/types.h>
 
16
#ifdef HAVE_FCNTL_H
 
17
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
 
18
#endif
 
19
#include <sys/stat.h>
 
20
#ifdef HAVE_UNISTD_H
 
21
#include <unistd.h>
 
22
#endif
 
23
#include <utime.h>
 
24
 
 
25
#include "elinks.h"
 
26
 
 
27
#include "bfu/hierbox.h"
 
28
#include "bfu/msgbox.h"
 
29
#include "config/options.h"
 
30
#include "dialogs/download.h"
 
31
#include "dialogs/menu.h"
 
32
#include "cache/cache.h"
 
33
#include "intl/gettext/libintl.h"
 
34
#include "mime/mime.h"
 
35
#include "osdep/osdep.h"
 
36
#include "protocol/http/date.h"
 
37
#include "protocol/uri.h"
 
38
#include "protocol/protocol.h"
 
39
#include "sched/connection.h"
 
40
#include "sched/download.h"
 
41
#include "sched/error.h"
 
42
#include "sched/history.h"
 
43
#include "sched/location.h"
 
44
#include "sched/session.h"
 
45
#include "sched/task.h"
 
46
#include "terminal/draw.h"
 
47
#include "terminal/screen.h"
 
48
#include "terminal/terminal.h"
 
49
#include "util/conv.h"
 
50
#include "util/error.h"
 
51
#include "util/file.h"
 
52
#include "util/memlist.h"
 
53
#include "util/memory.h"
 
54
#include "util/object.h"
 
55
#include "util/string.h"
 
56
#include "util/ttime.h"
 
57
 
 
58
 
 
59
/* TODO: tp_*() should be in separate file, I guess? --pasky */
 
60
 
 
61
 
 
62
INIT_LIST_HEAD(downloads);
 
63
 
 
64
 
 
65
int
 
66
are_there_downloads(void)
 
67
{
 
68
        struct file_download *down;
 
69
 
 
70
        foreach (down, downloads)
 
71
                if (!down->prog)
 
72
                        return 1;
 
73
 
 
74
        return 0;
 
75
}
 
76
 
 
77
 
 
78
static struct session *
 
79
get_download_ses(struct file_download *down)
 
80
{
 
81
        struct session *ses;
 
82
 
 
83
        foreach (ses, sessions)
 
84
                if (ses == down->ses)
 
85
                        return ses;
 
86
 
 
87
        foreach (ses, sessions)
 
88
                if (ses->tab->term == down->term)
 
89
                        return ses;
 
90
 
 
91
        if (!list_empty(sessions))
 
92
                return sessions.next;
 
93
 
 
94
        return NULL;
 
95
}
 
96
 
 
97
 
 
98
void
 
99
abort_download(struct file_download *down, int stop)
 
100
{
 
101
        if (down->box_item)
 
102
                done_listbox_item(&download_browser, down->box_item);
 
103
        if (down->dlg_data) cancel_dialog(down->dlg_data, NULL);
 
104
        if (down->download.state >= 0)
 
105
                change_connection(&down->download, NULL, PRI_CANCEL, stop);
 
106
        if (down->url) mem_free(down->url);
 
107
 
 
108
        if (down->handle != -1) {
 
109
                prealloc_truncate(down->handle, down->last_pos);
 
110
                close(down->handle);
 
111
        }
 
112
 
 
113
        if (down->prog) mem_free(down->prog);
 
114
        if (down->file) {
 
115
                if (down->delete) unlink(down->file);
 
116
                mem_free(down->file);
 
117
        }
 
118
        del_from_list(down);
 
119
        mem_free(down);
 
120
}
 
121
 
 
122
 
 
123
static void
 
124
kill_downloads_to_file(unsigned char *file)
 
125
{
 
126
        struct file_download *file_download;
 
127
 
 
128
        foreach (file_download, downloads) {
 
129
                if (strcmp(file_download->file, file))
 
130
                        continue;
 
131
 
 
132
                file_download = file_download->prev;
 
133
                abort_download(file_download->next, 0);
 
134
        }
 
135
}
 
136
 
 
137
 
 
138
void
 
139
abort_all_downloads(void)
 
140
{
 
141
        while (!list_empty(downloads))
 
142
                abort_download(downloads.next, 0 /* does it matter? */);
 
143
}
 
144
 
 
145
 
 
146
void
 
147
destroy_downloads(struct session *ses)
 
148
{
 
149
        struct file_download *file_download;
 
150
        struct session *s;
 
151
 
 
152
        /* We are supposed to blat all downloads to external handlers belonging
 
153
         * to @ses, but we will refuse to do so if there is another session
 
154
         * bound to this terminal. That looks like the reasonable thing to do,
 
155
         * fulfilling the principle of least astonishment. */
 
156
        foreach (s, sessions) {
 
157
                if (s != ses && s->tab->term == ses->tab->term)
 
158
                        return;
 
159
        }
 
160
 
 
161
        foreach (file_download, downloads) {
 
162
                if (file_download->ses != ses || !file_download->prog)
 
163
                        continue;
 
164
 
 
165
                file_download = file_download->prev;
 
166
                abort_download(file_download->next, 0);
 
167
        }
 
168
}
 
169
 
 
170
 
 
171
static void
 
172
download_error_dialog(struct file_download *file_download, int saved_errno)
 
173
{
 
174
        unsigned char *msg = stracpy(file_download->file);
 
175
        unsigned char *emsg = stracpy((unsigned char *) strerror(saved_errno));
 
176
        struct session *ses = get_download_ses(file_download);
 
177
 
 
178
        if (msg && emsg && ses) {
 
179
                struct terminal *term = ses->tab->term;
 
180
 
 
181
                msg_box(term, getml(msg, emsg, NULL), MSGBOX_FREE_TEXT,
 
182
                        N_("Download error"), AL_CENTER,
 
183
                        msg_text(term, N_("Could not create file %s: %s"), msg, emsg),
 
184
                        NULL, 1,
 
185
                        N_("OK"), NULL, B_ENTER | B_ESC);
 
186
        } else {
 
187
                if (msg) mem_free(msg);
 
188
                if (emsg) mem_free(emsg);
 
189
        }
 
190
}
 
191
 
 
192
int
 
193
write_cache_entry_to_file(struct cache_entry *ce, struct file_download *file_download)
 
194
{
 
195
        struct fragment *frag;
 
196
 
 
197
        if (file_download->download.prg && file_download->download.prg->seek) {
 
198
                file_download->last_pos = file_download->download.prg->seek;
 
199
                file_download->download.prg->seek = 0;
 
200
                /* This is exclusive with the prealloc, thus we can perform
 
201
                 * this in front of that thing safely. */
 
202
                if (lseek(file_download->handle, file_download->last_pos, SEEK_SET) < 0)
 
203
                        goto write_error;
 
204
        }
 
205
 
 
206
        foreach (frag, ce->frag) {
 
207
                int remain = file_download->last_pos - frag->offset;
 
208
                int *h = &file_download->handle;
 
209
                int w;
 
210
 
 
211
                if (remain < 0 || frag->length <= remain)
 
212
                        continue;
 
213
 
 
214
#ifdef USE_OPEN_PREALLOC
 
215
                if (!file_download->last_pos
 
216
                    && (!file_download->stat.prg
 
217
                        || file_download->stat.prg->size > 0)) {
 
218
                        close(*h);
 
219
                        *h = open_prealloc(file_download->file,
 
220
                                           O_CREAT|O_WRONLY|O_TRUNC,
 
221
                                           0666,
 
222
                                           file_download->stat.prg
 
223
                                           ? file_download->stat.prg->size
 
224
                                           : ce->length);
 
225
                        if (*h == -1) goto write_error;
 
226
                        set_bin(*h);
 
227
                }
 
228
#endif
 
229
 
 
230
                w = safe_write(*h, frag->data + remain, frag->length - remain);
 
231
                if (w == -1) goto write_error;
 
232
 
 
233
                file_download->last_pos += w;
 
234
        }
 
235
 
 
236
        return 1;
 
237
 
 
238
write_error:
 
239
        if (!list_empty(sessions)) download_error_dialog(file_download, errno);
 
240
 
 
241
        return 0;
 
242
}
 
243
 
 
244
static void
 
245
download_data_store(struct download *download, struct file_download *file_download)
 
246
{
 
247
        struct session *ses = get_download_ses(file_download);
 
248
        struct terminal *term = NULL;
 
249
 
 
250
        if (!ses) goto abort;
 
251
        term = ses->tab->term;
 
252
 
 
253
        if (download->state >= 0) {
 
254
                if (file_download->dlg_data)
 
255
                        redraw_dialog(file_download->dlg_data, 1);
 
256
                return;
 
257
        }
 
258
 
 
259
        if (download->state != S_OK) {
 
260
                unsigned char *errmsg = get_err_msg(download->state, term);
 
261
                unsigned char *url;
 
262
 
 
263
                if (!errmsg) goto abort;
 
264
 
 
265
                url = get_no_post_url(file_download->url, NULL);
 
266
 
 
267
                if (!url) goto abort;
 
268
 
 
269
                msg_box(term, getml(url, NULL), MSGBOX_FREE_TEXT,
 
270
                        N_("Download error"), AL_CENTER,
 
271
                        msg_text(term, N_("Error downloading %s:\n\n%s"), url, errmsg),
 
272
                        get_download_ses(file_download), 1,
 
273
                        N_("OK"), NULL, B_ENTER | B_ESC /*,
 
274
                        N_(T_RETRY), NULL, 0 */ /* FIXME: retry */);
 
275
 
 
276
                goto abort;
 
277
        }
 
278
 
 
279
        if (file_download->prog) {
 
280
                prealloc_truncate(file_download->handle,
 
281
                                  file_download->last_pos);
 
282
                close(file_download->handle);
 
283
                file_download->handle = -1;
 
284
                exec_on_terminal(term, file_download->prog, file_download->file,
 
285
                                 !!file_download->prog_flags);
 
286
                file_download->delete = 0;
 
287
                goto abort;
 
288
        }
 
289
 
 
290
        if (file_download->notify) {
 
291
                unsigned char *url = get_no_post_url(file_download->url, NULL);
 
292
 
 
293
                if (url) {
 
294
                        msg_box(term, getml(url, NULL), MSGBOX_FREE_TEXT,
 
295
                                N_("Download"), AL_CENTER,
 
296
                                msg_text(term, N_("Download complete:\n%s"), url),
 
297
                                get_download_ses(file_download), 1,
 
298
                                N_("OK"), NULL, B_ENTER | B_ESC);
 
299
                }
 
300
        }
 
301
 
 
302
        if (file_download->remotetime
 
303
            && get_opt_int("document.download.set_original_time")) {
 
304
                struct utimbuf foo;
 
305
 
 
306
                foo.actime = foo.modtime = file_download->remotetime;
 
307
                utime(file_download->file, &foo);
 
308
        }
 
309
 
 
310
abort:
 
311
        if (term && get_opt_int("document.download.notify_bell")
 
312
                    + file_download->notify >= 2) {
 
313
                beep_terminal(term);
 
314
        }
 
315
 
 
316
        abort_download(file_download, 0);
 
317
}
 
318
 
 
319
static void
 
320
download_data(struct download *download, struct file_download *file_download)
 
321
{
 
322
        struct cache_entry *ce = download->ce;
 
323
        int broken_302_redirect;
 
324
 
 
325
        if (!ce) goto store;
 
326
 
 
327
        if (download->state >= S_WAIT && download->state < S_TRANS)
 
328
                goto store;
 
329
 
 
330
        if (ce->last_modified)
 
331
                file_download->remotetime = parse_http_date(ce->last_modified);
 
332
 
 
333
        broken_302_redirect = get_opt_int("protocol.http.bugs.broken_302_redirect");
 
334
 
 
335
        while (ce->redirect && file_download->redirect_cnt++ < MAX_REDIRECTS) {
 
336
                unsigned char *u;
 
337
 
 
338
                if (download->state >= 0)
 
339
                        change_connection(&file_download->download, NULL, PRI_CANCEL, 0);
 
340
 
 
341
                u = join_urls(file_download->url, ce->redirect);
 
342
                if (!u) break;
 
343
 
 
344
                if (!broken_302_redirect && !ce->redirect_get) {
 
345
                        unsigned char *postdata = post_data_start(file_download->url);
 
346
 
 
347
                        if (postdata) add_to_strn(&u, postdata);
 
348
                }
 
349
 
 
350
                mem_free(file_download->url);
 
351
 
 
352
                file_download->url = u;
 
353
                file_download->download.state = S_WAIT_REDIR;
 
354
 
 
355
                if (file_download->dlg_data)
 
356
                        redraw_dialog(file_download->dlg_data, 1);
 
357
 
 
358
                load_url(file_download->url, get_cache_uri(ce), &file_download->download,
 
359
                         PRI_DOWNLOAD, CACHE_MODE_NORMAL,
 
360
                         download->prg ? download->prg->start : 0);
 
361
 
 
362
                return;
 
363
        }
 
364
 
 
365
        if (!write_cache_entry_to_file(ce, file_download)) {
 
366
                detach_connection(download, file_download->last_pos);
 
367
                abort_download(file_download, 0);
 
368
                return;
 
369
        }
 
370
 
 
371
        detach_connection(download, file_download->last_pos);
 
372
 
 
373
store:
 
374
        download_data_store(download, file_download);
 
375
}
 
376
 
 
377
 
 
378
/* XXX: We assume that resume is everytime zero in lun's callbacks. */
 
379
struct lun_hop {
 
380
        struct terminal *term;
 
381
        unsigned char *ofile, *file;
 
382
 
 
383
        void (*callback)(struct terminal *, unsigned char *, void *, int);
 
384
        void *data;
 
385
};
 
386
 
 
387
static void
 
388
lun_alternate(struct lun_hop *lun_hop)
 
389
{
 
390
        lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data, 0);
 
391
        if (lun_hop->ofile) mem_free(lun_hop->ofile);
 
392
        mem_free(lun_hop);
 
393
}
 
394
 
 
395
static void
 
396
lun_overwrite(struct lun_hop *lun_hop)
 
397
{
 
398
        lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, 0);
 
399
        if (lun_hop->file) mem_free(lun_hop->file);
 
400
        mem_free(lun_hop);
 
401
}
 
402
 
 
403
static void
 
404
lun_resume(struct lun_hop *lun_hop)
 
405
{
 
406
        lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, 1);
 
407
        if (lun_hop->file) mem_free(lun_hop->file);
 
408
        mem_free(lun_hop);
 
409
}
 
410
 
 
411
static void
 
412
lun_cancel(struct lun_hop *lun_hop)
 
413
{
 
414
        lun_hop->callback(lun_hop->term, NULL, lun_hop->data, 0);
 
415
        if (lun_hop->ofile) mem_free(lun_hop->ofile);
 
416
        if (lun_hop->file) mem_free(lun_hop->file);
 
417
        mem_free(lun_hop);
 
418
}
 
419
 
 
420
static void
 
421
lookup_unique_name(struct terminal *term, unsigned char *ofile, int resume,
 
422
                   void (*callback)(struct terminal *, unsigned char *, void *, int),
 
423
                   void *data)
 
424
{
 
425
        struct lun_hop *lun_hop;
 
426
        unsigned char *file;
 
427
        int overwrite;
 
428
 
 
429
        ofile = expand_tilde(ofile);
 
430
 
 
431
        /* Minor code duplication to prevent useless call to get_opt_int()
 
432
         * if possible. --Zas */
 
433
        if (resume) {
 
434
                callback(term, ofile, data, resume);
 
435
                return;
 
436
        }
 
437
 
 
438
        /* !overwrite means always silently overwrite, which may be admitelly
 
439
         * indeed a little confusing ;-) */
 
440
        overwrite = get_opt_int("document.download.overwrite");
 
441
        if (!overwrite) {
 
442
                /* Nothing special to do... */
 
443
                callback(term, ofile, data, resume);
 
444
                return;
 
445
        }
 
446
 
 
447
        /* Check if the file already exists (file != ofile). */
 
448
        file = get_unique_name(ofile);
 
449
 
 
450
        if (!file || overwrite == 1 || file == ofile) {
 
451
                /* Still nothing special to do... */
 
452
                if (file != ofile) mem_free(ofile);
 
453
                callback(term, file, data, 0);
 
454
                return;
 
455
        }
 
456
 
 
457
        /* overwrite == 2 (ask) and file != ofile (=> original file already
 
458
         * exists) */
 
459
 
 
460
        lun_hop = mem_calloc(1, sizeof(struct lun_hop));
 
461
        if (!lun_hop) {
 
462
                if (file != ofile) mem_free(file);
 
463
                mem_free(ofile);
 
464
                callback(term, NULL, data, 0);
 
465
                return;
 
466
        }
 
467
        lun_hop->term = term;
 
468
        lun_hop->ofile = ofile;
 
469
        lun_hop->file = (file != ofile) ? file : stracpy(ofile);
 
470
        lun_hop->callback = callback;
 
471
        lun_hop->data = data;
 
472
 
 
473
        msg_box(term, NULL, MSGBOX_FREE_TEXT,
 
474
                N_("File exists"), AL_CENTER,
 
475
                msg_text(term, N_("This file already exists:\n"
 
476
                        "%s\n\n"
 
477
                        "The alternative filename is:\n"
 
478
                        "%s"),
 
479
                        empty_string_or_(lun_hop->ofile),
 
480
                        empty_string_or_(file)),
 
481
                lun_hop, 4,
 
482
                N_("Save under the alternative name"), lun_alternate, B_ENTER,
 
483
                N_("Overwrite the original file"), lun_overwrite, 0,
 
484
                N_("Resume download of the original file"), lun_resume, 0,
 
485
                N_("Cancel"), lun_cancel, B_ESC);
 
486
}
 
487
 
 
488
 
 
489
static void create_download_file_do(struct terminal *, unsigned char *, void *, int);
 
490
 
 
491
struct cdf_hop {
 
492
        unsigned char **real_file;
 
493
        int safe;
 
494
 
 
495
        void (*callback)(struct terminal *, int, void *, int);
 
496
        void *data;
 
497
};
 
498
 
 
499
void
 
500
create_download_file(struct terminal *term, unsigned char *fi,
 
501
                     unsigned char **real_file, int safe, int resume,
 
502
                     void (*callback)(struct terminal *, int, void *, int),
 
503
                     void *data)
 
504
{
 
505
        struct cdf_hop *cdf_hop = mem_calloc(1, sizeof(struct cdf_hop));
 
506
        unsigned char *wd;
 
507
 
 
508
        if (!cdf_hop) {
 
509
                callback(term, -1, data, 0);
 
510
                return;
 
511
        }
 
512
 
 
513
        cdf_hop->real_file = real_file;
 
514
        cdf_hop->safe = safe;
 
515
        cdf_hop->callback = callback;
 
516
        cdf_hop->data = data;
 
517
 
 
518
        /* FIXME: The wd bussiness is probably useless here? --pasky */
 
519
        wd = get_cwd();
 
520
        set_cwd(term->cwd);
 
521
 
 
522
        /* Also the tilde will be expanded here. */
 
523
        lookup_unique_name(term, fi, resume, create_download_file_do, cdf_hop);
 
524
 
 
525
        if (wd) {
 
526
                set_cwd(wd);
 
527
                mem_free(wd);
 
528
        }
 
529
}
 
530
 
 
531
static void
 
532
create_download_file_do(struct terminal *term, unsigned char *file, void *data,
 
533
                        int resume)
 
534
{
 
535
        struct cdf_hop *cdf_hop = data;
 
536
        unsigned char *wd;
 
537
        int h = -1;
 
538
        int saved_errno;
 
539
#ifdef NO_FILE_SECURITY
 
540
        int sf = 0;
 
541
#else
 
542
        int sf = cdf_hop->safe;
 
543
#endif
 
544
 
 
545
        if (!file) goto finish;
 
546
 
 
547
        wd = get_cwd();
 
548
        set_cwd(term->cwd);
 
549
 
 
550
        /* O_APPEND means repositioning at the end of file before each write(),
 
551
         * thus ignoring seek()s and that can hide mysterious bugs. IMHO.
 
552
         * --pasky */
 
553
        h = open(file, O_CREAT | O_WRONLY | (resume ? 0 : O_TRUNC)
 
554
                        | (sf && !resume ? O_EXCL : 0),
 
555
                 sf ? 0600 : 0666);
 
556
        saved_errno = errno; /* Saved in case of ... --Zas */
 
557
 
 
558
        if (wd) {
 
559
                set_cwd(wd);
 
560
                mem_free(wd);
 
561
        }
 
562
 
 
563
        if (h == -1) {
 
564
                msg_box(term, NULL, MSGBOX_FREE_TEXT,
 
565
                        N_("Download error"), AL_CENTER,
 
566
                        msg_text(term, N_("Could not create file '%s':\n%s"),
 
567
                                file, strerror(saved_errno)),
 
568
                        NULL, 1,
 
569
                        N_("OK"), NULL, B_ENTER | B_ESC);
 
570
 
 
571
                mem_free(file);
 
572
                goto finish;
 
573
 
 
574
        } else {
 
575
                set_bin(h);
 
576
 
 
577
                if (!cdf_hop->safe) {
 
578
                        unsigned char *download_dir = get_opt_str("document.download.directory");
 
579
                        int i;
 
580
 
 
581
                        safe_strncpy(download_dir, file, MAX_STR_LEN);
 
582
 
 
583
                        /* Find the used directory so it's available in history */
 
584
                        for (i = strlen(download_dir); i >= 0; i--)
 
585
                                if (dir_sep(download_dir[i]))
 
586
                                        break;
 
587
                        download_dir[i + 1] = 0;
 
588
                }
 
589
        }
 
590
 
 
591
        if (cdf_hop->real_file)
 
592
                *cdf_hop->real_file = file;
 
593
        else
 
594
                mem_free(file);
 
595
 
 
596
finish:
 
597
        cdf_hop->callback(term, h, cdf_hop->data, resume);
 
598
        mem_free(cdf_hop);
 
599
        return;
 
600
}
 
601
 
 
602
 
 
603
static unsigned char *
 
604
get_temp_name(unsigned char *url)
 
605
{
 
606
        struct string name;
 
607
        unsigned char *extension;
 
608
        /* FIXME
 
609
         * We use tempnam() here, which is unsafe (race condition), for now.
 
610
         * This should be changed at some time, but it needs an in-depth work
 
611
         * of whole download code. --Zas */
 
612
        unsigned char *nm = tempnam(NULL, ELINKS_TEMPNAME_PREFIX);
 
613
 
 
614
        if (!nm) return NULL;
 
615
 
 
616
        if (!init_string(&name)) {
 
617
                mem_free(nm);
 
618
                return NULL;
 
619
        }
 
620
 
 
621
        add_to_string(&name, nm);
 
622
        free(nm);
 
623
 
 
624
        extension = get_extension_from_url(url);
 
625
        if (extension) {
 
626
                add_char_to_string(&name, '.');
 
627
                add_shell_safe_to_string(&name, extension, strlen(extension));
 
628
                mem_free(extension);
 
629
        }
 
630
 
 
631
        return name.source;
 
632
}
 
633
 
 
634
 
 
635
unsigned char *
 
636
subst_file(unsigned char *prog, unsigned char *file)
 
637
{
 
638
        struct string name;
 
639
 
 
640
        if (!init_string(&name)) return NULL;
 
641
 
 
642
        while (*prog) {
 
643
                register int p;
 
644
 
 
645
                for (p = 0; prog[p] && prog[p] != '%'; p++);
 
646
 
 
647
                add_bytes_to_string(&name, prog, p);
 
648
                prog += p;
 
649
 
 
650
                if (*prog == '%') {
 
651
#if defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)
 
652
#ifdef MAX_PATH
 
653
                        unsigned char new_path[MAX_PATH];
 
654
#else
 
655
                        unsigned char new_path[1024];
 
656
#endif
 
657
 
 
658
                        cygwin_conv_to_full_win32_path(file, new_path);
 
659
                        add_to_string(&name, new_path);
 
660
#else
 
661
                        add_to_string(&name, file);
 
662
#endif
 
663
                        prog++;
 
664
                }
 
665
        }
 
666
 
 
667
        return name.source;
 
668
}
 
669
 
 
670
 
 
671
static void common_download_do(struct terminal *, int, void *, int);
 
672
 
 
673
struct cmdw_hop {
 
674
        struct session *ses;
 
675
        unsigned char *real_file;
 
676
};
 
677
 
 
678
static void
 
679
common_download(struct session *ses, unsigned char *file, int resume)
 
680
{
 
681
        struct cmdw_hop *cmdw_hop;
 
682
 
 
683
        if (!ses->dn_url) return;
 
684
 
 
685
        cmdw_hop = mem_calloc(1, sizeof(struct cmdw_hop));
 
686
        if (!cmdw_hop) return;
 
687
        cmdw_hop->ses = ses;
 
688
 
 
689
        kill_downloads_to_file(file);
 
690
 
 
691
        create_download_file(ses->tab->term, file, &cmdw_hop->real_file, 0,
 
692
                             resume, common_download_do, cmdw_hop);
 
693
}
 
694
 
 
695
static void
 
696
common_download_do(struct terminal *term, int fd, void *data, int resume)
 
697
{
 
698
        struct cmdw_hop *cmdw_hop = data;
 
699
        struct file_download *file_download = NULL;
 
700
        unsigned char *url = cmdw_hop->ses->dn_url;
 
701
        struct stat buf;
 
702
 
 
703
        if (!cmdw_hop->real_file) goto download_error;
 
704
 
 
705
        file_download = mem_calloc(1, sizeof(struct file_download));
 
706
        if (!file_download) goto download_error;
 
707
 
 
708
        file_download->url = stracpy(url);
 
709
        if (!file_download->url) goto download_error;
 
710
 
 
711
        file_download->file = cmdw_hop->real_file;
 
712
 
 
713
        if (fstat(fd, &buf)) goto download_error;
 
714
        file_download->last_pos = resume ? (int) buf.st_size : 0;
 
715
 
 
716
        file_download->download.end = (void (*)(struct download *, void *)) download_data;
 
717
        file_download->download.data = file_download;
 
718
        file_download->handle = fd;
 
719
        file_download->ses = cmdw_hop->ses;
 
720
        /* The tab may be closed, but we will still want to ie. open the
 
721
         * handler on that terminal. */
 
722
        file_download->term = cmdw_hop->ses->tab->term;
 
723
        file_download->remotetime = 0;
 
724
 
 
725
        add_to_list(downloads, file_download);
 
726
        load_url(url, cmdw_hop->ses->ref_url, &file_download->download, PRI_DOWNLOAD, CACHE_MODE_NORMAL,
 
727
                 (resume ? file_download->last_pos : 0));
 
728
 
 
729
        if (is_in_downloads_list(file_download))
 
730
                file_download->box_item = add_listbox_item(&download_browser,
 
731
                                                           file_download->url,
 
732
                                                           file_download);
 
733
 
 
734
        display_download(cmdw_hop->ses->tab->term, file_download, cmdw_hop->ses);
 
735
 
 
736
        mem_free(cmdw_hop);
 
737
        return;
 
738
 
 
739
download_error:
 
740
        if (file_download) {
 
741
                if (file_download->url) mem_free(file_download->url);
 
742
                mem_free(file_download);
 
743
        }
 
744
        mem_free(cmdw_hop);
 
745
}
 
746
 
 
747
void
 
748
start_download(void *ses, unsigned char *file)
 
749
{
 
750
        enum protocol protocol = known_protocol(((struct session *)ses)->dn_url,
 
751
                                                NULL);
 
752
 
 
753
        if (protocol == PROTOCOL_UNKNOWN) {
 
754
                print_unknown_protocol_dialog(ses);
 
755
                return;
 
756
        }
 
757
 
 
758
        common_download(ses, file, 0);
 
759
}
 
760
 
 
761
void
 
762
resume_download(void *ses, unsigned char *file)
 
763
{
 
764
        common_download(ses, file, 1);
 
765
}
 
766
 
 
767
 
 
768
static void tp_cancel(void *);
 
769
static void tp_free(struct tq *);
 
770
 
 
771
 
 
772
static void continue_download_do(struct terminal *, int, void *, int);
 
773
 
 
774
struct codw_hop {
 
775
        struct tq *tq;
 
776
        unsigned char *real_file;
 
777
        unsigned char *file;
 
778
};
 
779
 
 
780
static void
 
781
continue_download(void *data, unsigned char *file)
 
782
{
 
783
        struct tq *tq = data;
 
784
        struct codw_hop *codw_hop;
 
785
 
 
786
        if (!tq->url) return;
 
787
 
 
788
        codw_hop = mem_calloc(1, sizeof(struct codw_hop));
 
789
        if (!codw_hop) {
 
790
                tp_cancel(tq);
 
791
                return;
 
792
        }
 
793
 
 
794
        if (tq->prog) {
 
795
                /* FIXME: get_temp_name() calls tempnam(). --Zas */
 
796
                file = get_temp_name(tq->url);
 
797
                if (!file) {
 
798
                        mem_free(codw_hop);
 
799
                        tp_cancel(tq);
 
800
                        return;
 
801
                }
 
802
        }
 
803
 
 
804
        codw_hop->tq = tq;
 
805
        codw_hop->file = file;
 
806
 
 
807
        kill_downloads_to_file(file);
 
808
 
 
809
        create_download_file(tq->ses->tab->term, file, &codw_hop->real_file,
 
810
                             !!tq->prog, 0, continue_download_do, codw_hop);
 
811
}
 
812
 
 
813
static void
 
814
continue_download_do(struct terminal *term, int fd, void *data, int resume)
 
815
{
 
816
        struct codw_hop *codw_hop = data;
 
817
        struct file_download *file_download = NULL;
 
818
        unsigned char *url = codw_hop->tq->url;
 
819
 
 
820
        if (!codw_hop->real_file) goto cancel;
 
821
 
 
822
        file_download = mem_calloc(1, sizeof(struct file_download));
 
823
        if (!file_download) goto cancel;
 
824
 
 
825
        object_nolock(file_download); /* Debugging purpose. */
 
826
 
 
827
        file_download->url = stracpy(url);
 
828
        if (!file_download->url) goto cancel;
 
829
 
 
830
        file_download->file = codw_hop->real_file;
 
831
 
 
832
        file_download->download.end = (void (*)(struct download *, void *)) download_data;
 
833
        file_download->download.data = file_download;
 
834
        file_download->last_pos = 0;
 
835
        file_download->handle = fd;
 
836
        file_download->ses = codw_hop->tq->ses;
 
837
 
 
838
        if (codw_hop->tq->prog) {
 
839
                file_download->prog = subst_file(codw_hop->tq->prog, codw_hop->file);
 
840
                file_download->delete = 1;
 
841
                mem_free(codw_hop->file);
 
842
                mem_free(codw_hop->tq->prog);
 
843
                codw_hop->tq->prog = NULL;
 
844
        }
 
845
 
 
846
        file_download->prog_flags = codw_hop->tq->prog_flags;
 
847
 
 
848
        add_to_list(downloads, file_download);
 
849
        change_connection(&codw_hop->tq->download, &file_download->download, PRI_DOWNLOAD, 0);
 
850
 
 
851
        if (is_in_downloads_list(file_download))
 
852
                file_download->box_item = add_listbox_item(&download_browser,
 
853
                                                           file_download->url,
 
854
                                                           file_download);
 
855
 
 
856
        tp_free(codw_hop->tq);
 
857
        display_download(codw_hop->tq->ses->tab->term, file_download, codw_hop->tq->ses);
 
858
 
 
859
        mem_free(codw_hop);
 
860
        return;
 
861
 
 
862
cancel:
 
863
        tp_cancel(codw_hop->tq);
 
864
        if (codw_hop->tq->prog && codw_hop->file) mem_free(codw_hop->file);
 
865
        if (file_download) {
 
866
                if (file_download->url) mem_free(file_download->url);
 
867
                mem_free(file_download);
 
868
        }
 
869
        mem_free(codw_hop);
 
870
}
 
871
 
 
872
 
 
873
static void
 
874
tp_free(struct tq *tq)
 
875
{
 
876
        object_unlock(tq->ce);
 
877
        mem_free(tq->url);
 
878
        if (tq->goto_position) mem_free(tq->goto_position);
 
879
        if (tq->prog) mem_free(tq->prog);
 
880
        if (tq->target_frame) mem_free(tq->target_frame);
 
881
        del_from_list(tq);
 
882
        mem_free(tq);
 
883
}
 
884
 
 
885
static void
 
886
tp_cancel(void *data)
 
887
{
 
888
        struct tq *tq = data;
 
889
        /* XXX: Should we really abort? (1 vs 0 as the last param) --pasky */
 
890
        change_connection(&tq->download, NULL, PRI_CANCEL, 1);
 
891
        tp_free(tq);
 
892
}
 
893
 
 
894
 
 
895
static void
 
896
tp_save(struct tq *tq)
 
897
{
 
898
        if (tq->prog) {
 
899
                mem_free(tq->prog);
 
900
                tq->prog = NULL;
 
901
        }
 
902
        query_file(tq->ses, tq->url, tq, continue_download, tp_cancel, 1);
 
903
}
 
904
 
 
905
 
 
906
static void
 
907
tp_open(struct tq *tq)
 
908
{
 
909
        continue_download(tq, "");
 
910
}
 
911
 
 
912
 
 
913
/* FIXME: We need to modify this function to take frame data instead, as we
 
914
 * want to use this function for frames as well (now, when frame has content
 
915
 * type text/plain, it is ignored and displayed as HTML). */
 
916
static void
 
917
tp_display(struct tq *tq)
 
918
{
 
919
        struct view_state *vs;
 
920
        struct session *ses = tq->ses;
 
921
        unsigned char *goto_position = ses->goto_position;
 
922
        unsigned char *loading_url = ses->loading_url;
 
923
        unsigned char *target_frame = ses->task.target_frame;
 
924
 
 
925
        ses->goto_position = tq->goto_position;
 
926
        ses->loading_url = tq->url;
 
927
        ses->task.target_frame = tq->target_frame;
 
928
        vs = ses_forward(ses, tq->frame);
 
929
        if (vs) vs->plain = 1;
 
930
        ses->goto_position = goto_position;
 
931
        ses->loading_url = loading_url;
 
932
        ses->task.target_frame = target_frame;
 
933
 
 
934
        if (!tq->frame) {
 
935
                tq->goto_position = NULL;
 
936
                cur_loc(ses)->download.end = (void (*)(struct download *, void *))
 
937
                                     doc_end_load;
 
938
                cur_loc(ses)->download.data = ses;
 
939
 
 
940
                if (tq->download.state >= 0)
 
941
                        change_connection(&tq->download, &cur_loc(ses)->download, PRI_MAIN, 0);
 
942
                else
 
943
                        cur_loc(ses)->download.state = tq->download.state;
 
944
        }
 
945
 
 
946
        display_timer(ses);
 
947
        tp_free(tq);
 
948
}
 
949
 
 
950
 
 
951
static void
 
952
type_query(struct tq *tq, unsigned char *ct, struct mime_handler *handler)
 
953
{
 
954
        struct string filename;
 
955
        unsigned char *content_type;
 
956
 
 
957
        if (tq->prog) {
 
958
                mem_free(tq->prog);
 
959
                tq->prog = NULL;
 
960
        }
 
961
 
 
962
        if (handler) {
 
963
                tq->prog = stracpy(handler->program);
 
964
                tq->prog_flags = handler->block;
 
965
                if (!handler->ask) {
 
966
                        tp_open(tq);
 
967
                        return;
 
968
                }
 
969
        }
 
970
 
 
971
        content_type = stracpy(ct);
 
972
        if (!content_type) return;
 
973
 
 
974
        if (init_string(&filename))
 
975
                add_string_uri_filename_to_string(&filename, tq->url);
 
976
 
 
977
        /* @filename.source should be last in the getml()s ! (It terminates the
 
978
         * pointers list in case of allocation failure.) */
 
979
 
 
980
        if (!handler) {
 
981
                if (!get_opt_int_tree(cmdline_options, "anonymous")) {
 
982
                        msg_box(tq->ses->tab->term, getml(content_type, filename.source, NULL), MSGBOX_FREE_TEXT,
 
983
                                N_("Unknown type"), AL_CENTER,
 
984
                                msg_text(tq->ses->tab->term, N_("Would you like to "
 
985
                                         "save the file '%s' (type: %s) "
 
986
                                         "or display it?"),
 
987
                                         filename.source, content_type),
 
988
                                tq, 3,
 
989
                                N_("Save"), tp_save, B_ENTER,
 
990
                                N_("Display"), tp_display, 0,
 
991
                                N_("Cancel"), tp_cancel, B_ESC);
 
992
                } else {
 
993
                        msg_box(tq->ses->tab->term, getml(content_type, filename.source, NULL), MSGBOX_FREE_TEXT,
 
994
                                N_("Unknown type"), AL_CENTER,
 
995
                                msg_text(tq->ses->tab->term, N_("Would you like to "
 
996
                                         "display the file '%s' (type: %s)?"),
 
997
                                         filename.source, content_type),
 
998
                                tq, 2,
 
999
                                N_("Display"), tp_display, B_ENTER,
 
1000
                                N_("Cancel"), tp_cancel, B_ESC);
 
1001
                }
 
1002
        } else {
 
1003
                unsigned char *description = handler->description;
 
1004
                unsigned char *desc_sep = (*description) ? "; " : "";
 
1005
 
 
1006
                if (!get_opt_int_tree(cmdline_options, "anonymous")) {
 
1007
                        /* TODO: Improve the dialog to let the user correct the
 
1008
                         * used program. */
 
1009
                        msg_box(tq->ses->tab->term, getml(content_type, filename.source, NULL), MSGBOX_FREE_TEXT,
 
1010
                                N_("What to do?"), AL_CENTER,
 
1011
                                msg_text(tq->ses->tab->term, N_("Would you like to "
 
1012
                                         "open the file '%s' (type: %s%s%s)\n"
 
1013
                                         "with '%s', save it or display it?"),
 
1014
                                         filename.source, content_type, desc_sep,
 
1015
                                         description, handler->program),
 
1016
                                tq, 4,
 
1017
                                N_("Open"), tp_open, B_ENTER,
 
1018
                                N_("Save"), tp_save, 0,
 
1019
                                N_("Display"), tp_display, 0,
 
1020
                                N_("Cancel"), tp_cancel, B_ESC);
 
1021
                } else {
 
1022
                        msg_box(tq->ses->tab->term, getml(content_type, filename.source, NULL), MSGBOX_FREE_TEXT,
 
1023
                                N_("What to do?"), AL_CENTER,
 
1024
                                msg_text(tq->ses->tab->term, N_("Would you like to "
 
1025
                                         "open the file '%s' (type: %s%s%s)\n"
 
1026
                                         "with '%s', or display it?"),
 
1027
                                         filename.source, content_type, desc_sep,
 
1028
                                         description, handler->program),
 
1029
                                tq, 3,
 
1030
                                N_("Open"), tp_open, B_ENTER,
 
1031
                                N_("Display"), tp_display, 0,
 
1032
                                N_("Cancel"), tp_cancel, B_ESC);
 
1033
                }
 
1034
        }
 
1035
}
 
1036
 
 
1037
struct {
 
1038
        unsigned char *type;
 
1039
        unsigned int plain:1;
 
1040
} static known_types[] = {
 
1041
        { "text/html",                  0 },
 
1042
        { "application/xhtml+xml",      0 }, /* RFC 3236 */
 
1043
        { "text/plain",                 1 },
 
1044
        { NULL,                         1 },
 
1045
};
 
1046
 
 
1047
int
 
1048
ses_chktype(struct session *ses, struct download *loading, struct cache_entry *ce, int frame)
 
1049
{
 
1050
        struct mime_handler *handler;
 
1051
        struct view_state *vs;
 
1052
        struct tq *tq;
 
1053
        unsigned char *ctype = get_content_type(ce->head, get_cache_uri(ce));
 
1054
        int plaintext = 1;
 
1055
        int ret = 0;
 
1056
        int xwin, i;
 
1057
 
 
1058
        if (!ctype)
 
1059
                goto plaintext_follow;
 
1060
 
 
1061
        for (i = 0; known_types[i].type; i++) {
 
1062
                if (strcasecmp(ctype, known_types[i].type))
 
1063
                        continue;
 
1064
 
 
1065
                plaintext = known_types[i].plain;
 
1066
                goto plaintext_follow;
 
1067
        }
 
1068
 
 
1069
        xwin = ses->tab->term->environment & ENV_XWIN;
 
1070
        handler = get_mime_type_handler(ctype, xwin);
 
1071
 
 
1072
        if (!handler && strlen(ctype) >= 4 && !strncasecmp(ctype, "text", 4))
 
1073
                goto plaintext_follow;
 
1074
 
 
1075
        foreach (tq, ses->tq)
 
1076
                if (!strcmp(tq->url, ses->loading_url))
 
1077
                        goto do_not_follow;
 
1078
 
 
1079
        tq = mem_calloc(1, sizeof(struct tq));
 
1080
        if (!tq) goto do_not_follow;
 
1081
        add_to_list(ses->tq, tq);
 
1082
        ret = 1;
 
1083
 
 
1084
        tq->url = stracpy(ses->loading_url);
 
1085
        change_connection(loading, &tq->download, PRI_MAIN, 0);
 
1086
        loading->state = S_OK;
 
1087
 
 
1088
        tq->ce = ce;
 
1089
        object_lock(tq->ce);
 
1090
 
 
1091
        if (ses->goto_position) tq->goto_position = stracpy(ses->goto_position);
 
1092
        if (ses->task.target_frame)
 
1093
                tq->target_frame = stracpy(ses->task.target_frame);
 
1094
        tq->ses = ses;
 
1095
 
 
1096
        type_query(tq, ctype, handler);
 
1097
 
 
1098
do_not_follow:
 
1099
        mem_free(ctype);
 
1100
        if (handler) mem_free(handler);
 
1101
 
 
1102
        return ret;
 
1103
 
 
1104
plaintext_follow:
 
1105
        if (ctype) mem_free(ctype);
 
1106
 
 
1107
        vs = ses_forward(ses, frame);
 
1108
        if (vs) vs->plain = plaintext;
 
1109
        return 0;
 
1110
}