📄 file.c
字号:
/* Internal "file" protocol implementation *//* $Id: file.c,v 1.185.2.4 2005/04/06 09:27:40 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h> /* OS/2 needs this after sys/types.h */#ifdef HAVE_FCNTL_H#include <fcntl.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "elinks.h"#include "cache/cache.h"#include "config/options.h"#include "encoding/encoding.h"#include "intl/gettext/libintl.h"#include "modules/module.h"#include "protocol/file/cgi.h"#include "protocol/file/file.h"#include "protocol/uri.h"#include "sched/connection.h"#include "util/conv.h"#include "util/file.h"#include "util/memory.h"#include "util/string.h"static struct option_info file_options[] = { INIT_OPT_TREE("protocol", N_("Local files"), "file", 0, N_("Options specific to local browsing.")),#ifdef CONFIG_CGI INIT_OPT_TREE("protocol.file", N_("Local CGI"), "cgi", 0, N_("Local CGI specific options.")), INIT_OPT_STRING("protocol.file.cgi", N_("Path"), "path", 0, "", N_("Colon separated list of directories, where CGI scripts are stored.")), INIT_OPT_BOOL("protocol.file.cgi", N_("Allow local CGI"), "policy", 0, 0, N_("Whether to execute local CGI scripts.")),#endif /* CONFIG_CGI */ INIT_OPT_BOOL("protocol.file", N_("Allow reading special files"), "allow_special_files", 0, 0, N_("Whether to allow reading from non-regular files.\n" "Note this can be dangerous; reading /dev/urandom or\n" "/dev/zero can ruin your day!")), INIT_OPT_BOOL("protocol.file", N_("Show hidden files in directory listing"), "show_hidden_files", 0, 1, N_("When set to false, files with name starting with a dot will be\n" "hidden in local directories listing.")), INIT_OPT_BOOL("protocol.file", N_("Try encoding extensions"), "try_encoding_extensions", 0, 1, N_("When set, if we can't open a file named 'filename', we'll try\n" "to open 'filename' with some encoding extension appended\n" "(ie. 'filename.gz'); it depends on the supported encodings.")), NULL_OPTION_INFO,};struct module file_protocol_module = struct_module( /* name: */ N_("File"), /* options: */ file_options, /* hooks: */ NULL, /* submodules: */ NULL, /* data: */ NULL, /* init: */ NULL, /* done: */ NULL);/* Directory listing *//* Based on the @entry attributes and file-/dir-/linkname is added to the @data * fragment. */static inline voidadd_dir_entry(struct directory_entry *entry, struct string *page, int pathlen, unsigned char *dircolor){ unsigned char *lnk = NULL; struct string html_encoded_name; struct string uri_encoded_name; if (!init_string(&html_encoded_name)) return; if (!init_string(&uri_encoded_name)) { done_string(&html_encoded_name); return; } encode_uri_string(&uri_encoded_name, entry->name + pathlen, 1); add_html_to_string(&html_encoded_name, entry->name + pathlen, strlen(entry->name) - pathlen); /* add_to_string(&fragment, &fragmentlen, " "); */ add_html_to_string(page, entry->attrib, strlen(entry->attrib)); add_to_string(page, "<a href=\""); add_string_to_string(page, &uri_encoded_name); if (entry->attrib[0] == 'd') { add_char_to_string(page, '/');#ifdef FS_UNIX_SOFTLINKS } else if (entry->attrib[0] == 'l') { struct stat st; unsigned char buf[MAX_STR_LEN]; int readlen = readlink(entry->name, buf, MAX_STR_LEN); if (readlen > 0 && readlen != MAX_STR_LEN) { buf[readlen] = '\0'; lnk = straconcat(" -> ", buf, NULL); } if (!stat(entry->name, &st) && S_ISDIR(st.st_mode)) add_char_to_string(page, '/');#endif } add_to_string(page, "\">"); if (entry->attrib[0] == 'd' && *dircolor) { /* The <b> is for the case when use_document_colors is off. */ string_concat(page, "<font color=\"", dircolor, "\"><b>", NULL); } add_string_to_string(page, &html_encoded_name); done_string(&uri_encoded_name); done_string(&html_encoded_name); if (entry->attrib[0] == 'd' && *dircolor) { add_to_string(page, "</b></font>"); } add_to_string(page, "</a>"); if (lnk) { add_html_to_string(page, lnk, strlen(lnk)); mem_free(lnk); } add_char_to_string(page, '\n');}/* First information such as permissions is gathered for each directory entry. * Finally the sorted entries are added to the @data->fragment one by one. */static inline voidadd_dir_entries(struct directory_entry *entries, unsigned char *dirpath, struct string *page){ unsigned char dircolor[8]; int dirpathlen = strlen(dirpath); int i; /* Setup @dircolor so it's easy to check if we should color dirs. */ if (get_opt_bool("document.browse.links.color_dirs")) { color_to_string(get_opt_color("document.colors.dirs"), (unsigned char *) &dircolor); } else { dircolor[0] = 0; } for (i = 0; entries[i].name; i++) { add_dir_entry(&entries[i], page, dirpathlen, dircolor); mem_free(entries[i].attrib); mem_free(entries[i].name); } /* We may have allocated space for entries but added none. */ mem_free_if(entries);}/* Generates a HTML page listing the content of @directory with the path * @dirpath. *//* Returns a connection state. S_OK if all is well. */static inline enum connection_statelist_directory(unsigned char *dirpath, struct string *page){ int show_hidden_files = get_opt_bool("protocol.file.show_hidden_files"); unsigned char *slash = dirpath; unsigned char *pslash = ++slash; struct directory_entry *entries; errno = 0; entries = get_directory_entries(dirpath, show_hidden_files); if (!entries) { if (errno) return -errno; return S_OUT_OF_MEM; } if (!init_string(page)) return S_OUT_OF_MEM; add_to_string(page, "<html>\n<head><title>"); add_html_to_string(page, dirpath, strlen(dirpath)); add_to_string(page, "</title>\n<base href=\""); encode_uri_string(page, dirpath, strlen(dirpath)); add_to_string(page, "\" />\n</head>\n<body>\n<h2>Directory /"); /* Make the directory path with links to each subdir. */ while ((slash = strchr(slash, '/'))) { *slash = 0; add_to_string(page, "<a href=\""); /* FIXME: htmlesc? At least we should escape quotes. --pasky */ add_to_string(page, dirpath); add_to_string(page, "/\">"); add_html_to_string(page, pslash, strlen(pslash)); add_to_string(page, "</a>/"); *slash = '/'; pslash = ++slash; } add_to_string(page, "</h2>\n<pre>"); add_dir_entries(entries, dirpath, page); add_to_string(page, "</pre>\n<hr>\n</body>\n</html>\n"); return S_OK;}/* To reduce redundant error handling code [calls to abort_conn_with_state()] * most of the function is build around conditions that will assign the error * code to @state if anything goes wrong. The rest of the function will then just * do the necessary cleanups. If all works out we end up with @state being S_OK * resulting in a cache entry being created with the fragment data generated by * either reading the file content or listing a directory. */voidfile_protocol_handler(struct connection *connection){ unsigned char *redirect_location = NULL; struct string page, name; enum connection_state state; unsigned char *type = NULL; if (get_cmd_opt_bool("anonymous")) { /* FIXME: Better connection_state ;-) */ abort_conn_with_state(connection, S_BAD_URL); return; }#ifdef CONFIG_CGI if (!execute_cgi(connection)) return;#endif /* CONFIG_CGI */ /* This function works on already simplified file-scheme URI pre-chewed * by transform_file_url(). By now, the function contains no hostname * part anymore, possibly relative path is converted to an absolute one * and uri->data is just the final path to file/dir we should try to * show. */ if (!init_string(&name) || !add_uri_to_string(&name, connection->uri, URI_PATH)) { done_string(&name); abort_conn_with_state(connection, S_OUT_OF_MEM); return; } decode_uri_string(&name); if (file_is_dir(name.source)) { /* In order for global history and directory listing to * function properly the directory url must end with a * directory separator. */ if (name.source[0] && !dir_sep(name.source[name.length - 1])) { redirect_location = "/"; state = S_OK; } else { state = list_directory(name.source, &page); type = "text/html"; } } else { state = read_encoded_file(&name, &page); /* FIXME: If state is now S_ENCODE_ERROR we should try loading * the file undecoded. --jonas */ } done_string(&name); if (state == S_OK) { struct cache_entry *cached; /* Try to add fragment data to the connection cache if either * file reading or directory listing worked out ok. */ cached = connection->cached = get_cache_entry(connection->uri); if (!connection->cached) { if (!redirect_location) done_string(&page); state = S_OUT_OF_MEM; } else if (redirect_location) { if (!redirect_cache(cached, redirect_location, 1, 0)) state = S_OUT_OF_MEM; } else { if (!cached->content_type) { unsigned char *ctype = null_or_stracpy(type); /* Not so gracefully handle failed memory * allocation. */ if (type && !ctype) state = S_OUT_OF_MEM; else cached->incomplete = 0; /* Setup file read or directory listing for * viewing. */ mem_free_set(&cached->content_type, ctype); } else { cached->incomplete = 0; } add_fragment(cached, 0, page.source, page.length); truncate_entry(cached, page.length, 1); done_string(&page); } } abort_conn_with_state(connection, state);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -