smb2: show comments.
[elinks.git] / src / protocol / smb / smb2.c
blob5c9c8150077105cde9d24f942829741e09a7067a
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 display_entry(struct smbc_dirent *entry, unsigned char dircolor[])
92 switch (entry->smbc_type) {
93 case SMBC_WORKGROUP:
94 printf("<a href=\"%s\">", entry->name);
95 if (*dircolor) {
96 printf("<font color=\"%s\"><b>", dircolor);
98 printf("%s", entry->name);
99 if (*dircolor) {
100 printf("</b></font>");
102 puts("</a> WORKGROUP");
103 break;
104 case SMBC_SERVER:
105 printf("<a href=\"%s\">", entry->name);
106 if (*dircolor) {
107 printf("<font color=\"%s\"><b>", dircolor);
109 printf("%s", entry->name);
110 if (*dircolor) {
111 printf("</b></font>");
113 printf("</a> SERVER ");
114 puts(entry->comment ? entry->comment : "");
115 break;
116 case SMBC_FILE_SHARE:
117 printf("<a href=\"%s\">", entry->name);
118 if (*dircolor) {
119 printf("<font color=\"%s\"><b>", dircolor);
121 printf("%s", entry->name);
122 if (*dircolor) {
123 printf("</b></font>");
125 printf("</a> FILE SHARE ");
126 puts(entry->comment ? entry->comment : "");
127 break;
128 case SMBC_PRINTER_SHARE:
129 printf("%s PRINTER ", entry->name);
130 puts(entry->comment ? entry->comment : "");
131 break;
132 case SMBC_COMMS_SHARE:
133 printf("%s COMM\n", entry->name);
134 break;
135 case SMBC_IPC_SHARE:
136 printf("%s IPC\n", entry->name);
137 break;
138 case SMBC_DIR:
139 printf("<a href=\"%s\">", entry->name);
140 if (*dircolor) {
141 printf("<font color=\"%s\"><b>", dircolor);
143 printf("%s", entry->name);
144 if (*dircolor) {
145 printf("</b></font>");
147 puts("</a>");
148 break;
149 case SMBC_LINK:
150 printf("<a href=\"%s\">%s</a> Link\n", entry->name, entry->name);
151 break;
152 case SMBC_FILE:
153 printf("<a href=\"%s\">%s</a>\n", entry->name, entry->name);
154 break;
155 default:
156 /* unknown type */
157 break;
161 static void
162 sort_and_display_entries(int dir, unsigned char dircolor[])
164 struct smbc_dirent *fentry, **table = NULL;
165 int size = 0;
166 int i;
168 while ((fentry = smbc_readdir(dir))) {
169 struct smbc_dirent **new_table, *new_entry;
170 int length = fentry->dirlen;
172 if (!strcmp(fentry->name, "."))
173 continue;
175 new_entry = mem_alloc(length);
176 if (fentry->comment) {
177 char *comment = mem_alloc(fentry->commentlen + 1);
179 if (comment) memcpy(comment, fentry->comment, fentry->commentlen + 1);
180 fentry->comment = comment;
182 if (!new_entry)
183 continue;
184 new_table = mem_realloc(table, (size + 1) * sizeof(*table));
185 if (!new_table)
186 continue;
187 memcpy(new_entry, fentry, length);
188 table = new_table;
189 table[size] = new_entry;
190 size++;
192 qsort(table, size, sizeof(*table),
193 (int (*)(const void *, const void *)) compare);
195 for (i = 0; i < size; i++) {
196 display_entry(table[i], dircolor);
200 static void
201 smb_directory(int dir, struct uri *uri)
203 struct string buf;
204 unsigned char dircolor[8] = "";
206 if (init_directory_listing(&buf, uri) != S_OK) {
207 smb_error(-S_OUT_OF_MEM);
210 fprintf(stderr, "text/html");
211 fclose(stderr);
213 puts(buf.source);
215 if (get_opt_bool("document.browse.links.color_dirs")) {
216 color_to_string(get_opt_color("document.colors.dirs"),
217 (unsigned char *) &dircolor);
220 sort_and_display_entries(dir, dircolor);
221 puts("</pre><hr/></body></html>");
222 smbc_closedir(dir);
223 exit(0);
226 static void
227 smb_auth(const char *srv, const char *shr, char *wg, int wglen, char *un,
228 int unlen, char *pw, int pwlen)
230 /* TODO */
233 #define READ_SIZE 4096
235 static void
236 do_smb(struct connection *conn)
238 unsigned char url_data[1024];
239 struct uri *uri = conn->uri;
240 struct auth_entry *auth = find_auth(uri);
241 unsigned char *url;
242 int dir;
244 if ((uri->userlen && uri->passwordlen) || !auth || !auth->valid) {
245 url = get_uri_string(uri, URI_BASE);
246 } else {
247 snprintf(url_data, 1024, "smb://%s:%s@%s", auth->user, auth->password,
248 get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA));
249 url = url_data;
252 if (!url) {
253 smb_error(-S_OUT_OF_MEM);
255 if (smbc_init(smb_auth, 0)) {
256 smb_error(errno);
258 dir = smbc_opendir(url);
259 if (dir >= 0) {
260 smb_directory(dir, conn->uri);
261 } else {
262 char buf[READ_SIZE];
263 struct stat sb;
264 int r, res;
265 int file = smbc_open(url, O_RDONLY, 0);
267 if (file < 0) {
268 smb_error(errno);
271 res = smbc_fstat(file, &sb);
272 if (res) {
273 smb_error(res);
275 /* filesize */
276 fprintf(stderr, "%" OFF_T_FORMAT, sb.st_size);
277 fclose(stderr);
279 while ((r = smbc_read(file, buf, READ_SIZE)) > 0) {
280 if (safe_write(STDOUT_FILENO, buf, r) <= 0)
281 break;
283 smbc_close(file);
284 exit(0);
288 #undef READ_SIZE
290 /* Kill the current connection and ask for a username/password for the next
291 * try. */
292 static void
293 prompt_username_pw(struct connection *conn)
295 add_auth_entry(conn->uri, "Samba", NULL, NULL, 0);
296 abort_connection(conn, S_OK);
299 static void
300 smb_got_error(struct socket *socket, struct read_buffer *rb)
302 int len = rb->length;
303 struct connection *conn = socket->conn;
304 int error;
306 if (len < 0) {
307 abort_connection(conn, -errno);
308 return;
311 rb->data[len] = '\0';
312 error = atoi(rb->data);
313 kill_buffer_data(rb, len);
314 switch (error) {
315 case EACCES:
316 prompt_username_pw(conn);
317 break;
318 default:
319 abort_connection(conn, -error);
320 break;
324 static void
325 smb_got_data(struct socket *socket, struct read_buffer *rb)
327 int len = rb->length;
328 struct connection *conn = socket->conn;
330 if (len < 0) {
331 abort_connection(conn, -errno);
332 return;
335 if (!len) {
336 abort_connection(conn, S_OK);
337 return;
340 socket->state = SOCKET_END_ONCLOSE;
341 conn->received += len;
342 if (add_fragment(conn->cached, conn->from, rb->data, len) == 1)
343 conn->tries = 0;
344 conn->from += len;
345 kill_buffer_data(rb, len);
347 read_from_socket(socket, rb, S_TRANS, smb_got_data);
350 static void
351 smb_got_header(struct socket *socket, struct read_buffer *rb)
353 struct connection *conn = socket->conn;
354 struct read_buffer *buf;
355 int error = 0;
357 conn->cached = get_cache_entry(conn->uri);
358 if (!conn->cached) {
359 close(socket->fd);
360 close(conn->data_socket->fd);
361 abort_connection(conn, S_OUT_OF_MEM);
362 return;
364 socket->state = SOCKET_END_ONCLOSE;
366 if (rb->length > 0) {
367 unsigned char *ctype = memacpy(rb->data, rb->length);
369 if (ctype && *ctype) {
370 if (!strcmp(ctype, "text/x-error")) {
371 error = 1;
372 mem_free(ctype);
373 } else {
374 if (ctype[0] >= '0' && ctype[0] <= '9') {
375 #ifdef HAVE_ATOLL
376 conn->est_length = (off_t)atoll(ctype);
377 #else
378 conn->est_length = (off_t)atoi(ctype);
379 #endif
380 mem_free(ctype);
382 else mem_free_set(&conn->cached->content_type, ctype);
384 } else {
385 mem_free_if(ctype);
389 buf = alloc_read_buffer(conn->data_socket);
390 if (!buf) {
391 close(socket->fd);
392 close(conn->data_socket->fd);
393 abort_connection(conn, S_OUT_OF_MEM);
394 return;
396 if (error) {
397 mem_free_set(&conn->cached->content_type, stracpy("text/html"));
398 read_from_socket(conn->data_socket, buf, S_CONN, smb_got_error);
399 } else {
400 read_from_socket(conn->data_socket, buf, S_CONN, smb_got_data);
404 void
405 smb_protocol_handler(struct connection *conn)
407 int smb_pipe[2] = { -1, -1 };
408 int header_pipe[2] = { -1, -1 };
409 pid_t cpid;
411 if (c_pipe(smb_pipe) || c_pipe(header_pipe)) {
412 int s_errno = errno;
414 if (smb_pipe[0] >= 0) close(smb_pipe[0]);
415 if (smb_pipe[1] >= 0) close(smb_pipe[1]);
416 if (header_pipe[0] >= 0) close(header_pipe[0]);
417 if (header_pipe[1] >= 0) close(header_pipe[1]);
418 abort_connection(conn, -s_errno);
419 return;
421 conn->from = 0;
422 conn->unrestartable = 1;
424 cpid = fork();
425 if (cpid == -1) {
426 int s_errno = errno;
428 close(smb_pipe[0]);
429 close(smb_pipe[1]);
430 close(header_pipe[0]);
431 close(header_pipe[1]);
432 retry_connection(conn, -s_errno);
433 return;
436 if (!cpid) {
437 dup2(smb_pipe[1], 1);
438 dup2(open("/dev/null", O_RDONLY), 0);
439 dup2(header_pipe[1], 2);
440 close(smb_pipe[0]);
441 close(header_pipe[0]);
443 close_all_non_term_fd();
444 do_smb(conn);
446 } else {
447 struct read_buffer *buf2;
449 conn->data_socket->fd = smb_pipe[0];
450 conn->socket->fd = header_pipe[0];
451 close(smb_pipe[1]);
452 close(header_pipe[1]);
453 buf2 = alloc_read_buffer(conn->socket);
454 if (!buf2) {
455 close(smb_pipe[0]);
456 close(header_pipe[0]);
457 abort_connection(conn, S_OUT_OF_MEM);
458 return;
460 read_from_socket(conn->socket, buf2, S_CONN, smb_got_header);