📄 download.c
字号:
int sf = 0;#else int sf = cdf_hop->safe;#endif if (!file) goto finish; wd = get_cwd(); set_cwd(term->cwd); /* O_APPEND means repositioning at the end of file before each write(), * thus ignoring seek()s and that can hide mysterious bugs. IMHO. * --pasky */ h = open(file, O_CREAT | O_WRONLY | (resume ? 0 : O_TRUNC) | (sf && !resume ? O_EXCL : 0), sf ? 0600 : 0666); saved_errno = errno; /* Saved in case of ... --Zas */ if (wd) { set_cwd(wd); mem_free(wd); } if (h == -1) { info_box(term, MSGBOX_FREE_TEXT, N_("Download error"), ALIGN_CENTER, msg_text(term, N_("Could not create file '%s':\n%s"), file, strerror(saved_errno))); mem_free(file); goto finish; } else { set_bin(h); if (!cdf_hop->safe) { unsigned char *download_dir = get_opt_str("document.download.directory"); int i; safe_strncpy(download_dir, file, MAX_STR_LEN); /* Find the used directory so it's available in history */ for (i = strlen(download_dir); i >= 0; i--) if (dir_sep(download_dir[i])) break; download_dir[i + 1] = 0; } } if (cdf_hop->real_file) *cdf_hop->real_file = file; else mem_free(file);finish: cdf_hop->callback(term, h, cdf_hop->data, resume); mem_free(cdf_hop);}voidcreate_download_file(struct terminal *term, unsigned char *fi, unsigned char **real_file, int safe, int resume, void (*callback)(struct terminal *, int, void *, int), void *data){ struct cdf_hop *cdf_hop = mem_calloc(1, sizeof(*cdf_hop)); unsigned char *wd; if (!cdf_hop) { callback(term, -1, data, 0); return; } cdf_hop->real_file = real_file; cdf_hop->safe = safe; cdf_hop->callback = callback; cdf_hop->data = data; /* FIXME: The wd bussiness is probably useless here? --pasky */ wd = get_cwd(); set_cwd(term->cwd); /* Also the tilde will be expanded here. */ lookup_unique_name(term, fi, resume, create_download_file_do, cdf_hop); if (wd) { set_cwd(wd); mem_free(wd); }}static unsigned char *get_temp_name(struct uri *uri){ struct string name; unsigned char *extension; /* FIXME * We use tempnam() here, which is unsafe (race condition), for now. * This should be changed at some time, but it needs an in-depth work * of whole download code. --Zas */ unsigned char *nm = tempnam(NULL, ELINKS_TEMPNAME_PREFIX); if (!nm) return NULL; if (!init_string(&name)) { free(nm); return NULL; } add_to_string(&name, nm); free(nm); extension = get_extension_from_uri(uri); if (extension) { add_shell_safe_to_string(&name, extension, strlen(extension)); mem_free(extension); } return name.source;}static unsigned char *subst_file(unsigned char *prog, unsigned char *file){ struct string name; if (!init_string(&name)) return NULL; while (*prog) { int p; for (p = 0; prog[p] && prog[p] != '%'; p++); add_bytes_to_string(&name, prog, p); prog += p; if (*prog == '%') {#if defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)#ifdef MAX_PATH unsigned char new_path[MAX_PATH];#else unsigned char new_path[1024];#endif cygwin_conv_to_full_win32_path(file, new_path); add_to_string(&name, new_path);#else add_to_string(&name, file);#endif prog++; } } return name.source;}struct cmdw_hop { struct session *ses; unsigned char *real_file;};static voidcommon_download_do(struct terminal *term, int fd, void *data, int resume){ struct cmdw_hop *cmdw_hop = data; unsigned char *file = cmdw_hop->real_file; struct file_download *file_download; struct session *ses = cmdw_hop->ses; struct stat buf; mem_free(cmdw_hop); if (!file || fstat(fd, &buf)) return; file_download = init_file_download(ses->download_uri, ses, file, fd); if (!file_download) return; file_download->last_pos = resume ? (int) buf.st_size : 0; display_download(ses->tab->term, file_download, ses); load_uri(file_download->uri, ses->referrer, &file_download->download, PRI_DOWNLOAD, CACHE_MODE_NORMAL, file_download->last_pos);}static voidcommon_download(struct session *ses, unsigned char *file, int resume){ struct cmdw_hop *cmdw_hop; if (!ses->download_uri) return; cmdw_hop = mem_calloc(1, sizeof(*cmdw_hop)); if (!cmdw_hop) return; cmdw_hop->ses = ses; kill_downloads_to_file(file); create_download_file(ses->tab->term, file, &cmdw_hop->real_file, 0, resume, common_download_do, cmdw_hop);}voidstart_download(void *ses, unsigned char *file){ common_download(ses, file, 0);}voidresume_download(void *ses, unsigned char *file){ common_download(ses, file, 1);}struct codw_hop { struct type_query *type_query; unsigned char *real_file; unsigned char *file;};static void tp_cancel(void *);static voidcontinue_download_do(struct terminal *term, int fd, void *data, int resume){ struct codw_hop *codw_hop = data; struct file_download *file_download = NULL; struct type_query *type_query; assert(codw_hop); assert(codw_hop->type_query); assert(codw_hop->type_query->uri); assert(codw_hop->type_query->ses); type_query = codw_hop->type_query; if (!codw_hop->real_file) goto cancel; file_download = init_file_download(type_query->uri, type_query->ses, codw_hop->real_file, fd); if (!file_download) goto cancel; if (type_query->external_handler) { file_download->external_handler = subst_file(type_query->external_handler, codw_hop->file); file_download->delete = 1; mem_free(codw_hop->file); mem_free_set(&type_query->external_handler, NULL); } file_download->external_handler_flags = type_query->external_handler_flags; /* Done here and not in init_file_download() so that the external * handler can become initialized. */ display_download(term, file_download, type_query->ses); change_connection(&type_query->download, &file_download->download, PRI_DOWNLOAD, 0); done_type_query(type_query); mem_free(codw_hop); return;cancel: if (type_query->external_handler) mem_free_if(codw_hop->file); tp_cancel(type_query); mem_free(codw_hop);}static voidcontinue_download(void *data, unsigned char *file){ struct type_query *type_query = data; struct codw_hop *codw_hop = mem_calloc(1, sizeof(*codw_hop)); if (!codw_hop) { tp_cancel(type_query); return; } if (type_query->external_handler) { /* FIXME: get_temp_name() calls tempnam(). --Zas */ file = get_temp_name(type_query->uri); if (!file) { mem_free(codw_hop); tp_cancel(type_query); return; } } codw_hop->type_query = type_query; codw_hop->file = file; kill_downloads_to_file(file); create_download_file(type_query->ses->tab->term, file, &codw_hop->real_file, !!type_query->external_handler, 0, continue_download_do, codw_hop);}static struct type_query *init_type_query(struct session *ses, struct download *download, struct cache_entry *cached){ struct type_query *type_query; /* There can be only one ... */ foreach (type_query, ses->type_queries) if (compare_uri(type_query->uri, ses->loading_uri, 0)) return NULL; type_query = mem_calloc(1, sizeof(*type_query)); if (!type_query) return NULL; type_query->uri = get_uri_reference(ses->loading_uri); type_query->ses = ses; type_query->target_frame = null_or_stracpy(ses->task.target_frame); type_query->cached = cached; object_lock(type_query->cached); change_connection(download, &type_query->download, PRI_MAIN, 0); download->state = S_OK; add_to_list(ses->type_queries, type_query); return type_query;}voiddone_type_query(struct type_query *type_query){ /* Unregister any active download */ if (is_in_progress_state(type_query->download.state)) change_connection(&type_query->download, NULL, PRI_CANCEL, 0); object_unlock(type_query->cached); done_uri(type_query->uri); mem_free_if(type_query->external_handler); mem_free_if(type_query->target_frame); del_from_list(type_query); mem_free(type_query);}static voidtp_cancel(void *data){ struct type_query *type_query = data; /* XXX: Should we really abort? (1 vs 0 as the last param) --pasky */ change_connection(&type_query->download, NULL, PRI_CANCEL, 1); done_type_query(type_query);}static voidtp_save(struct type_query *type_query){ mem_free_set(&type_query->external_handler, NULL); query_file(type_query->ses, type_query->uri, type_query, continue_download, tp_cancel, 1);}static voidtp_open(struct type_query *type_query){ continue_download(type_query, "");}static voidtp_show_header(struct type_query *type_query){ cached_header_dialog(type_query->ses, type_query->cached); done_type_query(type_query);}/* FIXME: We need to modify this function to take frame data instead, as we * want to use this function for frames as well (now, when frame has content * type text/plain, it is ignored and displayed as HTML). */static voidtp_display(struct type_query *type_query){ struct view_state *vs; struct session *ses = type_query->ses; struct uri *loading_uri = ses->loading_uri; unsigned char *target_frame = ses->task.target_frame; ses->loading_uri = type_query->uri; ses->task.target_frame = type_query->target_frame; vs = ses_forward(ses, type_query->frame); if (vs) vs->plain = 1; ses->loading_uri = loading_uri; ses->task.target_frame = target_frame; if (!type_query->frame) { struct download *old = &type_query->download; struct download *new = &cur_loc(ses)->download; new->callback = (void (*)(struct download *, void *)) doc_loading_callback; new->data = ses; if (is_in_progress_state(old->state)) change_connection(old, new, PRI_MAIN, 0); else new->state = old->state; } display_timer(ses); done_type_query(type_query);}static voiddo_type_query(struct type_query *type_query, unsigned char *ct, struct mime_handler *handler){ struct string filename; mem_free_set(&type_query->external_handler, NULL); if (handler) { type_query->external_handler = stracpy(handler->program); type_query->external_handler_flags = handler->block; if (!handler->ask) { tp_open(type_query); return; } } if (init_string(&filename)) add_mime_filename_to_string(&filename, type_query->uri); /* Let's make the filename pretty for display & save */ decode_uri_string_for_display(&filename); if (!handler) { if (!get_cmd_opt_bool("anonymous")) { msg_box(type_query->ses->tab->term, NULL, MSGBOX_FREE_TEXT, N_("Unknown type"), ALIGN_CENTER, msg_text(type_query->ses->tab->term, N_("Would you like to " "save the file '%s' (type: %s) " "or display it?"), filename.source, ct), type_query, 4, N_("Sa~ve"), tp_save, B_ENTER, N_("~Display"), tp_display, 0, N_("Show ~header"), tp_show_header, 0, N_("~Cancel"), tp_cancel, B_ESC); } else { msg_box(type_query->ses->tab->term, NULL, MSGBOX_FREE_TEXT, N_("Unknown type"), ALIGN_CENTER, msg_text(type_query->ses->tab->term, N_("Would you like to " "display the file '%s' (type: %s)?"), filename.source, ct), type_query, 3, N_("~Display"), tp_display, B_ENTER, N_("Show ~header"), tp_show_header, 0, N_("~Cancel"), tp_cancel, B_ESC); } } else { unsigned char *description = handler->description; unsigned char *desc_sep = (*description) ? "; " : ""; if (!get_cmd_opt_bool("anonymous")) { /* TODO: Improve the dialog to let the user correct the * used program. */ msg_box(type_query->ses->tab->term, NULL, MSGBOX_FREE_TEXT, N_("What to do?"), ALIGN_CENTER, msg_text(type_query->ses->tab->term, N_("Would you like to " "open the file '%s' (type: %s%s%s)\n" "with '%s', save it or display it?"), filename.source, ct, desc_sep, description, handler->program), type_query, 5, N_("~Open"), tp_open, B_ENTER, N_("Sa~ve"), tp_save, 0, N_("~Display"), tp_display, 0, N_("Show ~header"), tp_show_header, 0, N_("~Cancel"), tp_cancel, B_ESC); } else { msg_box(type_query->ses->tab->term, NULL, MSGBOX_FREE_TEXT, N_("What to do?"), ALIGN_CENTER, msg_text(type_query->ses->tab->term, N_("Would you like to " "open the file '%s' (type: %s%s%s)\n" "with '%s', or display it?"), filename.source, ct, desc_sep, description, handler->program), type_query, 4, N_("~Open"), tp_open, B_ENTER, N_("~Display"), tp_display, 0, N_("Show ~header"), tp_show_header, 0, N_("~Cancel"), tp_cancel, B_ESC); } } done_string(&filename);}struct { unsigned char *type; unsigned int plain:1;} static known_types[] = { { "text/html", 0 }, { "application/xhtml+xml", 0 }, /* RFC 3236 */ { "text/plain", 1 }, { NULL, 1 },};intses_chktype(struct session *ses, struct download *loading, struct cache_entry *cached, int frame){ struct mime_handler *handler; struct view_state *vs; struct type_query *type_query; unsigned char *ctype = get_content_type(cached); int plaintext = 1; int ret = 0; int xwin, i; if (!ctype || !*ctype) goto plaintext_follow; for (i = 0; known_types[i].type; i++) { if (strcasecmp(ctype, known_types[i].type)) continue; plaintext = known_types[i].plain; goto plaintext_follow; } xwin = ses->tab->term->environment & ENV_XWIN; handler = get_mime_type_handler(ctype, xwin); if (!handler && strlen(ctype) >= 4 && !strncasecmp(ctype, "text", 4)) goto plaintext_follow; type_query = init_type_query(ses, loading, cached); if (type_query) { ret = 1; do_type_query(type_query, ctype, handler); } mem_free_if(handler); return ret;plaintext_follow: vs = ses_forward(ses, frame); if (vs) vs->plain = plaintext; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -