1 /* Internal FSP protocol implementation */
4 #define _GNU_SOURCE /* Needed for asprintf() */
17 #include <fcntl.h> /* OS/2 needs this after sys/types.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"),
47 N_("FSP specific options.")),
49 INIT_OPT_BOOL("protocol.fsp", N_("Sort entries"),
51 N_("Whether to sort entries in directory listings.")),
56 struct module fsp_protocol_module
= struct_module(
57 /* name: */ N_("FSP"),
58 /* options: */ fsp_options
,
60 /* submodules: */ 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. */
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
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
));
128 return strcmp(a
->name
, b
->name
);
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
, "/\">");
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
);
160 add_to_string(&string
, "</b></font>");
163 add_to_string(&string
, "\">");
164 add_html_to_string(&string
, fentry
->name
, namelen
);
166 add_to_string(&string
, "</a>");
168 done_string(&string
);
172 sort_and_display_entries(FSP_DIR
*dir
, const unsigned char dircolor
[])
174 FSP_RDENTRY fentry
, *fresult
, *table
= NULL
;
178 while (!fsp_readdir_native(dir
, &fentry
, &fresult
)) {
179 FSP_RDENTRY
*new_table
;
182 if (!strcmp(fentry
.name
, "."))
184 new_table
= mem_realloc(table
, (size
+ 1) * sizeof(*table
));
188 copy_struct(&table
[size
], &fentry
);
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. */
194 qsort(table
, size
, sizeof(*table
), compare
);
196 for (i
= 0; i
< size
; i
++) {
197 display_entry(&table
[i
], dircolor
);
202 fsp_directory(FSP_SESSION
*ses
, struct uri
*uri
)
206 unsigned char *data
= get_uri_string(uri
, URI_DATA
);
207 unsigned char dircolor
[8] = "";
210 fsp_error(-S_OUT_OF_MEM
);
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");
223 if (get_opt_bool("document.browse.links.color_dirs")) {
224 color_to_string(get_opt_color("document.colors.dirs"),
228 if (get_opt_bool("protocol.fsp.sort")) {
229 sort_and_display_entries(dir
, dircolor
);
231 FSP_RDENTRY fentry
, *fresult
;
233 while (!fsp_readdir_native(dir
, &fentry
, &fresult
)) {
235 display_entry(&fentry
, dircolor
);
239 puts("</pre><hr/></body></html>");
240 fsp_close_session(ses
);
244 #define READ_SIZE 4096
247 do_fsp(struct connection
*conn
)
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
;
259 if (uri
->passwordlen
) {
260 password
= get_uri_string(uri
, URI_PASSWORD
);
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 */
293 FSP_FILE
*file
= fsp_fopen(ses
, data
, "r");
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. */
317 fprintf(stderr
, "%" OFF_PRINT_FORMAT
"\n",
318 (off_print_T
) sb
.st_size
);
321 while ((r
= fsp_fread(buf
, 1, READ_SIZE
, file
)) > 0)
322 if (safe_write(STDOUT_FILENO
, buf
, r
) <= 0)
326 fsp_close_session(ses
);
335 /* FSP asynchronous connection management (parent process): */
337 /* Kill the current connection and ask for a username/password for the next
340 prompt_username_pw(struct connection
*conn
)
342 add_auth_entry(conn
->uri
, "FSP", NULL
, NULL
, 0);
343 abort_connection(conn
, S_OK
);
347 fsp_got_error(struct socket
*socket
, struct read_buffer
*rb
)
349 int len
= rb
->length
;
350 struct connection
*conn
= socket
->conn
;
354 abort_connection(conn
, -errno
);
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
362 assert(rb
->freespace
>= 1);
364 abort_connection(conn
, S_INTERNAL
);
367 rb
->data
[len
] = '\0';
368 error
= atoi(rb
->data
);
369 kill_buffer_data(rb
, len
);
372 prompt_username_pw(conn
);
375 abort_connection(conn
, -error
);
381 fsp_got_data(struct socket
*socket
, struct read_buffer
*rb
)
383 int len
= rb
->length
;
384 struct connection
*conn
= socket
->conn
;
387 abort_connection(conn
, -errno
);
392 abort_connection(conn
, S_OK
);
396 socket
->state
= SOCKET_END_ONCLOSE
;
397 conn
->received
+= len
;
398 if (add_fragment(conn
->cached
, conn
->from
, rb
->data
, len
) == 1)
401 kill_buffer_data(rb
, len
);
403 read_from_socket(socket
, rb
, S_TRANS
, fsp_got_data
);
407 fsp_got_header(struct socket
*socket
, struct read_buffer
*rb
)
409 struct connection
*conn
= socket
->conn
;
410 struct read_buffer
*buf
;
413 conn
->cached
= get_cache_entry(conn
->uri
);
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
);
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")) {
435 if (ctype
[0] >= '0' && ctype
[0] <= '9') {
437 conn
->est_length
= (off_t
)atoll(ctype
);
439 conn
->est_length
= (off_t
)atol(ctype
);
443 /* avoid read from socket error */
444 if (!conn
->est_length
) {
445 abort_connection(conn
, S_OK
);
449 else mem_free_set(&conn
->cached
->content_type
, ctype
);
456 buf
= alloc_read_buffer(conn
->data_socket
);
458 close_socket(socket
);
459 close_socket(conn
->data_socket
);
460 abort_connection(conn
, S_OUT_OF_MEM
);
465 mem_free_set(&conn
->cached
->content_type
, stracpy("text/html"));
466 read_from_socket(conn
->data_socket
, buf
, S_CONN
, fsp_got_error
);
468 read_from_socket(conn
->data_socket
, buf
, S_CONN
, fsp_got_data
);
474 fsp_protocol_handler(struct connection
*conn
)
476 int fsp_pipe
[2] = { -1, -1 };
477 int header_pipe
[2] = { -1, -1 };
480 if (c_pipe(fsp_pipe
) || c_pipe(header_pipe
)) {
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
);
491 conn
->unrestartable
= 1;
492 find_auth(conn
->uri
); /* remember username and password */
500 close(header_pipe
[0]);
501 close(header_pipe
[1]);
502 retry_connection(conn
, -s_errno
);
507 dup2(fsp_pipe
[1], 1);
508 dup2(open("/dev/null", O_RDONLY
), 0);
509 dup2(header_pipe
[1], 2);
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();
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
);
535 close(header_pipe
[1]);
536 buf2
= alloc_read_buffer(conn
->socket
);
538 close_socket(conn
->data_socket
);
539 close_socket(conn
->socket
);
540 abort_connection(conn
, S_OUT_OF_MEM
);
543 read_from_socket(conn
->socket
, buf2
, S_CONN
, fsp_got_header
);