⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 download.c

📁 一个很有名的浏览器
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Downloads managment *//* $Id: download.c,v 1.338.2.9 2005/05/13 20:06:01 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#ifdef HAVE_SYS_CYGWIN_H#include <sys/cygwin.h>#endif#include <sys/types.h>#ifdef HAVE_FCNTL_H#include <fcntl.h> /* OS/2 needs this after sys/types.h */#endif#include <sys/stat.h>#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include <utime.h>#include "elinks.h"#include "bfu/dialog.h"#include "cache/cache.h"#include "config/options.h"#include "dialogs/document.h"#include "dialogs/download.h"#include "dialogs/menu.h"#include "intl/gettext/libintl.h"#include "mime/mime.h"#include "osdep/osdep.h"#include "protocol/date.h"#include "protocol/protocol.h"#include "protocol/uri.h"#include "sched/connection.h"#include "sched/download.h"#include "sched/error.h"#include "sched/history.h"#include "sched/location.h"#include "sched/session.h"#include "sched/task.h"#include "terminal/draw.h"#include "terminal/screen.h"#include "terminal/terminal.h"#include "util/conv.h"#include "util/error.h"#include "util/file.h"#include "util/lists.h"#include "util/memlist.h"#include "util/memory.h"#include "util/object.h"#include "util/string.h"#include "util/ttime.h"/* TODO: tp_*() should be in separate file, I guess? --pasky */INIT_LIST_HEAD(downloads);intare_there_downloads(void){	struct file_download *file_download;	foreach (file_download, downloads)		if (!file_download->external_handler)			return 1;	return 0;}static void download_data(struct download *download, struct file_download *file_download);static struct file_download *init_file_download(struct uri *uri, struct session *ses, unsigned char *file, int fd){	struct file_download *file_download;	file_download = mem_calloc(1, sizeof(*file_download));	if (!file_download) return NULL;	/* Actually we could allow fragments in the URI and just change all the	 * places that compares and shows the URI, but for now it is much	 * easier this way. */	file_download->uri = get_composed_uri(uri, URI_BASE);	if (!file_download->uri) {		mem_free(file_download);		return NULL;	}	file_download->box_item = add_listbox_leaf(&download_browser, NULL,						   file_download);	file_download->file = file;	file_download->handle = fd;	file_download->download.callback = (void (*)(struct download *, void *)) download_data;	file_download->download.data = file_download;	file_download->ses = ses;	/* The tab may be closed, but we will still want to ie. open the	 * handler on that terminal. */	file_download->term = ses->tab->term;	object_nolock(file_download, "file_download"); /* Debugging purpose. */	add_to_list(downloads, file_download);	return file_download;}voidabort_download(struct file_download *file_download){#if 0	/* When hacking to cleanup the download code, remove lots of duplicated	 * code and implement stuff from bug 435 we should reintroduce this	 * assertion. Currently it will trigger often and shows that the	 * download dialog code potentially could access free()d memory. */	assert(!is_object_used(file_download));#endif	if (file_download->box_item)		done_listbox_item(&download_browser, file_download->box_item);	if (file_download->dlg_data)		cancel_dialog(file_download->dlg_data, NULL);	if (is_in_progress_state(file_download->download.state))		change_connection(&file_download->download, NULL, PRI_CANCEL,				  file_download->stop);	if (file_download->uri) done_uri(file_download->uri);	if (file_download->handle != -1) {		prealloc_truncate(file_download->handle,				  file_download->last_pos);		close(file_download->handle);	}	mem_free_if(file_download->external_handler);	if (file_download->file) {		if (file_download->delete) unlink(file_download->file);		mem_free(file_download->file);	}	del_from_list(file_download);	mem_free(file_download);}static voidkill_downloads_to_file(unsigned char *file){	struct file_download *file_download;	foreach (file_download, downloads) {		if (strcmp(file_download->file, file))			continue;		file_download = file_download->prev;		abort_download(file_download->next);	}}voidabort_all_downloads(void){	while (!list_empty(downloads))		abort_download(downloads.next);}voiddestroy_downloads(struct session *ses){	struct file_download *file_download, *next;	struct session *s;	/* We are supposed to blat all downloads to external handlers belonging	 * to @ses, but we will refuse to do so if there is another session	 * bound to this terminal. That looks like the reasonable thing to do,	 * fulfilling the principle of least astonishment. */	foreach (s, sessions) {		if (s == ses || s->tab->term != ses->tab->term)			continue;		foreach (file_download, downloads) {			if (file_download->ses != ses)				continue;			file_download->ses = s;		}		return;	}	foreachsafe (file_download, next, downloads) {		if (file_download->ses != ses)			continue;		if (!file_download->external_handler) {			file_download->ses = NULL;			continue;		}		abort_download(file_download);	}}static voiddownload_error_dialog(struct file_download *file_download, int saved_errno){	unsigned char *emsg = (unsigned char *) strerror(saved_errno);	struct session *ses = file_download->ses;	struct terminal *term = file_download->term;	if (!ses) return;	info_box(term, MSGBOX_FREE_TEXT,		 N_("Download error"), ALIGN_CENTER,		 msg_text(term, N_("Could not create file '%s':\n%s"),			  file_download->file, emsg));}static intwrite_cache_entry_to_file(struct cache_entry *cached, struct file_download *file_download){	struct fragment *frag;	if (file_download->download.progress && file_download->download.progress->seek) {		file_download->last_pos = file_download->download.progress->seek;		file_download->download.progress->seek = 0;		/* This is exclusive with the prealloc, thus we can perform		 * this in front of that thing safely. */		if (lseek(file_download->handle, file_download->last_pos, SEEK_SET) < 0) {			download_error_dialog(file_download, errno);			return 0;		}	}	foreach (frag, cached->frag) {		int remain = file_download->last_pos - frag->offset;		int *h = &file_download->handle;		int w;		if (remain < 0 || frag->length <= remain)			continue;#ifdef USE_OPEN_PREALLOC		if (!file_download->last_pos		    && (!file_download->download.prg			|| file_download->download.progress->size > 0)) {			close(*h);			*h = open_prealloc(file_download->file,					   O_CREAT|O_WRONLY|O_TRUNC,					   0666,					   file_download->download.prg					   ? file_download->download.progress->size					   : cached->length);			if (*h == -1) {				download_error_dialog(file_download, errno);				return 0;			}			set_bin(*h);		}#endif		w = safe_write(*h, frag->data + remain, frag->length - remain);		if (w == -1) {			download_error_dialog(file_download, errno);			return 0;		}		file_download->last_pos += w;	}	return 1;}static voidabort_download_and_beep(struct file_download *file_download, struct terminal *term){	if (term && get_opt_int("document.download.notify_bell")		    + file_download->notify >= 2) {		beep_terminal(term);	}	abort_download(file_download);}static voiddownload_data_store(struct download *download, struct file_download *file_download){	struct terminal *term = file_download->term;	if (!term) {		/* No term here, so no beep. --Zas */		abort_download(file_download);		return;	}	if (is_in_progress_state(download->state)) {		if (file_download->dlg_data)			redraw_dialog(file_download->dlg_data, 1);		return;	}	if (download->state != S_OK) {		unsigned char *errmsg = get_err_msg(download->state, term);		unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);		if (url) {			info_box(term, MSGBOX_FREE_TEXT,				 N_("Download error"), ALIGN_CENTER,				 msg_text(term, N_("Error downloading %s:\n\n%s"),					  url, errmsg));			mem_free(url);		}		abort_download_and_beep(file_download, term);		return;	}	if (file_download->external_handler) {		prealloc_truncate(file_download->handle,				  file_download->last_pos);		close(file_download->handle);		file_download->handle = -1;		exec_on_terminal(term, file_download->external_handler,				 file_download->file,				 !!file_download->external_handler_flags);		file_download->delete = 0;		abort_download_and_beep(file_download, term);		return;	}	if (file_download->notify) {		unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);		/* This is apparently a little racy. Deleting the box item will		 * update the download browser _after_ the notification dialog		 * has been drawn whereby it will be hidden. This should make		 * the download browser update before launcing any		 * notification. */		if (file_download->box_item) {			done_listbox_item(&download_browser, file_download->box_item);			file_download->box_item = NULL;		}		if (url) {			info_box(term, MSGBOX_FREE_TEXT,				 N_("Download"), ALIGN_CENTER,				 msg_text(term, N_("Download complete:\n%s"), url));			mem_free(url);		}	}	if (file_download->remotetime	    && get_opt_bool("document.download.set_original_time")) {		struct utimbuf foo;		foo.actime = foo.modtime = file_download->remotetime;		utime(file_download->file, &foo);	}	abort_download_and_beep(file_download, term);}static voiddownload_data(struct download *download, struct file_download *file_download){	struct cache_entry *cached = download->cached;	if (!cached || is_in_queued_state(download->state)) {		download_data_store(download, file_download);		return;	}	if (cached->last_modified)		file_download->remotetime = parse_date(&cached->last_modified, NULL, 0, 1);	if (cached->redirect && file_download->redirect_cnt++ < MAX_REDIRECTS) {		if (is_in_progress_state(download->state))			change_connection(&file_download->download, NULL, PRI_CANCEL, 0);		assertm(compare_uri(cached->uri, file_download->uri, 0),			"Redirecting using bad base URI");		done_uri(file_download->uri);		file_download->uri = get_uri_reference(cached->redirect);		file_download->download.state = S_WAIT_REDIR;		if (file_download->dlg_data)			redraw_dialog(file_download->dlg_data, 1);		load_uri(file_download->uri, cached->uri, &file_download->download,			 PRI_DOWNLOAD, CACHE_MODE_NORMAL,			 download->progress ? download->progress->start : 0);		return;	}	if (!write_cache_entry_to_file(cached, file_download)) {		detach_connection(download, file_download->last_pos);		abort_download(file_download);		return;	}	detach_connection(download, file_download->last_pos);	download_data_store(download, file_download);}/* XXX: We assume that resume is everytime zero in lun's callbacks. */struct lun_hop {	struct terminal *term;	unsigned char *ofile, *file;	void (*callback)(struct terminal *, unsigned char *, void *, int);	void *data;};static voidlun_alternate(struct lun_hop *lun_hop){	lun_hop->callback(lun_hop->term, lun_hop->file, lun_hop->data, 0);	mem_free_if(lun_hop->ofile);	mem_free(lun_hop);}static voidlun_overwrite(struct lun_hop *lun_hop){	lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, 0);	mem_free_if(lun_hop->file);	mem_free(lun_hop);}static voidlun_resume(struct lun_hop *lun_hop){	lun_hop->callback(lun_hop->term, lun_hop->ofile, lun_hop->data, 1);	mem_free_if(lun_hop->file);	mem_free(lun_hop);}static voidlun_cancel(struct lun_hop *lun_hop){	lun_hop->callback(lun_hop->term, NULL, lun_hop->data, 0);	mem_free_if(lun_hop->ofile);	mem_free_if(lun_hop->file);	mem_free(lun_hop);}static voidlookup_unique_name(struct terminal *term, unsigned char *ofile, int resume,		   void (*callback)(struct terminal *, unsigned char *, void *, int),		   void *data){	struct lun_hop *lun_hop;	unsigned char *file;	int overwrite;	ofile = expand_tilde(ofile);	/* Minor code duplication to prevent useless call to get_opt_int()	 * if possible. --Zas */	if (resume) {		callback(term, ofile, data, resume);		return;	}	/* !overwrite means always silently overwrite, which may be admitelly	 * indeed a little confusing ;-) */	overwrite = get_opt_int("document.download.overwrite");	if (!overwrite) {		/* Nothing special to do... */		callback(term, ofile, data, resume);		return;	}	/* Check if file is a directory, and use a default name if it's the	 * case. */	if (file_is_dir(ofile)) {		info_box(term, MSGBOX_FREE_TEXT,			 N_("Download error"), ALIGN_CENTER,			 msg_text(term, N_("'%s' is a directory."),				  ofile));		mem_free(ofile);		callback(term, NULL, data, 0);		return;	}	/* Check if the file already exists (file != ofile). */	file = get_unique_name(ofile);	if (!file || overwrite == 1 || file == ofile) {		/* Still nothing special to do... */		if (file != ofile) mem_free(ofile);		callback(term, file, data, 0);		return;	}	/* overwrite == 2 (ask) and file != ofile (=> original file already	 * exists) */	lun_hop = mem_calloc(1, sizeof(*lun_hop));	if (!lun_hop) {		if (file != ofile) mem_free(file);		mem_free(ofile);		callback(term, NULL, data, 0);		return;	}	lun_hop->term = term;	lun_hop->ofile = ofile;	lun_hop->file = (file != ofile) ? file : stracpy(ofile);	lun_hop->callback = callback;	lun_hop->data = data;	msg_box(term, NULL, MSGBOX_FREE_TEXT,		N_("File exists"), ALIGN_CENTER,		msg_text(term, N_("This file already exists:\n"			"%s\n\n"			"The alternative filename is:\n"			"%s"),			empty_string_or_(lun_hop->ofile),			empty_string_or_(file)),		lun_hop, 4,		N_("Sa~ve under the alternative name"), lun_alternate, B_ENTER,		N_("~Overwrite the original file"), lun_overwrite, 0,		N_("~Resume download of the original file"), lun_resume, 0,		N_("~Cancel"), lun_cancel, B_ESC);}struct cdf_hop {	unsigned char **real_file;	int safe;	void (*callback)(struct terminal *, int, void *, int);	void *data;};static voidcreate_download_file_do(struct terminal *term, unsigned char *file, void *data,			int resume){	struct cdf_hop *cdf_hop = data;	unsigned char *wd;	int h = -1;	int saved_errno;#ifdef NO_FILE_SECURITY

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -