smb: used string functions in place of printf and puts.
[elinks.git] / src / protocol / smb / smb2.c
blob52a4d6d4d94179ee458ae0fbe35aaa67f720460d
1 /* SMB 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 #ifdef HAVE_LIBSMBCLIENT_H
13 #include <libsmbclient.h>
14 #endif
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #ifdef HAVE_FCNTL_H
19 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
20 #endif
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
25 #include "elinks.h"
27 #include "cache/cache.h"
28 #include "config/options.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/module.h"
31 #include "main/select.h"
32 #include "network/connection.h"
33 #include "network/socket.h"
34 #include "osdep/osdep.h"
35 #include "protocol/auth/auth.h"
36 #include "protocol/common.h"
37 #include "protocol/protocol.h"
38 #include "protocol/smb/smb.h"
39 #include "protocol/uri.h"
40 #include "util/conv.h"
41 #include "util/memory.h"
42 #include "util/snprintf.h"
43 #include "util/string.h"
45 /* These options are not used. */
46 struct option_info smb_options[] = {
47 INIT_OPT_TREE("protocol", N_("SMB"),
48 "smb", 0,
49 N_("SAMBA specific options.")),
51 INIT_OPT_STRING("protocol.smb", N_("Credentials"),
52 "credentials", 0, "",
53 N_("Credentials file passed to smbclient via -A option.")),
55 NULL_OPTION_INFO,
58 struct module smb_protocol_module = struct_module(
59 /* name: */ N_("SMB"),
60 /* options: */ smb_options,
61 /* hooks: */ NULL,
62 /* submodules: */ NULL,
63 /* data: */ NULL,
64 /* init: */ NULL,
65 /* done: */ NULL
68 static void
69 smb_error(int error)
71 fprintf(stderr, "text/x-error");
72 printf("%d\n", error);
73 exit(1);
76 static int
77 compare(const void *a, const void *b)
79 const struct smbc_dirent **da = (const struct smbc_dirent **)a;
80 const struct smbc_dirent **db = (const struct smbc_dirent **)b;
81 int res = (*da)->smbc_type - (*db)->smbc_type;
83 if (res) {
84 return res;
86 return strcmp((*da)->name, (*db)->name);
89 static void
90 smb_add_link(struct string *string, struct smbc_dirent *entry,
91 unsigned char *text, unsigned char dircolor[])
93 add_to_string(string, "<a href=\"");
94 add_to_string(string, entry->name);
95 add_to_string(string, "\">");
96 if (*dircolor) {
97 add_to_string(string, "<font color=\"");
98 add_to_string(string, dircolor);
99 add_to_string(string, "\"><b>");
101 add_to_string(string, entry->name);
102 if (*dircolor) {
103 add_to_string(string, "</b></font>");
105 add_to_string(string, "</a>");
106 if (text) add_to_string(string, text);
109 static void
110 display_entry(struct smbc_dirent *entry, unsigned char dircolor[])
112 static unsigned char zero = '\0';
113 struct string string;
115 if (!init_string(&string)) return;
117 switch (entry->smbc_type) {
118 case SMBC_WORKGROUP:
119 smb_add_link(&string, entry, " WORKGROUP ", dircolor);
120 break;
121 case SMBC_SERVER:
122 smb_add_link(&string, entry, " SERVER ", dircolor);
123 if (entry->comment) add_to_string(&string, entry->comment);
124 break;
125 case SMBC_FILE_SHARE:
126 smb_add_link(&string, entry, " FILE SHARE ", dircolor);
127 if (entry->comment) add_to_string(&string, entry->comment);
128 break;
129 case SMBC_PRINTER_SHARE:
130 add_to_string(&string, entry->name);
131 add_to_string(&string, " PRINTER ");
132 if (entry->comment) add_to_string(&string, entry->comment);
133 break;
134 case SMBC_COMMS_SHARE:
135 add_to_string(&string, entry->name);
136 add_to_string(&string, " COMM");
137 break;
138 case SMBC_IPC_SHARE:
139 add_to_string(&string, entry->name);
140 add_to_string(&string, " IPC");
141 break;
142 case SMBC_DIR:
143 smb_add_link(&string, entry, NULL, dircolor);
144 break;
145 case SMBC_LINK:
146 smb_add_link(&string, entry, " Link", &zero);
147 break;
148 case SMBC_FILE:
149 smb_add_link(&string, entry, NULL, &zero);
150 break;
151 default:
152 /* unknown type */
153 break;
155 puts(string.source);
156 done_string(&string);
159 static void
160 sort_and_display_entries(int dir, unsigned char dircolor[])
162 struct smbc_dirent *fentry, **table = NULL;
163 int size = 0;
164 int i;
166 while ((fentry = smbc_readdir(dir))) {
167 struct smbc_dirent **new_table, *new_entry;
168 int length = fentry->dirlen;
170 if (!strcmp(fentry->name, "."))
171 continue;
173 new_entry = mem_alloc(length);
174 if (fentry->comment) {
175 char *comment = mem_alloc(fentry->commentlen + 1);
177 if (comment) memcpy(comment, fentry->comment, fentry->commentlen + 1);
178 fentry->comment = comment;
180 if (!new_entry)
181 continue;
182 new_table = mem_realloc(table, (size + 1) * sizeof(*table));
183 if (!new_table)
184 continue;
185 memcpy(new_entry, fentry, length);
186 table = new_table;
187 table[size] = new_entry;
188 size++;
190 qsort(table, size, sizeof(*table),
191 (int (*)(const void *, const void *)) compare);
193 for (i = 0; i < size; i++) {
194 display_entry(table[i], dircolor);
198 static void
199 smb_directory(int dir, struct uri *uri)
201 struct string buf;
202 unsigned char dircolor[8] = "";
204 if (init_directory_listing(&buf, uri) != S_OK) {
205 smb_error(-S_OUT_OF_MEM);
208 fprintf(stderr, "text/html");
209 fclose(stderr);
211 puts(buf.source);
213 if (get_opt_bool("document.browse.links.color_dirs")) {
214 color_to_string(get_opt_color("document.colors.dirs"),
215 (unsigned char *) &dircolor);
218 sort_and_display_entries(dir, dircolor);
219 puts("</pre><hr/></body></html>");
220 smbc_closedir(dir);
221 exit(0);
224 static void
225 smb_auth(const char *srv, const char *shr, char *wg, int wglen, char *un,
226 int unlen, char *pw, int pwlen)
228 /* TODO */
231 #define READ_SIZE 4096
233 static void
234 do_smb(struct connection *conn)
236 struct uri *uri = conn->uri;
237 struct auth_entry *auth = find_auth(uri);
238 struct string string;
239 unsigned char *url;
240 int dir;
242 if ((uri->userlen && uri->passwordlen) || !auth || !auth->valid) {
243 url = get_uri_string(uri, URI_BASE);
244 } else {
245 unsigned char *uri_string = get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA);
247 if (!uri_string || !init_string(&string)) {
248 smb_error(-S_OUT_OF_MEM);
250 add_to_string(&string, "smb://");
251 add_to_string(&string, auth->user);
252 add_char_to_string(&string, ':');
253 add_to_string(&string, auth->password);
254 add_char_to_string(&string, '@');
255 add_to_string(&string, uri_string);
256 url = string.source;
259 if (!url) {
260 smb_error(-S_OUT_OF_MEM);
262 if (smbc_init(smb_auth, 0)) {
263 smb_error(errno);
265 dir = smbc_opendir(url);
266 if (dir >= 0) {
267 smb_directory(dir, conn->uri);
268 } else {
269 char buf[READ_SIZE];
270 struct stat sb;
271 int r, res;
272 int file = smbc_open(url, O_RDONLY, 0);
274 if (file < 0) {
275 smb_error(errno);
278 res = smbc_fstat(file, &sb);
279 if (res) {
280 smb_error(res);
282 /* filesize */
283 fprintf(stderr, "%" OFF_T_FORMAT, sb.st_size);
284 fclose(stderr);
286 while ((r = smbc_read(file, buf, READ_SIZE)) > 0) {
287 if (safe_write(STDOUT_FILENO, buf, r) <= 0)
288 break;
290 smbc_close(file);
291 exit(0);
295 #undef READ_SIZE
297 /* Kill the current connection and ask for a username/password for the next
298 * try. */
299 static void
300 prompt_username_pw(struct connection *conn)
302 add_auth_entry(conn->uri, "Samba", NULL, NULL, 0);
303 abort_connection(conn, S_OK);
306 static void
307 smb_got_error(struct socket *socket, struct read_buffer *rb)
309 int len = rb->length;
310 struct connection *conn = socket->conn;
311 int error;
313 if (len < 0) {
314 abort_connection(conn, -errno);
315 return;
318 rb->data[len] = '\0';
319 error = atoi(rb->data);
320 kill_buffer_data(rb, len);
321 switch (error) {
322 case EACCES:
323 prompt_username_pw(conn);
324 break;
325 default:
326 abort_connection(conn, -error);
327 break;
331 static void
332 smb_got_data(struct socket *socket, struct read_buffer *rb)
334 int len = rb->length;
335 struct connection *conn = socket->conn;
337 if (len < 0) {
338 abort_connection(conn, -errno);
339 return;
342 if (!len) {
343 abort_connection(conn, S_OK);
344 return;
347 socket->state = SOCKET_END_ONCLOSE;
348 conn->received += len;
349 if (add_fragment(conn->cached, conn->from, rb->data, len) == 1)
350 conn->tries = 0;
351 conn->from += len;
352 kill_buffer_data(rb, len);
354 read_from_socket(socket, rb, S_TRANS, smb_got_data);
357 static void
358 smb_got_header(struct socket *socket, struct read_buffer *rb)
360 struct connection *conn = socket->conn;
361 struct read_buffer *buf;
362 int error = 0;
364 conn->cached = get_cache_entry(conn->uri);
365 if (!conn->cached) {
366 close(socket->fd);
367 close(conn->data_socket->fd);
368 abort_connection(conn, S_OUT_OF_MEM);
369 return;
371 socket->state = SOCKET_END_ONCLOSE;
373 if (rb->length > 0) {
374 unsigned char *ctype = memacpy(rb->data, rb->length);
376 if (ctype && *ctype) {
377 if (!strcmp(ctype, "text/x-error")) {
378 error = 1;
379 mem_free(ctype);
380 } else {
381 if (ctype[0] >= '0' && ctype[0] <= '9') {
382 #ifdef HAVE_ATOLL
383 conn->est_length = (off_t)atoll(ctype);
384 #else
385 conn->est_length = (off_t)atoi(ctype);
386 #endif
387 mem_free(ctype);
389 /* avoid error */
390 if (!conn->est_length) {
391 abort_connection(conn, S_OK);
392 return;
395 else mem_free_set(&conn->cached->content_type, ctype);
397 } else {
398 mem_free_if(ctype);
402 buf = alloc_read_buffer(conn->data_socket);
403 if (!buf) {
404 close(socket->fd);
405 close(conn->data_socket->fd);
406 abort_connection(conn, S_OUT_OF_MEM);
407 return;
409 if (error) {
410 mem_free_set(&conn->cached->content_type, stracpy("text/html"));
411 read_from_socket(conn->data_socket, buf, S_CONN, smb_got_error);
412 } else {
413 read_from_socket(conn->data_socket, buf, S_CONN, smb_got_data);
417 void
418 smb_protocol_handler(struct connection *conn)
420 int smb_pipe[2] = { -1, -1 };
421 int header_pipe[2] = { -1, -1 };
422 pid_t cpid;
424 if (c_pipe(smb_pipe) || c_pipe(header_pipe)) {
425 int s_errno = errno;
427 if (smb_pipe[0] >= 0) close(smb_pipe[0]);
428 if (smb_pipe[1] >= 0) close(smb_pipe[1]);
429 if (header_pipe[0] >= 0) close(header_pipe[0]);
430 if (header_pipe[1] >= 0) close(header_pipe[1]);
431 abort_connection(conn, -s_errno);
432 return;
434 conn->from = 0;
435 conn->unrestartable = 1;
437 cpid = fork();
438 if (cpid == -1) {
439 int s_errno = errno;
441 close(smb_pipe[0]);
442 close(smb_pipe[1]);
443 close(header_pipe[0]);
444 close(header_pipe[1]);
445 retry_connection(conn, -s_errno);
446 return;
449 if (!cpid) {
450 dup2(smb_pipe[1], 1);
451 dup2(open("/dev/null", O_RDONLY), 0);
452 dup2(header_pipe[1], 2);
453 close(smb_pipe[0]);
454 close(header_pipe[0]);
456 close_all_non_term_fd();
457 do_smb(conn);
459 } else {
460 struct read_buffer *buf2;
462 conn->data_socket->fd = smb_pipe[0];
463 conn->socket->fd = header_pipe[0];
464 close(smb_pipe[1]);
465 close(header_pipe[1]);
466 buf2 = alloc_read_buffer(conn->socket);
467 if (!buf2) {
468 close(smb_pipe[0]);
469 close(header_pipe[0]);
470 abort_connection(conn, S_OUT_OF_MEM);
471 return;
473 read_from_socket(conn->socket, buf2, S_CONN, smb_got_header);