Retry only for https protocol
[elinks.git] / src / protocol / smb / smb2.c
blob7381a6d7fb4f8ba70459f8459405b0a287a1754d
1 /* SMB protocol implementation */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #ifdef HAVE_LIBSMBCLIENT_H
9 #include <libsmbclient.h>
10 #endif
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #ifdef HAVE_FCNTL_H
15 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
16 #endif
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
21 #include "elinks.h"
23 #include "cache/cache.h"
24 #include "config/options.h"
25 #include "intl/gettext/libintl.h"
26 #include "main/module.h"
27 #include "main/select.h"
28 #include "network/connection.h"
29 #include "network/socket.h"
30 #include "osdep/osdep.h"
31 #include "protocol/auth/auth.h"
32 #include "protocol/common.h"
33 #include "protocol/protocol.h"
34 #include "protocol/smb/smb.h"
35 #include "protocol/uri.h"
36 #include "util/conv.h"
37 #include "util/file.h"
38 #include "util/memory.h"
39 #include "util/string.h"
41 struct module smb_protocol_module = struct_module(
42 /* name: */ N_("SMB"),
43 /* options: */ NULL,
44 /* hooks: */ NULL,
45 /* submodules: */ NULL,
46 /* data: */ NULL,
47 /* init: */ NULL,
48 /* done: */ NULL
51 static FILE *header_out, *data_out;
53 /* The child process generally does not bother to free the memory it
54 * allocates. When the process exits, the operating system will free
55 * the memory anyway. There is no point in changing this, because the
56 * child process also inherits memory allocations from the parent
57 * process, and it would be very cumbersome to free those. */
59 static void
60 smb_error(struct connection_state error)
62 if (is_system_error(error))
63 fprintf(data_out, "S%d\n", (int) error.syserr);
64 else
65 fprintf(data_out, "I%d\n", (int) error.basic);
66 fputs("text/x-error", header_out);
67 exit(1);
70 /** First information such as permissions is gathered for each directory entry.
71 * All entries are then sorted. */
72 static struct directory_entry *
73 get_smb_directory_entries(int dir, struct string *prefix)
75 struct directory_entry *entries = NULL;
77 int size = 0;
78 struct smbc_dirent *entry;
80 while ((entry = smbc_readdir(dir))) {
81 struct stat st, *stp;
82 struct directory_entry *new_entries;
83 struct string attrib;
84 struct string name;
86 if (!strcmp(entry->name, "."))
87 continue;
89 new_entries = mem_realloc(entries, (size + 2) * sizeof(*new_entries));
90 if (!new_entries) continue;
91 entries = new_entries;
93 if (!init_string(&attrib)) {
94 continue;
97 if (!init_string(&name)) {
98 done_string(&attrib);
99 continue;
102 add_string_to_string(&name, prefix);
103 add_to_string(&name, entry->name);
105 stp = (smbc_stat(name.source, &st)) ? NULL : &st;
107 stat_type(&attrib, stp);
108 stat_mode(&attrib, stp);
109 stat_links(&attrib, stp);
110 stat_user(&attrib, stp);
111 stat_group(&attrib, stp);
112 stat_size(&attrib, stp);
113 stat_date(&attrib, stp);
115 entries[size].name = stracpy(entry->name);
116 entries[size].attrib = attrib.source;
117 done_string(&name);
118 size++;
120 smbc_closedir(dir);
122 if (!size) {
123 /* We may have allocated space for entries but added none. */
124 mem_free_if(entries);
125 return NULL;
127 qsort(entries, size, sizeof(*entries), compare_dir_entries);
128 memset(&entries[size], 0, sizeof(*entries));
129 return entries;
132 static void
133 add_smb_dir_entry(struct directory_entry *entry, struct string *page,
134 int pathlen, unsigned char *dircolor)
136 unsigned char *lnk = NULL;
137 struct string html_encoded_name;
138 struct string uri_encoded_name;
140 if (!init_string(&html_encoded_name)) return;
141 if (!init_string(&uri_encoded_name)) {
142 done_string(&html_encoded_name);
143 return;
146 encode_uri_string(&uri_encoded_name, entry->name + pathlen, -1, 1);
147 add_html_to_string(&html_encoded_name, entry->name + pathlen,
148 strlen(entry->name) - pathlen);
150 /* add_to_string(&fragment, &fragmentlen, " "); */
151 add_html_to_string(page, entry->attrib, strlen(entry->attrib));
152 add_to_string(page, "<a href=\"");
153 add_string_to_string(page, &uri_encoded_name);
155 if (entry->attrib[0] == 'd') {
156 add_char_to_string(page, '/');
158 #ifdef FS_UNIX_SOFTLINKS
159 } else if (entry->attrib[0] == 'l') {
160 struct stat st;
161 unsigned char buf[MAX_STR_LEN];
162 int readlen = readlink(entry->name, buf, MAX_STR_LEN);
164 if (readlen > 0 && readlen != MAX_STR_LEN) {
165 buf[readlen] = '\0';
166 lnk = straconcat(" -> ", buf, (unsigned char *) NULL);
169 if (!stat(entry->name, &st) && S_ISDIR(st.st_mode))
170 add_char_to_string(page, '/');
171 #endif
174 add_to_string(page, "\">");
176 if (entry->attrib[0] == 'd' && *dircolor) {
177 /* The <b> is for the case when use_document_colors is off. */
178 string_concat(page, "<font color=\"", dircolor, "\"><b>",
179 (unsigned char *) NULL);
182 add_string_to_string(page, &html_encoded_name);
183 done_string(&uri_encoded_name);
184 done_string(&html_encoded_name);
186 if (entry->attrib[0] == 'd' && *dircolor) {
187 add_to_string(page, "</b></font>");
190 add_to_string(page, "</a>");
191 if (lnk) {
192 add_html_to_string(page, lnk, strlen(lnk));
193 mem_free(lnk);
196 add_char_to_string(page, '\n');
199 /* First information such as permissions is gathered for each directory entry.
200 * Finally the sorted entries are added to the @data->fragment one by one. */
201 static void
202 add_smb_dir_entries(struct directory_entry *entries, unsigned char *dirpath,
203 struct string *page)
205 unsigned char dircolor[8];
206 int i;
208 /* Setup @dircolor so it's easy to check if we should color dirs. */
209 if (get_opt_bool("document.browse.links.color_dirs", NULL)) {
210 color_to_string(get_opt_color("document.colors.dirs", NULL),
211 (unsigned char *) &dircolor);
212 } else {
213 dircolor[0] = 0;
216 for (i = 0; entries[i].name; i++) {
217 add_smb_dir_entry(&entries[i], page, 0, dircolor);
218 mem_free(entries[i].attrib);
219 mem_free(entries[i].name);
222 /* We may have allocated space for entries but added none. */
223 mem_free_if(entries);
226 static void
227 smb_directory(int dir, struct string *prefix, struct uri *uri)
229 struct string buf;
230 struct directory_entry *entries;
232 if (!is_in_state(init_directory_listing(&buf, uri), S_OK)) {
233 smb_error(connection_state(S_OUT_OF_MEM));
236 fputs("text/html", header_out);
237 fclose(header_out);
239 entries = get_smb_directory_entries(dir, prefix);
240 add_smb_dir_entries(entries, NULL, &buf);
241 add_to_string(&buf, "</pre><hr/></body></html>\n");
243 fputs(buf.source, data_out);
244 done_string(&buf);
245 exit(0);
248 static void
249 smb_auth(const char *srv, const char *shr, char *wg, int wglen, char *un,
250 int unlen, char *pw, int pwlen)
252 /* TODO */
255 #define READ_SIZE 4096
257 static void
258 do_smb(struct connection *conn)
260 struct uri *uri = conn->uri;
261 struct auth_entry *auth = find_auth(uri);
262 struct string string;
263 unsigned char *url;
264 int dir;
266 if ((uri->userlen && uri->passwordlen) || !auth) {
267 url = get_uri_string(uri, URI_BASE);
268 } else {
269 unsigned char *uri_string = get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA);
271 if (!uri_string || !init_string(&string)) {
272 smb_error(connection_state(S_OUT_OF_MEM));
274 /* Must URI-encode the username and password to avoid
275 * ambiguity if they contain "/:@" characters.
276 * Libsmbclient then decodes them again, and the
277 * server gets them as they were in auth->user and
278 * auth->password, i.e. as the user typed them in the
279 * auth dialog. This implies that, if the username or
280 * password contains some characters or bytes that the
281 * user cannot directly type, then she cannot enter
282 * them. If that becomes an actual problem, it should
283 * be fixed in the auth dialog, e.g. by providing a
284 * hexadecimal input mode. */
285 add_to_string(&string, "smb://");
286 encode_uri_string(&string, auth->user, -1, 1);
287 add_char_to_string(&string, ':');
288 encode_uri_string(&string, auth->password, -1, 1);
289 add_char_to_string(&string, '@');
290 add_to_string(&string, uri_string);
291 url = string.source;
294 if (!url) {
295 smb_error(connection_state(S_OUT_OF_MEM));
297 if (smbc_init(smb_auth, 0)) {
298 smb_error(connection_state_for_errno(errno));
302 dir = smbc_opendir(url);
303 if (dir >= 0) {
304 struct string prefix;
306 init_string(&prefix);
307 add_to_string(&prefix, url);
308 add_char_to_string(&prefix, '/');
309 smb_directory(dir, &prefix, conn->uri);
310 done_string(&prefix);
311 } else {
312 const int errno_from_opendir = errno;
313 char buf[READ_SIZE];
314 struct stat sb;
315 int r, res, fdout;
316 int file = smbc_open(url, O_RDONLY, 0);
318 if (file < 0) {
319 /* If we're opening the list of shares without
320 * proper authentication, then smbc_opendir
321 * fails with EACCES and smbc_open fails with
322 * ENOENT. In this case, return the EACCES so
323 * that the parent ELinks process will prompt
324 * for credentials. */
325 if (errno == ENOENT && errno_from_opendir == EACCES)
326 errno = errno_from_opendir;
327 smb_error(connection_state_for_errno(errno));
330 res = smbc_fstat(file, &sb);
331 if (res) {
332 smb_error(connection_state_for_errno(res));
334 /* filesize */
335 fprintf(header_out, "%" OFF_PRINT_FORMAT,
336 (off_print_T) sb.st_size);
337 fclose(header_out);
339 fdout = fileno(data_out);
340 while ((r = smbc_read(file, buf, READ_SIZE)) > 0) {
341 if (safe_write(fdout, buf, r) <= 0)
342 break;
344 smbc_close(file);
345 exit(0);
349 #undef READ_SIZE
351 /* Kill the current connection and ask for a username/password for the next
352 * try. */
353 static void
354 prompt_username_pw(struct connection *conn)
356 add_auth_entry(conn->uri, "Samba", NULL, NULL, 0);
357 abort_connection(conn, connection_state(S_OK));
360 static void
361 smb_got_error(struct socket *socket, struct read_buffer *rb)
363 int len = rb->length;
364 struct connection *conn = socket->conn;
365 struct connection_state error;
367 if (len < 0) {
368 abort_connection(conn, connection_state_for_errno(errno));
369 return;
372 /* There should be free space in the buffer, because
373 * @alloc_read_buffer allocated several kibibytes, and the
374 * child process wrote only an integer and a newline to the
375 * pipe. */
376 assert(rb->freespace >= 1);
377 if_assert_failed {
378 abort_connection(conn, connection_state(S_INTERNAL));
379 return;
381 rb->data[len] = '\0';
382 switch (rb->data[0]) {
383 case 'S':
384 error = connection_state_for_errno(atoi(rb->data + 1));
385 break;
386 case 'I':
387 error = connection_state(atoi(rb->data + 1));
388 break;
389 default:
390 ERROR("malformed error code: %s", rb->data);
391 error = connection_state(S_INTERNAL);
392 break;
394 kill_buffer_data(rb, len);
396 if (is_system_error(error) && error.syserr == EACCES)
397 prompt_username_pw(conn);
398 else
399 abort_connection(conn, error);
402 static void
403 smb_got_data(struct socket *socket, struct read_buffer *rb)
405 int len = rb->length;
406 struct connection *conn = socket->conn;
408 if (len < 0) {
409 abort_connection(conn, connection_state_for_errno(errno));
410 return;
413 if (!len) {
414 abort_connection(conn, connection_state(S_OK));
415 return;
418 socket->state = SOCKET_END_ONCLOSE;
419 conn->received += len;
420 if (add_fragment(conn->cached, conn->from, rb->data, len) == 1)
421 conn->tries = 0;
422 conn->from += len;
423 kill_buffer_data(rb, len);
425 read_from_socket(socket, rb, connection_state(S_TRANS), smb_got_data);
428 static void
429 smb_got_header(struct socket *socket, struct read_buffer *rb)
431 struct connection *conn = socket->conn;
432 struct read_buffer *buf;
433 int error = 0;
435 conn->cached = get_cache_entry(conn->uri);
436 if (!conn->cached) {
437 /* Even though these are pipes rather than real
438 * sockets, call close_socket instead of close, to
439 * ensure that abort_connection won't try to close the
440 * file descriptors again. (Could we skip the calls
441 * and assume abort_connection will do them?) */
442 close_socket(socket);
443 close_socket(conn->data_socket);
444 abort_connection(conn, connection_state(S_OUT_OF_MEM));
445 return;
447 socket->state = SOCKET_END_ONCLOSE;
449 if (rb->length > 0) {
450 unsigned char *ctype = memacpy(rb->data, rb->length);
452 if (ctype && *ctype) {
453 if (!strcmp(ctype, "text/x-error")) {
454 error = 1;
455 mem_free(ctype);
456 } else {
457 if (ctype[0] >= '0' && ctype[0] <= '9') {
458 #ifdef HAVE_ATOLL
459 conn->est_length = (off_t)atoll(ctype);
460 #else
461 conn->est_length = (off_t)atol(ctype);
462 #endif
463 mem_free(ctype);
465 /* avoid error */
466 if (!conn->est_length) {
467 abort_connection(conn, connection_state(S_OK));
468 return;
471 else mem_free_set(&conn->cached->content_type, ctype);
473 } else {
474 mem_free_if(ctype);
478 buf = alloc_read_buffer(conn->data_socket);
479 if (!buf) {
480 close_socket(socket);
481 close_socket(conn->data_socket);
482 abort_connection(conn, connection_state(S_OUT_OF_MEM));
483 return;
485 if (error) {
486 mem_free_set(&conn->cached->content_type, stracpy("text/html"));
487 read_from_socket(conn->data_socket, buf,
488 connection_state(S_CONN), smb_got_error);
489 } else {
490 read_from_socket(conn->data_socket, buf,
491 connection_state(S_CONN), smb_got_data);
495 static void
496 close_all_fds_but_two(int header, int data)
498 int n;
499 int max = 1024;
500 #ifdef RLIMIT_NOFILE
501 struct rlimit lim;
503 if (!getrlimit(RLIMIT_NOFILE, &lim))
504 max = lim.rlim_max;
505 #endif
506 for (n = 3; n < max; n++) {
507 if (n != header && n != data) close(n);
513 void
514 smb_protocol_handler(struct connection *conn)
516 int smb_pipe[2] = { -1, -1 };
517 int header_pipe[2] = { -1, -1 };
518 pid_t cpid;
520 if (c_pipe(smb_pipe) || c_pipe(header_pipe)) {
521 int s_errno = errno;
523 if (smb_pipe[0] >= 0) close(smb_pipe[0]);
524 if (smb_pipe[1] >= 0) close(smb_pipe[1]);
525 if (header_pipe[0] >= 0) close(header_pipe[0]);
526 if (header_pipe[1] >= 0) close(header_pipe[1]);
527 abort_connection(conn, connection_state_for_errno(s_errno));
528 return;
530 conn->from = 0;
531 conn->unrestartable = 1;
532 find_auth(conn->uri); /* remember username and password */
534 cpid = fork();
535 if (cpid == -1) {
536 int s_errno = errno;
538 close(smb_pipe[0]);
539 close(smb_pipe[1]);
540 close(header_pipe[0]);
541 close(header_pipe[1]);
542 retry_connection(conn, connection_state_for_errno(s_errno));
543 return;
546 if (!cpid) {
547 dup2(open("/dev/null", O_RDONLY), 0);
548 close(1);
549 close(2);
550 data_out = fdopen(smb_pipe[1], "w");
551 header_out = fdopen(header_pipe[1], "w");
553 if (!data_out || !header_out) exit(1);
555 close(smb_pipe[0]);
556 close(header_pipe[0]);
558 /* There may be outgoing data in stdio buffers
559 * inherited from the parent process. The parent
560 * process is going to write this data, so the child
561 * process must not do that. Closing the file
562 * descriptors ensures this.
564 * FIXME: If something opens more files and gets the
565 * same file descriptors and does not close them
566 * before exit(), then stdio may attempt to write the
567 * buffers to the wrong files. This might happen for
568 * example if libsmbclient calls syslog(). */
570 close_all_fds_but_two(smb_pipe[1], header_pipe[1]);
571 do_smb(conn);
573 } else {
574 struct read_buffer *buf2;
576 conn->data_socket->fd = smb_pipe[0];
577 conn->socket->fd = header_pipe[0];
578 set_nonblocking_fd(conn->data_socket->fd);
579 set_nonblocking_fd(conn->socket->fd);
580 close(smb_pipe[1]);
581 close(header_pipe[1]);
582 buf2 = alloc_read_buffer(conn->socket);
583 if (!buf2) {
584 close_socket(conn->data_socket);
585 close_socket(conn->socket);
586 abort_connection(conn, connection_state(S_OUT_OF_MEM));
587 return;
589 read_from_socket(conn->socket, buf2,
590 connection_state(S_CONN), smb_got_header);