content encoding: New function accept_encoding_header.
[elinks.git] / src / protocol / fsp / fsp.c
blobefa6e2e42a039b808b0f61edf7dcca1e828e02e7
1 /* Internal FSP protocol implementation */
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE /* Needed for asprintf() */
5 #endif
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include <errno.h>
12 #include <fsplib.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #ifdef HAVE_FCNTL_H
17 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
18 #endif
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
23 #include "elinks.h"
25 #include "cache/cache.h"
26 #include "config/options.h"
27 #include "intl/gettext/libintl.h"
28 #include "main/module.h"
29 #include "main/select.h"
30 #include "network/connection.h"
31 #include "network/socket.h"
32 #include "osdep/osdep.h"
33 #include "protocol/auth/auth.h"
34 #include "protocol/common.h"
35 #include "protocol/protocol.h"
36 #include "protocol/fsp/fsp.h"
37 #include "protocol/uri.h"
38 #include "util/conv.h"
39 #include "util/memory.h"
40 #include "util/snprintf.h"
41 #include "util/string.h"
44 struct option_info fsp_options[] = {
45 INIT_OPT_TREE("protocol", N_("FSP"),
46 "fsp", 0,
47 N_("FSP specific options.")),
49 INIT_OPT_BOOL("protocol.fsp", N_("Sort entries"),
50 "sort", 0, 1,
51 N_("Whether to sort entries in directory listings.")),
53 NULL_OPTION_INFO,
56 struct module fsp_protocol_module = struct_module(
57 /* name: */ N_("FSP"),
58 /* options: */ fsp_options,
59 /* hooks: */ NULL,
60 /* submodules: */ NULL,
61 /* data: */ NULL,
62 /* init: */ NULL,
63 /* done: */ NULL
67 /* Because functions of fsplib block waiting for a response from the
68 * server, and ELinks wants non-blocking operations so that other
69 * connections and the user interface keep working, this FSP protocol
70 * module forks a child process for each FSP connection. The child
71 * process writes the results to two pipes, which the main ELinks
72 * process then reads in a non-blocking fashion. The child process
73 * gets these pipes as its stdout and stderr.
75 * - If an error occurs, the child process writes "text/x-error"
76 * without newline to stderr, and an error code and a newline to
77 * stdout. The error code is either from errno or a negated value
78 * from enum connection_state, e.g. -S_OUT_OF_MEM. In particular,
79 * EPERM causes the parent process to prompt for username and
80 * password. (In this, fsplib differs from libsmbclient, which uses
81 * EACCES if authentication fails.)
83 * - If the resource is a regular file, the child process writes the
84 * estimated length of the file (in bytes) and a newline to stderr,
85 * and the contents of the file to stdout.
87 * - If the resource is a directory, the child process writes
88 * "text/html" without newline to stderr, and an HTML rendering
89 * of the directory listing to stdout.
91 * The exit code of the child process also indicates whether an error
92 * occurred, but the parent process ignores it. */
94 /* FSP synchronous connection management (child process):
96 * The child process generally does not bother to free the memory it
97 * allocates. When the process exits, the operating system will free
98 * the memory anyway. There is no point in changing this, because the
99 * child process also inherits memory allocations from the parent
100 * process, and it would be very cumbersome to free those. */
102 /* FIXME: Although it is probably not so much an issue, check if writes to
103 * stdout fails for directory listing like we do for file fetching. */
105 static void
106 fsp_error(int error)
108 printf("%d\n", error);
109 fprintf(stderr, "text/x-error");
110 /* In principle, this should perhaps call fsp_close_session to
111 * make the server accept any key from the next client process
112 * at this IP address. That doesn't seem necessary though:
113 * fsplib uses various IPC schemes to synchronize the use of
114 * server-provided keys between client processes, so the next
115 * client process will probably be able to use the key saved
116 * by this one. */
117 exit(1);
120 static int
121 compare(const void *av, const void *bv)
123 const FSP_RDENTRY *a = av, *b = bv;
124 int res = ((b->type == FSP_RDTYPE_DIR) - (a->type == FSP_RDTYPE_DIR));
126 if (res)
127 return res;
128 return strcmp(a->name, b->name);
131 static void
132 display_entry(const FSP_RDENTRY *fentry, const unsigned char dircolor[])
134 struct string string;
136 /* fentry->name is a fixed-size array and is followed by other
137 * members; thus, if the name reported by the server does not
138 * fit in the array, fsplib must either truncate or reject it.
139 * If fsplib truncates the name, it does not document whether
140 * fentry->namlen is the original length or the truncated
141 * length. ELinks therefore ignores fentry->namlen and
142 * instead measures the length on its own. */
143 const size_t namelen = strlen(fentry->name);
145 if (!init_string(&string)) return;
146 add_format_to_string(&string, "%10d", fentry->size);
147 add_to_string(&string, "\t<a href=\"");
148 /* The result of encode_uri_string does not include '&' or '<'
149 * which could mess up the HTML. */
150 encode_uri_string(&string, fentry->name, namelen, 0);
151 if (fentry->type == FSP_RDTYPE_DIR) {
152 add_to_string(&string, "/\">");
153 if (*dircolor) {
154 add_to_string(&string, "<font color=\"");
155 add_to_string(&string, dircolor);
156 add_to_string(&string, "\"><b>");
158 add_html_to_string(&string, fentry->name, namelen);
159 if (*dircolor) {
160 add_to_string(&string, "</b></font>");
162 } else {
163 add_to_string(&string, "\">");
164 add_html_to_string(&string, fentry->name, namelen);
166 add_to_string(&string, "</a>");
167 puts(string.source);
168 done_string(&string);
171 static void
172 sort_and_display_entries(FSP_DIR *dir, const unsigned char dircolor[])
174 FSP_RDENTRY fentry, *fresult, *table = NULL;
175 int size = 0;
176 int i;
178 while (!fsp_readdir_native(dir, &fentry, &fresult)) {
179 FSP_RDENTRY *new_table;
181 if (!fresult) break;
182 if (!strcmp(fentry.name, "."))
183 continue;
184 new_table = mem_realloc(table, (size + 1) * sizeof(*table));
185 if (!new_table)
186 continue;
187 table = new_table;
188 copy_struct(&table[size], &fentry);
189 size++;
191 /* If size==0, then table==NULL. According to ISO/IEC 9899:1999
192 * 7.20.5p1, the NULL must not be given to qsort. */
193 if (size > 0)
194 qsort(table, size, sizeof(*table), compare);
196 for (i = 0; i < size; i++) {
197 display_entry(&table[i], dircolor);
201 static void
202 fsp_directory(FSP_SESSION *ses, struct uri *uri)
204 struct string buf;
205 FSP_DIR *dir;
206 unsigned char *data = get_uri_string(uri, URI_DATA);
207 unsigned char dircolor[8] = "";
209 if (!data)
210 fsp_error(-S_OUT_OF_MEM);
211 decode_uri(data);
212 if (init_directory_listing(&buf, uri) != S_OK)
213 fsp_error(-S_OUT_OF_MEM);
215 dir = fsp_opendir(ses, data);
216 if (!dir) fsp_error(errno);
218 fprintf(stderr, "text/html");
219 fclose(stderr);
221 puts(buf.source);
223 if (get_opt_bool("document.browse.links.color_dirs")) {
224 color_to_string(get_opt_color("document.colors.dirs"),
225 dircolor);
228 if (get_opt_bool("protocol.fsp.sort")) {
229 sort_and_display_entries(dir, dircolor);
230 } else {
231 FSP_RDENTRY fentry, *fresult;
233 while (!fsp_readdir_native(dir, &fentry, &fresult)) {
234 if (!fresult) break;
235 display_entry(&fentry, dircolor);
238 fsp_closedir(dir);
239 puts("</pre><hr/></body></html>");
240 fsp_close_session(ses);
241 exit(0);
244 #define READ_SIZE 4096
246 static void
247 do_fsp(struct connection *conn)
249 FSP_SESSION *ses;
250 struct stat sb;
251 struct uri *uri = conn->uri;
252 struct auth_entry *auth;
253 unsigned char *host = get_uri_string(uri, URI_HOST);
254 unsigned char *data = get_uri_string(uri, URI_DATA);
255 unsigned short port = (unsigned short)get_uri_port(uri);
256 unsigned char *password = NULL;
258 decode_uri(data);
259 if (uri->passwordlen) {
260 password = get_uri_string(uri, URI_PASSWORD);
261 } else {
262 auth = find_auth(uri);
263 if (auth) password = auth->password;
266 ses = fsp_open_session(host, port, password);
267 if (!ses) fsp_error(errno);
269 /* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS
270 * https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738
271 * If ELinks and fsplib are using different values of
272 * _FILE_OFFSET_BITS, then they get different definitions of
273 * struct stat, and the st_size stored by fsp_stat is
274 * typically not the same as the st_size read by ELinks.
275 * Fortunately, st_mode seems to have the same offset and size
276 * in both versions of struct stat.
278 * If all the bytes used by the 32-bit st_size are also used
279 * by the 64-bit st_size, then ELinks may be able to guess
280 * which ones they are, because the current version 2 of FSP
281 * supports only 32-bit file sizes in protocol packets. Begin
282 * by filling struct stat with 0xAA so that it's easier to
283 * detect which bytes fsp_stat has left unchanged. (Only
284 * sb.st_size really needs to be filled, but filling the rest
285 * too helps viewing the data with a debugger.) */
286 memset(&sb, 0xAA, sizeof(sb));
287 if (fsp_stat(ses, data, &sb)) fsp_error(errno);
289 if (S_ISDIR(sb.st_mode)) {
290 fsp_directory(ses, uri);
291 } else { /* regular file */
292 char buf[READ_SIZE];
293 FSP_FILE *file = fsp_fopen(ses, data, "r");
294 int r;
296 if (!file) {
297 fsp_error(errno);
300 #if SIZEOF_OFF_T >= 8
301 if (sb.st_size < 0 || sb.st_size > 0xFFFFFFFF) {
302 /* Probably a _FILE_OFFSET_BITS mismatch as
303 * described above. Try to detect which half
304 * of st_size is the real size. This may
305 * depend on the endianness of the processor
306 * and on the padding in struct stat. */
307 if ((sb.st_size & 0xFFFFFFFF00000000ULL) == 0xAAAAAAAA00000000ULL)
308 sb.st_size = sb.st_size & 0xFFFFFFFF;
309 else if ((sb.st_size & 0xFFFFFFFF) == 0xAAAAAAAA)
310 sb.st_size = (sb.st_size >> 32) & 0xFFFFFFFF;
311 else /* Can't figure it out. */
312 sb.st_size = 1;
314 #endif
316 /* Send filesize */
317 fprintf(stderr, "%" OFF_PRINT_FORMAT "\n",
318 (off_print_T) sb.st_size);
319 fclose(stderr);
321 while ((r = fsp_fread(buf, 1, READ_SIZE, file)) > 0)
322 if (safe_write(STDOUT_FILENO, buf, r) <= 0)
323 break;
325 fsp_fclose(file);
326 fsp_close_session(ses);
327 exit(0);
331 #undef READ_SIZE
335 /* FSP asynchronous connection management (parent process): */
337 /* Kill the current connection and ask for a username/password for the next
338 * try. */
339 static void
340 prompt_username_pw(struct connection *conn)
342 add_auth_entry(conn->uri, "FSP", NULL, NULL, 0);
343 abort_connection(conn, S_OK);
346 static void
347 fsp_got_error(struct socket *socket, struct read_buffer *rb)
349 int len = rb->length;
350 struct connection *conn = socket->conn;
351 int error;
353 if (len < 0) {
354 abort_connection(conn, -errno);
355 return;
358 /* There should be free space in the buffer, because
359 * @alloc_read_buffer allocated several kibibytes, and the
360 * child process wrote only an integer and a newline to the
361 * pipe. */
362 assert(rb->freespace >= 1);
363 if_assert_failed {
364 abort_connection(conn, S_INTERNAL);
365 return;
367 rb->data[len] = '\0';
368 error = atoi(rb->data);
369 kill_buffer_data(rb, len);
370 switch (error) {
371 case EPERM:
372 prompt_username_pw(conn);
373 break;
374 default:
375 abort_connection(conn, -error);
376 break;
380 static void
381 fsp_got_data(struct socket *socket, struct read_buffer *rb)
383 int len = rb->length;
384 struct connection *conn = socket->conn;
386 if (len < 0) {
387 abort_connection(conn, -errno);
388 return;
391 if (!len) {
392 abort_connection(conn, S_OK);
393 return;
396 socket->state = SOCKET_END_ONCLOSE;
397 conn->received += len;
398 if (add_fragment(conn->cached, conn->from, rb->data, len) == 1)
399 conn->tries = 0;
400 conn->from += len;
401 kill_buffer_data(rb, len);
403 read_from_socket(socket, rb, S_TRANS, fsp_got_data);
406 static void
407 fsp_got_header(struct socket *socket, struct read_buffer *rb)
409 struct connection *conn = socket->conn;
410 struct read_buffer *buf;
411 int error = 0;
413 conn->cached = get_cache_entry(conn->uri);
414 if (!conn->cached) {
415 /* Even though these are pipes rather than real
416 * sockets, call close_socket instead of close, to
417 * ensure that abort_connection won't try to close the
418 * file descriptors again. (Could we skip the calls
419 * and assume abort_connection will do them?) */
420 close_socket(socket);
421 close_socket(conn->data_socket);
422 abort_connection(conn, S_OUT_OF_MEM);
423 return;
425 socket->state = SOCKET_END_ONCLOSE;
427 if (rb->length > 0) {
428 unsigned char *ctype = memacpy(rb->data, rb->length);
430 if (ctype && *ctype) {
431 if (!strcmp(ctype, "text/x-error")) {
432 error = 1;
433 mem_free(ctype);
434 } else {
435 if (ctype[0] >= '0' && ctype[0] <= '9') {
436 #ifdef HAVE_ATOLL
437 conn->est_length = (off_t)atoll(ctype);
438 #else
439 conn->est_length = (off_t)atol(ctype);
440 #endif
441 mem_free(ctype);
443 /* avoid read from socket error */
444 if (!conn->est_length) {
445 abort_connection(conn, S_OK);
446 return;
449 else mem_free_set(&conn->cached->content_type, ctype);
451 } else {
452 mem_free_if(ctype);
456 buf = alloc_read_buffer(conn->data_socket);
457 if (!buf) {
458 close_socket(socket);
459 close_socket(conn->data_socket);
460 abort_connection(conn, S_OUT_OF_MEM);
461 return;
464 if (error) {
465 mem_free_set(&conn->cached->content_type, stracpy("text/html"));
466 read_from_socket(conn->data_socket, buf, S_CONN, fsp_got_error);
467 } else {
468 read_from_socket(conn->data_socket, buf, S_CONN, fsp_got_data);
473 void
474 fsp_protocol_handler(struct connection *conn)
476 int fsp_pipe[2] = { -1, -1 };
477 int header_pipe[2] = { -1, -1 };
478 pid_t cpid;
480 if (c_pipe(fsp_pipe) || c_pipe(header_pipe)) {
481 int s_errno = errno;
483 if (fsp_pipe[0] >= 0) close(fsp_pipe[0]);
484 if (fsp_pipe[1] >= 0) close(fsp_pipe[1]);
485 if (header_pipe[0] >= 0) close(header_pipe[0]);
486 if (header_pipe[1] >= 0) close(header_pipe[1]);
487 abort_connection(conn, -s_errno);
488 return;
490 conn->from = 0;
491 conn->unrestartable = 1;
492 find_auth(conn->uri); /* remember username and password */
494 cpid = fork();
495 if (cpid == -1) {
496 int s_errno = errno;
498 close(fsp_pipe[0]);
499 close(fsp_pipe[1]);
500 close(header_pipe[0]);
501 close(header_pipe[1]);
502 retry_connection(conn, -s_errno);
503 return;
506 if (!cpid) {
507 dup2(fsp_pipe[1], 1);
508 dup2(open("/dev/null", O_RDONLY), 0);
509 dup2(header_pipe[1], 2);
510 close(fsp_pipe[0]);
511 close(header_pipe[0]);
513 /* There may be outgoing data in stdio buffers
514 * inherited from the parent process. The parent
515 * process is going to write this data, so the child
516 * process must not do that. Closing the file
517 * descriptors ensures this.
519 * FIXME: If something opens more files and gets the
520 * same file descriptors and does not close them
521 * before exit(), then stdio may attempt to write the
522 * buffers to the wrong files. This might happen for
523 * example if fsplib calls syslog(). */
524 close_all_non_term_fd();
525 do_fsp(conn);
527 } else {
528 struct read_buffer *buf2;
530 conn->data_socket->fd = fsp_pipe[0];
531 conn->socket->fd = header_pipe[0];
532 set_nonblocking_fd(conn->data_socket->fd);
533 set_nonblocking_fd(conn->socket->fd);
534 close(fsp_pipe[1]);
535 close(header_pipe[1]);
536 buf2 = alloc_read_buffer(conn->socket);
537 if (!buf2) {
538 close_socket(conn->data_socket);
539 close_socket(conn->socket);
540 abort_connection(conn, S_OUT_OF_MEM);
541 return;
543 read_from_socket(conn->socket, buf2, S_CONN, fsp_got_header);