SMJS: Use JS_THIS_OBJECT and JS_CALLEE, not argv[-1] and argv[-2]
[elinks/elinks-j605.git] / src / protocol / file / file.c
blob0adaeff15363fcbffe2016698e0d817be45f569c
1 /* Internal "file" protocol implementation */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
13 #ifdef HAVE_FCNTL_H
14 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
15 #endif
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
20 #include "elinks.h"
22 #include "cache/cache.h"
23 #include "config/options.h"
24 #include "encoding/encoding.h"
25 #include "intl/gettext/libintl.h"
26 #include "main/module.h"
27 #include "network/connection.h"
28 #include "network/socket.h"
29 #include "osdep/osdep.h"
30 #include "protocol/common.h"
31 #include "protocol/file/cgi.h"
32 #include "protocol/file/file.h"
33 #include "protocol/http/http.h"
34 #include "protocol/uri.h"
35 #include "util/conv.h"
36 #include "util/file.h"
37 #include "util/memory.h"
38 #include "util/string.h"
41 static union option_info file_options[] = {
42 INIT_OPT_TREE("protocol", N_("Local files"),
43 "file", 0,
44 N_("Options specific to local browsing.")),
46 INIT_OPT_BOOL("protocol.file", N_("Allow reading special files"),
47 "allow_special_files", 0, 0,
48 N_("Whether to allow reading from non-regular files. "
49 "Note this can be dangerous; reading /dev/urandom or "
50 "/dev/zero can ruin your day!")),
52 INIT_OPT_BOOL("protocol.file", N_("Show hidden files in directory listing"),
53 "show_hidden_files", 0, 1,
54 N_("When set to false, files with name starting with a dot "
55 "will be hidden in local directory listings.")),
57 INIT_OPT_BOOL("protocol.file", N_("Try encoding extensions"),
58 "try_encoding_extensions", 0, 1,
59 N_("When set, if we can't open a file named 'filename', "
60 "we'll try to open 'filename' with some encoding extension "
61 "appended (ie. 'filename.gz'); it depends on the supported "
62 "encodings.")),
64 NULL_OPTION_INFO,
67 struct module file_protocol_module = struct_module(
68 /* name: */ N_("File"),
69 /* options: */ file_options,
70 /* hooks: */ NULL,
71 /* submodules: */ NULL,
72 /* data: */ NULL,
73 /* init: */ NULL,
74 /* done: */ NULL
78 /* Directory listing */
80 /* Based on the @entry attributes and file-/dir-/linkname is added to the @data
81 * fragment. All the strings are in the system charset. */
82 static inline void
83 add_dir_entry(struct directory_entry *entry, struct string *page,
84 int pathlen, unsigned char *dircolor)
86 unsigned char *lnk = NULL;
87 struct string html_encoded_name;
88 struct string uri_encoded_name;
90 if (!init_string(&html_encoded_name)) return;
91 if (!init_string(&uri_encoded_name)) {
92 done_string(&html_encoded_name);
93 return;
96 encode_uri_string(&uri_encoded_name, entry->name + pathlen, -1, 1);
97 add_html_to_string(&html_encoded_name, entry->name + pathlen,
98 strlen(entry->name) - pathlen);
100 /* add_to_string(&fragment, &fragmentlen, " "); */
101 add_html_to_string(page, entry->attrib, strlen(entry->attrib));
102 add_to_string(page, "<a href=\"");
103 add_string_to_string(page, &uri_encoded_name);
105 if (entry->attrib[0] == 'd') {
106 add_char_to_string(page, '/');
108 #ifdef FS_UNIX_SOFTLINKS
109 } else if (entry->attrib[0] == 'l') {
110 struct stat st;
111 unsigned char buf[MAX_STR_LEN];
112 int readlen = readlink(entry->name, buf, MAX_STR_LEN);
114 if (readlen > 0 && readlen != MAX_STR_LEN) {
115 buf[readlen] = '\0';
116 lnk = straconcat(" -> ", buf, (unsigned char *) NULL);
119 if (!stat(entry->name, &st) && S_ISDIR(st.st_mode))
120 add_char_to_string(page, '/');
121 #endif
124 add_to_string(page, "\">");
126 if (entry->attrib[0] == 'd' && *dircolor) {
127 /* The <b> is for the case when use_document_colors is off. */
128 string_concat(page, "<font color=\"", dircolor, "\"><b>",
129 (unsigned char *) NULL);
132 add_string_to_string(page, &html_encoded_name);
133 done_string(&uri_encoded_name);
134 done_string(&html_encoded_name);
136 if (entry->attrib[0] == 'd' && *dircolor) {
137 add_to_string(page, "</b></font>");
140 add_to_string(page, "</a>");
141 if (lnk) {
142 add_html_to_string(page, lnk, strlen(lnk));
143 mem_free(lnk);
146 add_char_to_string(page, '\n');
149 /* First information such as permissions is gathered for each directory entry.
150 * Finally the sorted entries are added to the @data->fragment one by one. */
151 static inline void
152 add_dir_entries(struct directory_entry *entries, unsigned char *dirpath,
153 struct string *page)
155 unsigned char dircolor[8];
156 int dirpathlen = strlen(dirpath);
157 int i;
159 /* Setup @dircolor so it's easy to check if we should color dirs. */
160 if (get_opt_bool("document.browse.links.color_dirs", NULL)) {
161 color_to_string(get_opt_color("document.colors.dirs", NULL),
162 (unsigned char *) &dircolor);
163 } else {
164 dircolor[0] = 0;
167 for (i = 0; entries[i].name; i++) {
168 add_dir_entry(&entries[i], page, dirpathlen, dircolor);
169 mem_free(entries[i].attrib);
170 mem_free(entries[i].name);
173 /* We may have allocated space for entries but added none. */
174 mem_free_if(entries);
177 /* Generates an HTML page listing the content of @directory with the path
178 * @dirpath. */
179 /* Returns a connection state. S_OK if all is well. */
180 static inline struct connection_state
181 list_directory(struct connection *conn, unsigned char *dirpath,
182 struct string *page)
184 int show_hidden_files = get_opt_bool("protocol.file.show_hidden_files",
185 NULL);
186 struct directory_entry *entries;
187 struct connection_state state;
189 errno = 0;
190 entries = get_directory_entries(dirpath, show_hidden_files);
191 if (!entries) {
192 if (errno) return connection_state_for_errno(errno);
193 return connection_state(S_OUT_OF_MEM);
196 state = init_directory_listing(page, conn->uri);
197 if (!is_in_state(state, S_OK))
198 return connection_state(S_OUT_OF_MEM);
200 add_dir_entries(entries, dirpath, page);
202 if (!add_to_string(page, "</pre>\n<hr/>\n</body>\n</html>\n")) {
203 done_string(page);
204 return connection_state(S_OUT_OF_MEM);
207 return connection_state(S_OK);
210 static void
211 check_if_closed(struct socket *socket, struct read_buffer *rb)
213 if (socket->state == SOCKET_CLOSED) {
214 abort_connection(socket->conn, connection_state(S_OK));
215 return;
217 http_got_header(socket, rb);
220 static void
221 read_from_stdin(struct connection *conn)
223 struct read_buffer *rb = alloc_read_buffer(conn->socket);
225 if (!rb) return;
227 memcpy(rb->data, "HTTP/1.0 200 OK\r\n\r\n", 19);
228 rb->length = 19;
229 rb->freespace -= 19;
231 conn->unrestartable = 1;
233 conn->socket->state = SOCKET_END_ONCLOSE;
234 read_from_socket(conn->socket, rb, connection_state(S_SENT),
235 check_if_closed);
238 /* To reduce redundant error handling code [calls to abort_connection()]
239 * most of the function is build around conditions that will assign the error
240 * code to @state if anything goes wrong. The rest of the function will then just
241 * do the necessary cleanups. If all works out we end up with @state being S_OK
242 * resulting in a cache entry being created with the fragment data generated by
243 * either reading the file content or listing a directory. */
244 void
245 file_protocol_handler(struct connection *connection)
247 unsigned char *redirect_location = NULL;
248 struct string page, name;
249 struct connection_state state;
250 int set_dir_content_type = 0;
252 if (get_cmd_opt_bool("anonymous")) {
253 if (strcmp(connection->uri->string, "file:///dev/stdin")
254 || isatty(STDIN_FILENO)) {
255 abort_connection(connection,
256 connection_state(S_FILE_ANONYMOUS));
257 return;
261 #ifdef CONFIG_CGI
262 if (!execute_cgi(connection)) return;
263 #endif /* CONFIG_CGI */
265 /* Treat /dev/stdin in special way */
266 if (!strcmp(connection->uri->string, "file:///dev/stdin")) {
267 int fd = open("/dev/stdin", O_RDONLY);
269 if (fd == -1) {
270 abort_connection(connection, connection_state(-errno));
271 return;
273 set_nonblocking_fd(fd);
274 if (!init_http_connection_info(connection, 1, 0, 1)) {
275 abort_connection(connection, connection_state(S_OUT_OF_MEM));
276 close(fd);
277 return;
279 connection->socket->fd = fd;
280 connection->data_socket->fd = -1;
281 read_from_stdin(connection);
282 return;
286 /* This function works on already simplified file-scheme URI pre-chewed
287 * by transform_file_url(). By now, the function contains no hostname
288 * part anymore, possibly relative path is converted to an absolute one
289 * and uri->data is just the final path to file/dir we should try to
290 * show. */
292 if (!init_string(&name)
293 || !add_uri_to_string(&name, connection->uri, URI_PATH)) {
294 done_string(&name);
295 abort_connection(connection, connection_state(S_OUT_OF_MEM));
296 return;
299 decode_uri_string(&name);
301 /* In Win32, file_is_dir seems to always return 0 if the name
302 * ends with a directory separator. */
303 if ((name.length > 0 && dir_sep(name.source[name.length - 1]))
304 || file_is_dir(name.source)) {
305 /* In order for global history and directory listing to
306 * function properly the directory url must end with a
307 * directory separator. */
308 if (name.source[0] && !dir_sep(name.source[name.length - 1])) {
309 redirect_location = STRING_DIR_SEP;
310 state = connection_state(S_OK);
311 } else {
312 state = list_directory(connection, name.source, &page);
313 set_dir_content_type = 1;
316 } else {
317 state = read_encoded_file(&name, &page);
318 /* FIXME: If state is now S_ENCODE_ERROR we should try loading
319 * the file undecoded. --jonas */
322 done_string(&name);
324 if (is_in_state(state, S_OK)) {
325 struct cache_entry *cached;
327 /* Try to add fragment data to the connection cache if either
328 * file reading or directory listing worked out ok. */
329 cached = connection->cached = get_cache_entry(connection->uri);
330 if (!connection->cached) {
331 if (!redirect_location) done_string(&page);
332 state = connection_state(S_OUT_OF_MEM);
334 } else if (redirect_location) {
335 if (!redirect_cache(cached, redirect_location, 1, 0))
336 state = connection_state(S_OUT_OF_MEM);
338 } else {
339 add_fragment(cached, 0, page.source, page.length);
340 connection->from += page.length;
342 if (!cached->head && set_dir_content_type) {
343 unsigned char *head;
345 /* If the system charset somehow
346 * changes after the directory listing
347 * has been generated, it should be
348 * parsed with the original charset. */
349 head = straconcat("\r\nContent-Type: text/html; charset=",
350 get_cp_mime_name(get_cp_index("System")),
351 "\r\n", (unsigned char *) NULL);
353 /* Not so gracefully handle failed memory
354 * allocation. */
355 if (!head)
356 state = connection_state(S_OUT_OF_MEM);
358 /* Setup directory listing for viewing. */
359 mem_free_set(&cached->head, head);
362 done_string(&page);
366 abort_connection(connection, state);