Line data Source code
1 : /*
2 : smbget: a wget-like utility with support for recursive downloading of
3 : smb:// urls
4 : Copyright (C) 2003-2004 Jelmer Vernooij <jelmer@samba.org>
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #include "includes.h"
20 : #include "system/filesys.h"
21 : #include "lib/cmdline/cmdline.h"
22 : #include "lib/param/param.h"
23 : #include "libsmbclient.h"
24 : #include "cmdline_contexts.h"
25 : #include "auth/credentials/credentials.h"
26 : #include "auth/gensec/gensec.h"
27 :
28 : static int columns = 0;
29 :
30 : static time_t total_start_time = 0;
31 : static off_t total_bytes = 0;
32 :
33 : #define SMB_MAXPATHLEN MAXPATHLEN
34 :
35 : /*
36 : * Number of bytes to read when checking whether local and remote file
37 : * are really the same file
38 : */
39 : #define RESUME_CHECK_SIZE 512
40 : #define RESUME_DOWNLOAD_OFFSET 1024
41 : #define RESUME_CHECK_OFFSET (RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE)
42 : /* Number of bytes to read at once */
43 : #define SMB_DEFAULT_BLOCKSIZE 64000
44 :
45 : struct opt {
46 : char *outputfile;
47 : size_t blocksize;
48 :
49 : int quiet;
50 : int dots;
51 : int verbose;
52 : int send_stdout;
53 : int update;
54 : unsigned limit_rate;
55 : };
56 : static struct opt opt = { .blocksize = SMB_DEFAULT_BLOCKSIZE };
57 :
58 : static bool smb_download_file(const char *base, const char *name,
59 : bool recursive, bool resume, bool toplevel,
60 : char *outfile);
61 :
62 46 : static int get_num_cols(void)
63 : {
64 : #ifdef TIOCGWINSZ
65 : struct winsize ws;
66 46 : if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
67 46 : return 0;
68 : }
69 0 : return ws.ws_col;
70 : #else
71 : #warning No support for TIOCGWINSZ
72 : char *cols = getenv("COLUMNS");
73 : if (!cols) {
74 : return 0;
75 : }
76 : return atoi(cols);
77 : #endif
78 : }
79 :
80 0 : static void change_columns(int sig)
81 : {
82 0 : columns = get_num_cols();
83 0 : }
84 :
85 1226 : static void human_readable(off_t s, char *buffer, int l)
86 : {
87 1226 : if (s > 1024 * 1024 * 1024) {
88 0 : snprintf(buffer, l, "%.2fGB", 1.0 * s / (1024 * 1024 * 1024));
89 1226 : } else if (s > 1024 * 1024) {
90 0 : snprintf(buffer, l, "%.2fMB", 1.0 * s / (1024 * 1024));
91 1226 : } else if (s > 1024) {
92 845 : snprintf(buffer, l, "%.2fkB", 1.0 * s / 1024);
93 : } else {
94 381 : snprintf(buffer, l, "%jdb", (intmax_t)s);
95 : }
96 1226 : }
97 :
98 : /*
99 : * Authentication callback for libsmbclient.
100 : *
101 : * The command line parser will take care asking for a password interactively!
102 : */
103 86 : static void get_auth_data_with_context_fn(SMBCCTX *ctx,
104 : const char *srv,
105 : const char *shr,
106 : char *dom,
107 : int dom_len,
108 : char *usr,
109 : int usr_len,
110 : char *pwd,
111 : int pwd_len)
112 : {
113 86 : struct cli_credentials *creds = samba_cmdline_get_creds();
114 86 : const char *username = NULL;
115 86 : const char *password = NULL;
116 86 : const char *domain = NULL;
117 :
118 86 : username = cli_credentials_get_username(creds);
119 86 : if (username != NULL) {
120 86 : strncpy(usr, username, usr_len - 1);
121 : }
122 :
123 86 : password = cli_credentials_get_password(creds);
124 86 : if (password != NULL) {
125 80 : strncpy(pwd, password, pwd_len - 1);
126 : }
127 :
128 86 : domain = cli_credentials_get_domain(creds);
129 86 : if (domain != NULL) {
130 86 : strncpy(dom, domain, dom_len - 1);
131 : }
132 :
133 86 : smbc_set_credentials_with_fallback(ctx, domain, username, password);
134 :
135 86 : if (!opt.quiet && username != NULL) {
136 86 : if (username[0] == '\0') {
137 4 : printf("Using guest user\n");
138 : } else {
139 82 : printf("Using domain: %s, user: %s\n",
140 : domain,
141 : username);
142 : }
143 : }
144 86 : }
145 :
146 30 : static bool smb_download_dir(const char *base, const char *name, int resume)
147 : {
148 : char path[SMB_MAXPATHLEN];
149 : int dirhandle;
150 : struct smbc_dirent *dirent;
151 30 : const char *relname = name;
152 : char *tmpname;
153 30 : bool ok = false;
154 :
155 30 : snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
156 30 : (base[0] && name[0] && name[0] != '/' &&
157 0 : base[strlen(base)-1] != '/') ? "/" : "",
158 : name);
159 :
160 : /* List files in directory and call smb_download_file on them */
161 30 : dirhandle = smbc_opendir(path);
162 30 : if (dirhandle < 1) {
163 0 : if (errno == ENOTDIR) {
164 0 : return smb_download_file(base, name, true, resume,
165 : false, NULL);
166 : }
167 0 : fprintf(stderr, "Can't open directory %s: %s\n", path,
168 0 : strerror(errno));
169 0 : return false;
170 : }
171 :
172 54 : while (*relname == '/') {
173 24 : relname++;
174 : }
175 :
176 30 : if (strlen(relname) > 0) {
177 24 : int rc = mkdir(relname, 0755);
178 24 : if (rc == -1 && errno != EEXIST) {
179 0 : fprintf(stderr, "Can't create directory %s: %s\n",
180 0 : relname, strerror(errno));
181 0 : return false;
182 : }
183 : }
184 :
185 30 : tmpname = SMB_STRDUP(name);
186 :
187 132 : while ((dirent = smbc_readdir(dirhandle))) {
188 : char *newname;
189 102 : if (!strcmp(dirent->name, ".") || !strcmp(dirent->name, "..")) {
190 60 : ok = true;
191 60 : continue;
192 : }
193 42 : if (asprintf(&newname, "%s/%s", tmpname, dirent->name) == -1) {
194 0 : free(tmpname);
195 0 : return false;
196 : }
197 42 : switch (dirent->smbc_type) {
198 24 : case SMBC_DIR:
199 24 : ok = smb_download_dir(base, newname, resume);
200 24 : break;
201 :
202 0 : case SMBC_WORKGROUP:
203 0 : ok = smb_download_dir("smb://", dirent->name, resume);
204 0 : break;
205 :
206 0 : case SMBC_SERVER:
207 0 : ok = smb_download_dir("smb://", dirent->name, resume);
208 0 : break;
209 :
210 18 : case SMBC_FILE:
211 18 : ok = smb_download_file(base, newname, true, resume,
212 : false, NULL);
213 18 : break;
214 :
215 0 : case SMBC_FILE_SHARE:
216 0 : ok = smb_download_dir(base, newname, resume);
217 0 : break;
218 :
219 0 : case SMBC_PRINTER_SHARE:
220 0 : if (!opt.quiet) {
221 0 : printf("Ignoring printer share %s\n",
222 0 : dirent->name);
223 : }
224 0 : break;
225 :
226 0 : case SMBC_COMMS_SHARE:
227 0 : if (!opt.quiet) {
228 0 : printf("Ignoring comms share %s\n",
229 0 : dirent->name);
230 : }
231 0 : break;
232 :
233 0 : case SMBC_IPC_SHARE:
234 0 : if (!opt.quiet) {
235 0 : printf("Ignoring ipc$ share %s\n",
236 0 : dirent->name);
237 : }
238 0 : break;
239 :
240 0 : default:
241 0 : fprintf(stderr, "Ignoring file '%s' of type '%d'\n",
242 : newname, dirent->smbc_type);
243 0 : break;
244 : }
245 :
246 42 : if (!ok) {
247 0 : fprintf(stderr, "Failed to download %s: %s\n",
248 0 : newname, strerror(errno));
249 0 : free(newname);
250 0 : free(tmpname);
251 0 : return false;
252 : }
253 42 : free(newname);
254 : }
255 30 : free(tmpname);
256 :
257 30 : smbc_closedir(dirhandle);
258 30 : return ok;
259 : }
260 :
261 394 : static char *print_time(long t)
262 : {
263 : static char buffer[100];
264 : int secs, mins, hours;
265 394 : if (t < -1) {
266 323 : strncpy(buffer, "Unknown", sizeof(buffer));
267 323 : return buffer;
268 : }
269 :
270 71 : secs = (int)t % 60;
271 71 : mins = (int)t / 60 % 60;
272 71 : hours = (int)t / (60 * 60);
273 71 : snprintf(buffer, sizeof(buffer) - 1, "%02d:%02d:%02d", hours, mins,
274 : secs);
275 71 : return buffer;
276 : }
277 :
278 394 : static void print_progress(const char *name, time_t start, time_t now,
279 : off_t start_pos, off_t pos, off_t total)
280 : {
281 394 : double avg = 0.0;
282 394 : long eta = -1;
283 394 : double prcnt = 0.0;
284 : char hpos[22], htotal[22], havg[22];
285 : char *status, *filename;
286 : int len;
287 394 : if (now - start) {
288 72 : avg = 1.0 * (pos - start_pos) / (now - start);
289 : }
290 394 : eta = (total - pos) / avg;
291 394 : if (total) {
292 394 : prcnt = 100.0 * pos / total;
293 : }
294 :
295 394 : human_readable(pos, hpos, sizeof(hpos));
296 394 : human_readable(total, htotal, sizeof(htotal));
297 394 : human_readable(avg, havg, sizeof(havg));
298 :
299 394 : len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos,
300 : htotal, prcnt, havg, print_time(eta));
301 394 : if (len == -1) {
302 0 : return;
303 : }
304 :
305 394 : if (columns) {
306 0 : int required = strlen(name),
307 0 : available = columns - len - strlen("[] ");
308 0 : if (required > available) {
309 0 : if (asprintf(&filename, "...%s",
310 0 : name + required - available + 3) == -1) {
311 0 : return;
312 : }
313 : } else {
314 0 : filename = SMB_STRNDUP(name, available);
315 : }
316 : } else {
317 394 : filename = SMB_STRDUP(name);
318 : }
319 :
320 394 : fprintf(stderr, "\r[%s] %s", filename, status);
321 :
322 394 : free(filename);
323 394 : free(status);
324 : }
325 :
326 : /* Return false on error, true on success. */
327 :
328 58 : static bool smb_download_file(const char *base, const char *name,
329 : bool recursive, bool resume, bool toplevel,
330 : char *outfile)
331 : {
332 : int remotehandle, localhandle;
333 58 : time_t start_time = time_mono(NULL);
334 : const char *newpath;
335 : char path[SMB_MAXPATHLEN];
336 : char checkbuf[2][RESUME_CHECK_SIZE];
337 58 : char *readbuf = NULL;
338 58 : off_t offset_download = 0, offset_check = 0, curpos = 0,
339 58 : start_offset = 0;
340 : struct stat localstat, remotestat;
341 58 : clock_t start_of_bucket_ticks = 0;
342 58 : size_t bytes_in_bucket = 0;
343 58 : size_t bucket_size = 0;
344 58 : clock_t ticks_to_fill_bucket = 0;
345 :
346 58 : snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
347 58 : (*base && *name && name[0] != '/' &&
348 0 : base[strlen(base)-1] != '/') ? "/" : "",
349 : name);
350 :
351 58 : remotehandle = smbc_open(path, O_RDONLY, 0755);
352 :
353 58 : if (remotehandle < 0) {
354 0 : switch (errno) {
355 0 : case EISDIR:
356 0 : if (!recursive) {
357 0 : fprintf(stderr,
358 : "%s is a directory. Specify -R "
359 : "to download recursively\n",
360 : path);
361 0 : return false;
362 : }
363 0 : return smb_download_dir(base, name, resume);
364 :
365 0 : case ENOENT:
366 0 : fprintf(stderr,
367 : "%s can't be found on the remote server\n",
368 : path);
369 0 : return false;
370 :
371 0 : case ENOMEM:
372 0 : fprintf(stderr, "Not enough memory\n");
373 0 : return false;
374 :
375 0 : case ENODEV:
376 0 : fprintf(stderr,
377 : "The share name used in %s does not exist\n",
378 : path);
379 0 : return false;
380 :
381 0 : case EACCES:
382 0 : fprintf(stderr, "You don't have enough permissions "
383 : "to access %s\n",
384 : path);
385 0 : return false;
386 :
387 0 : default:
388 0 : perror("smbc_open");
389 0 : return false;
390 : }
391 : }
392 :
393 58 : if (smbc_fstat(remotehandle, &remotestat) < 0) {
394 0 : fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
395 0 : return false;
396 : }
397 :
398 58 : if (outfile) {
399 0 : newpath = outfile;
400 58 : } else if (!name[0]) {
401 40 : newpath = strrchr(base, '/');
402 40 : if (newpath) {
403 40 : newpath++;
404 : } else {
405 0 : newpath = base;
406 : }
407 : } else {
408 18 : newpath = name;
409 : }
410 :
411 58 : if (!toplevel && (newpath[0] == '/')) {
412 18 : newpath++;
413 : }
414 :
415 : /* Open local file according to the mode */
416 58 : if (opt.update) {
417 : /* if it is up-to-date, skip */
418 4 : if (stat(newpath, &localstat) == 0 &&
419 4 : localstat.st_mtime >= remotestat.st_mtime) {
420 2 : if (opt.verbose) {
421 2 : printf("%s is up-to-date, skipping\n", newpath);
422 : }
423 2 : smbc_close(remotehandle);
424 2 : return true;
425 : }
426 : /* else open it for writing and truncate if it exists */
427 2 : localhandle = open(
428 : newpath, O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC, 0775);
429 2 : if (localhandle < 0) {
430 0 : fprintf(stderr, "Can't open %s : %s\n", newpath,
431 0 : strerror(errno));
432 0 : smbc_close(remotehandle);
433 0 : return false;
434 : }
435 : /* no offset */
436 54 : } else if (!opt.send_stdout) {
437 54 : localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR |
438 54 : (!resume ? O_EXCL : 0),
439 : 0755);
440 54 : if (localhandle < 0) {
441 0 : fprintf(stderr, "Can't open %s: %s\n", newpath,
442 0 : strerror(errno));
443 0 : smbc_close(remotehandle);
444 0 : return false;
445 : }
446 :
447 54 : if (fstat(localhandle, &localstat) != 0) {
448 0 : fprintf(stderr, "Can't fstat %s: %s\n", newpath,
449 0 : strerror(errno));
450 0 : smbc_close(remotehandle);
451 0 : close(localhandle);
452 0 : return false;
453 : }
454 :
455 54 : start_offset = localstat.st_size;
456 :
457 54 : if (localstat.st_size &&
458 4 : localstat.st_size == remotestat.st_size) {
459 0 : if (opt.verbose) {
460 0 : fprintf(stderr, "%s is already downloaded "
461 : "completely.\n",
462 : path);
463 0 : } else if (!opt.quiet) {
464 0 : fprintf(stderr, "%s\n", path);
465 : }
466 0 : smbc_close(remotehandle);
467 0 : close(localhandle);
468 0 : return true;
469 : }
470 :
471 54 : if (localstat.st_size > RESUME_CHECK_OFFSET &&
472 2 : remotestat.st_size > RESUME_CHECK_OFFSET) {
473 2 : offset_download =
474 2 : localstat.st_size - RESUME_DOWNLOAD_OFFSET;
475 2 : offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
476 2 : if (opt.verbose) {
477 2 : printf("Trying to start resume of %s at %jd\n"
478 : "At the moment %jd of %jd bytes have "
479 : "been retrieved\n",
480 : newpath, (intmax_t)offset_check,
481 2 : (intmax_t)localstat.st_size,
482 2 : (intmax_t)remotestat.st_size);
483 : }
484 : }
485 :
486 54 : if (offset_check) {
487 : off_t off1, off2;
488 : /* First, check all bytes from offset_check to
489 : * offset_download */
490 2 : off1 = lseek(localhandle, offset_check, SEEK_SET);
491 2 : if (off1 < 0) {
492 0 : fprintf(stderr,
493 : "Can't seek to %jd in local file %s\n",
494 : (intmax_t)offset_check, newpath);
495 0 : smbc_close(remotehandle);
496 0 : close(localhandle);
497 0 : return false;
498 : }
499 :
500 2 : off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
501 2 : if (off2 < 0) {
502 0 : fprintf(stderr,
503 : "Can't seek to %jd in remote file %s\n",
504 : (intmax_t)offset_check, newpath);
505 0 : smbc_close(remotehandle);
506 0 : close(localhandle);
507 0 : return false;
508 : }
509 :
510 2 : if (off1 != off2) {
511 0 : fprintf(stderr, "Offset in local and remote "
512 : "files are different "
513 : "(local: %jd, remote: %jd)\n",
514 : (intmax_t)off1, (intmax_t)off2);
515 0 : smbc_close(remotehandle);
516 0 : close(localhandle);
517 0 : return false;
518 : }
519 :
520 2 : if (smbc_read(remotehandle, checkbuf[0],
521 : RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
522 0 : fprintf(stderr, "Can't read %d bytes from "
523 : "remote file %s\n",
524 : RESUME_CHECK_SIZE, path);
525 0 : smbc_close(remotehandle);
526 0 : close(localhandle);
527 0 : return false;
528 : }
529 :
530 2 : if (read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) !=
531 : RESUME_CHECK_SIZE) {
532 0 : fprintf(stderr, "Can't read %d bytes from "
533 : "local file %s\n",
534 : RESUME_CHECK_SIZE, name);
535 0 : smbc_close(remotehandle);
536 0 : close(localhandle);
537 0 : return false;
538 : }
539 :
540 2 : if (memcmp(checkbuf[0], checkbuf[1],
541 : RESUME_CHECK_SIZE) == 0) {
542 0 : if (opt.verbose) {
543 0 : printf("Current local and remote file "
544 : "appear to be the same. "
545 : "Starting download from "
546 : "offset %jd\n",
547 : (intmax_t)offset_download);
548 : }
549 : } else {
550 2 : fprintf(stderr, "Local and remote file appear "
551 : "to be different, not "
552 : "doing resume for %s\n",
553 : path);
554 2 : smbc_close(remotehandle);
555 2 : close(localhandle);
556 2 : return false;
557 : }
558 : }
559 : } else {
560 0 : localhandle = STDOUT_FILENO;
561 0 : start_offset = 0;
562 0 : offset_download = 0;
563 0 : offset_check = 0;
564 : }
565 :
566 : /* We implement rate limiting by filling up a bucket with bytes and
567 : * checking, once the bucket is filled, if it was filled too fast.
568 : * If so, we sleep for some time to get an average transfer rate that
569 : * equals to the one set by the user.
570 : *
571 : * The bucket size directly affects the traffic characteristics.
572 : * The smaller the bucket the more frequent the pause/resume cycle.
573 : * A large bucket can result in burst of high speed traffic and large
574 : * pauses. A cycle of 100ms looks like a good value. This value (in
575 : * ticks) is held in `ticks_to_fill_bucket`. The `bucket_size` is
576 : * calculated as:
577 : * `limit_rate * 1024 * / (CLOCKS_PER_SEC / ticks_to_fill_bucket)`
578 : *
579 : * After selecting the bucket size we also need to check the blocksize
580 : * of the transfer, since this is the minimum unit of traffic that we
581 : * can observe. Achieving a ~10% precision requires a blocksize with a
582 : * maximum size of `bucket_size / 10`.
583 : */
584 54 : if (opt.limit_rate > 0) {
585 : unsigned max_block_size;
586 : /* This is the time that the bucket should take to fill. */
587 2 : ticks_to_fill_bucket = 100 /*ms*/ * CLOCKS_PER_SEC / 1000;
588 : /* This is the size of the bucket in bytes.
589 : * If we fill the bucket too quickly we should pause */
590 2 : bucket_size = opt.limit_rate * 1024 / (CLOCKS_PER_SEC / ticks_to_fill_bucket);
591 2 : max_block_size = bucket_size / 10;
592 2 : max_block_size = max_block_size > 0 ? max_block_size : 1;
593 2 : if (opt.blocksize > max_block_size) {
594 2 : if (opt.blocksize != SMB_DEFAULT_BLOCKSIZE) {
595 0 : fprintf(stderr,
596 : "Warning: Overriding block size to %d "
597 : "due to limit-rate", max_block_size);
598 : }
599 2 : opt.blocksize = max_block_size;
600 : }
601 2 : start_of_bucket_ticks = clock();
602 : }
603 :
604 54 : readbuf = (char *)SMB_MALLOC(opt.blocksize);
605 54 : if (!readbuf) {
606 0 : fprintf(stderr, "Failed to allocate %zu bytes for read "
607 0 : "buffer (%s)", opt.blocksize, strerror(errno));
608 0 : if (localhandle != STDOUT_FILENO) {
609 0 : close(localhandle);
610 : }
611 0 : return false;
612 : }
613 :
614 : /* Now, download all bytes from offset_download to the end */
615 448 : for (curpos = offset_download; curpos < remotestat.st_size;
616 394 : curpos += opt.blocksize) {
617 : ssize_t bytesread;
618 : ssize_t byteswritten;
619 :
620 : /* Rate limiting. This pauses the transfer to limit traffic. */
621 394 : if (opt.limit_rate > 0) {
622 256 : if (bytes_in_bucket > bucket_size) {
623 22 : clock_t now_ticks = clock();
624 22 : clock_t diff_ticks = now_ticks
625 : - start_of_bucket_ticks;
626 : /* Check if the bucket filled up too fast. */
627 22 : if (diff_ticks < ticks_to_fill_bucket) {
628 : /* Pause until `ticks_to_fill_bucket` */
629 22 : double sleep_us
630 22 : = (ticks_to_fill_bucket - diff_ticks)
631 22 : * 1000000.0 / CLOCKS_PER_SEC;
632 22 : usleep(sleep_us);
633 : }
634 : /* Reset the byte counter and the ticks. */
635 22 : bytes_in_bucket = 0;
636 22 : start_of_bucket_ticks = clock();
637 : }
638 : }
639 :
640 394 : bytesread = smbc_read(remotehandle, readbuf, opt.blocksize);
641 394 : if (opt.limit_rate > 0) {
642 256 : bytes_in_bucket += bytesread;
643 : }
644 394 : if(bytesread < 0) {
645 0 : fprintf(stderr,
646 : "Can't read %zu bytes at offset %jd, file %s\n",
647 : opt.blocksize, (intmax_t)curpos, path);
648 0 : smbc_close(remotehandle);
649 0 : if (localhandle != STDOUT_FILENO) {
650 0 : close(localhandle);
651 : }
652 0 : free(readbuf);
653 0 : return false;
654 : }
655 :
656 394 : total_bytes += bytesread;
657 :
658 394 : byteswritten = write(localhandle, readbuf, bytesread);
659 394 : if (byteswritten != bytesread) {
660 0 : fprintf(stderr,
661 : "Can't write %zd bytes to local file %s at "
662 : "offset %jd\n", bytesread, path,
663 : (intmax_t)curpos);
664 0 : free(readbuf);
665 0 : smbc_close(remotehandle);
666 0 : if (localhandle != STDOUT_FILENO) {
667 0 : close(localhandle);
668 : }
669 0 : return false;
670 : }
671 :
672 394 : if (opt.dots) {
673 0 : fputc('.', stderr);
674 394 : } else if (!opt.quiet) {
675 394 : print_progress(newpath, start_time, time_mono(NULL),
676 : start_offset, curpos,
677 : remotestat.st_size);
678 : }
679 : }
680 :
681 54 : free(readbuf);
682 :
683 54 : if (opt.dots) {
684 0 : fputc('\n', stderr);
685 0 : printf("%s downloaded\n", path);
686 54 : } else if (!opt.quiet) {
687 : int i;
688 54 : fprintf(stderr, "\r%s", path);
689 54 : if (columns) {
690 0 : for (i = strlen(path); i < columns; i++) {
691 0 : fputc(' ', stderr);
692 : }
693 : }
694 54 : fputc('\n', stderr);
695 : }
696 :
697 54 : smbc_close(remotehandle);
698 54 : if (localhandle != STDOUT_FILENO) {
699 54 : close(localhandle);
700 : }
701 54 : return true;
702 : }
703 :
704 44 : static void clean_exit(void)
705 : {
706 : char bs[100];
707 44 : human_readable(total_bytes, bs, sizeof(bs));
708 44 : if (!opt.quiet) {
709 44 : fprintf(stderr, "Downloaded %s in %lu seconds\n", bs,
710 44 : (unsigned long)(time_mono(NULL) - total_start_time));
711 : }
712 44 : exit(0);
713 : }
714 :
715 0 : static void signal_quit(int v)
716 : {
717 0 : clean_exit();
718 0 : }
719 :
720 46 : int main(int argc, char **argv)
721 : {
722 46 : int c = 0;
723 46 : const char *file = NULL;
724 46 : int smb_encrypt = false;
725 46 : int resume = 0, recursive = 0;
726 46 : TALLOC_CTX *frame = talloc_stackframe();
727 46 : bool ok = false;
728 46 : const char **argv_const = discard_const_p(const char *, argv);
729 276 : struct poptOption long_options[] = {
730 : POPT_AUTOHELP
731 :
732 : {
733 : .longName = "guest",
734 : .shortName = 'a',
735 : .argInfo = POPT_ARG_NONE,
736 : .arg = NULL,
737 : .val = 'a',
738 : .descrip = "Work as user guest"
739 : },
740 : {
741 : .longName = "encrypt",
742 : .shortName = 'e',
743 : .argInfo = POPT_ARG_NONE,
744 : .arg = &smb_encrypt,
745 : .val = 1,
746 : .descrip = "Encrypt SMB transport"
747 : },
748 : {
749 : .longName = "resume",
750 : .shortName = 'r',
751 : .argInfo = POPT_ARG_NONE,
752 : .arg = &resume,
753 : .val = 1,
754 : .descrip = "Automatically resume aborted files"
755 : },
756 : {
757 : .longName = "update",
758 : .shortName = 'u',
759 : .argInfo = POPT_ARG_NONE,
760 : .arg = &opt.update,
761 : .val = 1,
762 : .descrip = "Download only when remote file is "
763 : "newer than local file or local file "
764 : "is missing"
765 : },
766 : {
767 : .longName = "recursive",
768 : .shortName = 0,
769 : .argInfo = POPT_ARG_NONE,
770 : .arg = &recursive,
771 : .val = true,
772 : .descrip = "Recursively download files"
773 : },
774 : {
775 : .longName = "blocksize",
776 : .shortName = 'b',
777 : .argInfo = POPT_ARG_INT,
778 : .arg = &opt.blocksize,
779 : .val = 'b',
780 : .descrip = "Change number of bytes in a block"
781 : },
782 :
783 : {
784 : .longName = "outputfile",
785 : .shortName = 'o',
786 : .argInfo = POPT_ARG_STRING,
787 : .arg = &opt.outputfile,
788 : .val = 'o',
789 : .descrip = "Write downloaded data to specified file"
790 : },
791 : {
792 : .longName = "stdout",
793 : .shortName = 0,
794 : .argInfo = POPT_ARG_NONE,
795 : .arg = &opt.send_stdout,
796 : .val = true,
797 : .descrip = "Write data to stdout"
798 : },
799 : {
800 : .longName = "dots",
801 : .shortName = 'D',
802 : .argInfo = POPT_ARG_NONE,
803 : .arg = &opt.dots,
804 : .val = 1,
805 : .descrip = "Show dots as progress indication"
806 : },
807 : {
808 : .longName = "quiet",
809 : .shortName = 'q',
810 : .argInfo = POPT_ARG_NONE,
811 : .arg = &opt.quiet,
812 : .val = 1,
813 : .descrip = "Be quiet"
814 : },
815 : {
816 : .longName = "verbose",
817 : .shortName = 'v',
818 : .argInfo = POPT_ARG_NONE,
819 : .arg = &opt.verbose,
820 : .val = 1,
821 : .descrip = "Be verbose"
822 : },
823 : {
824 : .longName = "limit-rate",
825 : .shortName = 0,
826 : .argInfo = POPT_ARG_INT,
827 : .arg = &opt.limit_rate,
828 : .val = 'l',
829 : .descrip = "Limit download speed to this many KB/s"
830 : },
831 :
832 46 : POPT_COMMON_SAMBA
833 46 : POPT_COMMON_CONNECTION
834 46 : POPT_COMMON_CREDENTIALS
835 46 : POPT_LEGACY_S3
836 46 : POPT_COMMON_VERSION
837 : POPT_TABLEEND
838 : };
839 46 : poptContext pc = NULL;
840 46 : struct cli_credentials *creds = NULL;
841 46 : enum smb_encryption_setting encryption_state = SMB_ENCRYPTION_DEFAULT;
842 46 : enum credentials_use_kerberos use_kerberos = CRED_USE_KERBEROS_DESIRED;
843 46 : smbc_smb_encrypt_level encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT;
844 : #if 0
845 : enum smb_signing_setting signing_state = SMB_SIGNING_DEFAULT;
846 : const char *use_signing = "auto";
847 : #endif
848 46 : bool is_nt_hash = false;
849 : uint32_t gensec_features;
850 46 : bool use_wbccache = false;
851 46 : SMBCCTX *smb_ctx = NULL;
852 : int rc;
853 :
854 46 : smb_init_locale();
855 :
856 46 : ok = samba_cmdline_init(frame,
857 : SAMBA_CMDLINE_CONFIG_CLIENT,
858 : false);
859 46 : if (!ok) {
860 0 : goto done;
861 : }
862 :
863 : #ifdef SIGWINCH
864 46 : signal(SIGWINCH, change_columns);
865 : #endif
866 46 : signal(SIGINT, signal_quit);
867 46 : signal(SIGTERM, signal_quit);
868 :
869 46 : pc = samba_popt_get_context(getprogname(),
870 : argc,
871 : argv_const,
872 : long_options,
873 : 0);
874 46 : if (pc == NULL) {
875 0 : ok = false;
876 0 : goto done;
877 : }
878 :
879 46 : creds = samba_cmdline_get_creds();
880 :
881 112 : while ((c = poptGetNextOpt(pc)) != -1) {
882 66 : switch (c) {
883 4 : case 'a':
884 4 : cli_credentials_set_anonymous(creds);
885 4 : break;
886 0 : case POPT_ERROR_BADOPT:
887 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
888 : poptBadOption(pc, 0), poptStrerror(c));
889 0 : poptPrintUsage(pc, stderr, 0);
890 0 : ok = false;
891 0 : goto done;
892 : }
893 :
894 66 : if (c < -1) {
895 0 : fprintf(stderr, "%s: %s\n",
896 : poptBadOption(pc, POPT_BADOPTION_NOALIAS),
897 : poptStrerror(c));
898 0 : ok = false;
899 0 : goto done;
900 : }
901 : }
902 :
903 46 : if ((opt.send_stdout || resume || opt.outputfile) && opt.update) {
904 0 : fprintf(stderr, "The -o, -R or -O and -U options can not be "
905 : "used together.\n");
906 0 : ok = true;
907 0 : goto done;
908 : }
909 46 : if ((opt.send_stdout || opt.outputfile) && recursive) {
910 0 : fprintf(stderr, "The -o or -O and -R options can not be "
911 : "used together.\n");
912 0 : ok = true;
913 0 : goto done;
914 : }
915 :
916 46 : if (opt.outputfile && opt.send_stdout) {
917 0 : fprintf(stderr, "The -o and -O options can not be "
918 : "used together.\n");
919 0 : ok = true;
920 0 : goto done;
921 : }
922 :
923 46 : samba_cmdline_burn(argc, argv);
924 :
925 46 : smb_ctx = smbc_new_context();
926 46 : if (smb_ctx == NULL) {
927 0 : fprintf(stderr, "Unable to initialize libsmbclient\n");
928 0 : ok = false;
929 0 : goto done;
930 : }
931 46 : smbc_setDebug(smb_ctx, debuglevel_get());
932 :
933 46 : rc = smbc_setConfiguration(smb_ctx, lp_default_path());
934 46 : if (rc < 0) {
935 0 : ok = false;
936 0 : goto done;
937 : }
938 :
939 46 : smbc_setFunctionAuthDataWithContext(smb_ctx,
940 : get_auth_data_with_context_fn);
941 :
942 46 : ok = smbc_init_context(smb_ctx);
943 46 : if (!ok) {
944 0 : goto done;
945 : }
946 46 : smbc_set_context(smb_ctx);
947 :
948 46 : encryption_state = cli_credentials_get_smb_encryption(creds);
949 46 : switch (encryption_state) {
950 2 : case SMB_ENCRYPTION_REQUIRED:
951 2 : encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE;
952 2 : break;
953 0 : case SMB_ENCRYPTION_DESIRED:
954 : case SMB_ENCRYPTION_IF_REQUIRED:
955 0 : encrypt_level = SMBC_ENCRYPTLEVEL_REQUEST;
956 0 : break;
957 0 : case SMB_ENCRYPTION_OFF:
958 0 : encrypt_level = SMBC_ENCRYPTLEVEL_NONE;
959 0 : break;
960 44 : case SMB_ENCRYPTION_DEFAULT:
961 44 : encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT;
962 44 : break;
963 : }
964 46 : if (smb_encrypt) {
965 2 : encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE;
966 : }
967 46 : smbc_setOptionSmbEncryptionLevel(smb_ctx, encrypt_level);
968 :
969 : #if 0
970 : signing_state = cli_credentials_get_smb_signing(creds);
971 : if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
972 : signing_state = SMB_SIGNING_REQUIRED;
973 : }
974 : switch (signing_state) {
975 : case SMB_SIGNING_REQUIRED:
976 : use_signing = "required";
977 : break;
978 : case SMB_SIGNING_DEFAULT:
979 : case SMB_SIGNING_DESIRED:
980 : case SMB_SIGNING_IF_REQUIRED:
981 : use_signing = "yes";
982 : break;
983 : case SMB_SIGNING_OFF:
984 : use_signing = "off";
985 : break;
986 : default:
987 : use_signing = "auto";
988 : break;
989 : }
990 : /* FIXME: There is no libsmbclient function to set signing state */
991 : #endif
992 :
993 46 : use_kerberos = cli_credentials_get_kerberos_state(creds);
994 46 : switch (use_kerberos) {
995 6 : case CRED_USE_KERBEROS_REQUIRED:
996 6 : smbc_setOptionUseKerberos(smb_ctx, true);
997 6 : smbc_setOptionFallbackAfterKerberos(smb_ctx, false);
998 6 : break;
999 36 : case CRED_USE_KERBEROS_DESIRED:
1000 36 : smbc_setOptionUseKerberos(smb_ctx, true);
1001 36 : smbc_setOptionFallbackAfterKerberos(smb_ctx, true);
1002 36 : break;
1003 4 : case CRED_USE_KERBEROS_DISABLED:
1004 4 : smbc_setOptionUseKerberos(smb_ctx, false);
1005 4 : break;
1006 : }
1007 :
1008 : /* Check if the password supplied is an NT hash */
1009 46 : is_nt_hash = cli_credentials_is_password_nt_hash(creds);
1010 46 : smbc_setOptionUseNTHash(smb_ctx, is_nt_hash);
1011 :
1012 : /* Check if we should use the winbind ccache */
1013 46 : gensec_features = cli_credentials_get_gensec_features(creds);
1014 46 : use_wbccache = (gensec_features & GENSEC_FEATURE_NTLM_CCACHE);
1015 46 : smbc_setOptionUseCCache(smb_ctx, use_wbccache);
1016 :
1017 46 : columns = get_num_cols();
1018 :
1019 46 : total_start_time = time_mono(NULL);
1020 :
1021 92 : while ((file = poptGetArg(pc))) {
1022 46 : if (!recursive) {
1023 40 : ok = smb_download_file(file, "", recursive, resume,
1024 : true, opt.outputfile);
1025 : } else {
1026 6 : ok = smb_download_dir(file, "", resume);
1027 : }
1028 : }
1029 :
1030 46 : done:
1031 46 : gfree_all();
1032 46 : poptFreeContext(pc);
1033 46 : TALLOC_FREE(frame);
1034 46 : if (ok) {
1035 44 : clean_exit();
1036 : }
1037 2 : return ok ? 0 : 1;
1038 : }
|