This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "includes.h"
#include "libsmbclient.h"
#if _FILE_OFFSET_BITS==64
#define OFF_T_FORMAT "%lld"
+#define OFF_T_FORMAT_CAST long long
#else
#define OFF_T_FORMAT "%ld"
+#define OFF_T_FORMAT_CAST long
#endif
-int columns = 0;
+static int columns = 0;
-time_t total_start_time = 0;
-off_t total_bytes = 0;
+static int debuglevel, update;
+static char *outputfile;
+
+
+static time_t total_start_time = 0;
+static off_t total_bytes = 0;
#define SMB_MAXPATHLEN MAXPATHLEN
/* Number of bytes to read at once */
#define SMB_DEFAULT_BLOCKSIZE 64000
-const char *username = NULL, *password = NULL, *workgroup = NULL;
-int nonprompt = 0, quiet = 0, dots = 0, keep_permissions = 0, verbose = 0;
-int blocksize = SMB_DEFAULT_BLOCKSIZE;
+static const char *username = NULL, *password = NULL, *workgroup = NULL;
+static int nonprompt = 0, quiet = 0, dots = 0, keep_permissions = 0, verbose = 0, send_stdout = 0;
+static int blocksize = SMB_DEFAULT_BLOCKSIZE;
-int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile);
+static int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile);
-int get_num_cols(void)
+static int get_num_cols(void)
{
#ifdef TIOCGWINSZ
struct winsize ws;
if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
- perror("ioctl");
return 0;
}
return ws.ws_col;
#endif
}
-void change_columns(int sig)
+static void change_columns(int sig)
{
columns = get_num_cols();
}
-void human_readable(off_t s, char *buffer, int l)
+static void human_readable(off_t s, char *buffer, int l)
{
if(s > 1024 * 1024 * 1024) snprintf(buffer, l, "%.2fGb", 1.0 * s / (1024 * 1024 * 1024));
else if(s > 1024 * 1024) snprintf(buffer, l, "%.2fMb", 1.0 * s / (1024 * 1024));
else if(s > 1024) snprintf(buffer, l, "%.2fkb", 1.0 * s / 1024);
- else snprintf(buffer, l, OFF_T_FORMAT"b", s);
+ else snprintf(buffer, l, OFF_T_FORMAT"b", (OFF_T_FORMAT_CAST)s);
}
-void get_auth_data(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen)
+static void get_auth_data(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen)
{
static char hasasked = 0;
char *wgtmp, *usertmp;
if(!nonprompt && !username) {
printf("Username for %s at %s [guest] ", shr, srv);
- fgets(tmp, sizeof(tmp), stdin);
+ if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
+ return;
+ }
if(tmp[strlen(tmp)-1] == '\n')tmp[strlen(tmp)-1] = '\0';
strncpy(un, tmp, unlen-1);
} else if(username) strncpy(un, username, unlen-1);
if(!nonprompt && !password) {
char *prompt, *pass;
- asprintf(&prompt, "Password for %s at %s: ", shr, srv);
+ if (asprintf(&prompt, "Password for %s at %s: ", shr, srv) == -1) {
+ return;
+ }
pass = getpass(prompt);
free(prompt);
strncpy(pw, pass, pwlen-1);
if(workgroup)strncpy(wg, workgroup, wglen-1);
- wgtmp = strndup(wg, wglen);
- usertmp = strndup(un, unlen);
+ wgtmp = SMB_STRNDUP(wg, wglen);
+ usertmp = SMB_STRNDUP(un, unlen);
if(!quiet)printf("Using workgroup %s, %s%s\n", wgtmp, *usertmp?"user ":"guest user", usertmp);
free(wgtmp); free(usertmp);
}
-int smb_download_dir(const char *base, const char *name, int resume)
+static int smb_download_dir(const char *base, const char *name, int resume)
{
char path[SMB_MAXPATHLEN];
int dirhandle;
while(*relname == '/')relname++;
mkdir(relname, 0755);
- tmpname = strdup(name);
+ tmpname = SMB_STRDUP(name);
while((dirent = smbc_readdir(dirhandle))) {
char *newname;
if(!strcmp(dirent->name, ".") || !strcmp(dirent->name, ".."))continue;
- asprintf(&newname, "%s/%s", tmpname, dirent->name);
+ if (asprintf(&newname, "%s/%s", tmpname, dirent->name) == -1) {
+ return 0;
+ }
switch(dirent->smbc_type) {
case SMBC_DIR:
smb_download_dir(base, newname, resume);
return 1;
}
-char *print_time(long t)
+static char *print_time(long t)
{
static char buffer[100];
int secs, mins, hours;
return buffer;
}
-void print_progress(const char *name, time_t start, time_t now, off_t start_pos, off_t pos, off_t total)
+static void print_progress(const char *name, time_t start, time_t now, off_t start_pos, off_t pos, off_t total)
{
double avg = 0.0;
long eta = -1;
human_readable(avg, havg, sizeof(havg));
len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos, htotal, prcnt, havg, print_time(eta));
+ if (len == -1) {
+ return;
+ }
if(columns) {
int required = strlen(name), available = columns - len - strlen("[] ");
- if(required > available) asprintf(&filename, "...%s", name + required - available + 3);
- else filename = strndup(name, available);
- } else filename = strdup(name);
+ if(required > available) {
+ if (asprintf(&filename, "...%s", name + required - available + 3) == -1) {
+ return;
+ }
+ } else {
+ filename = SMB_STRNDUP(name, available);
+ }
+ } else filename = SMB_STRDUP(name);
fprintf(stderr, "\r[%s] %s", filename, status);
free(filename); free(status);
}
-int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile) {
+static int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile) {
int remotehandle, localhandle;
time_t start_time = time(NULL);
const char *newpath;
if(newpath[0] == '/')newpath++;
- /* Open local file and, if necessary, resume */
- localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | (!resume?O_EXCL:0), 0755);
- if(localhandle < 0) {
- fprintf(stderr, "Can't open %s: %s\n", newpath, strerror(errno));
- smbc_close(remotehandle);
- return 0;
- }
-
- fstat(localhandle, &localstat);
-
- start_offset = localstat.st_size;
-
- if(localstat.st_size && localstat.st_size == remotestat.st_size) {
- if(verbose)fprintf(stderr, "%s is already downloaded completely.\n", path);
- else if(!quiet)fprintf(stderr, "%s\n", path);
- smbc_close(remotehandle);
- close(localhandle);
- return 1;
- }
-
- if(localstat.st_size > RESUME_CHECK_OFFSET && remotestat.st_size > RESUME_CHECK_OFFSET) {
- offset_download = localstat.st_size - RESUME_DOWNLOAD_OFFSET;
- offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
- if(verbose)printf("Trying to start resume of %s at "OFF_T_FORMAT"\n"
- "At the moment "OFF_T_FORMAT" of "OFF_T_FORMAT" bytes have been retrieved\n", newpath, offset_check,
- localstat.st_size, remotestat.st_size);
- }
-
- if(offset_check) {
- off_t off1, off2;
- /* First, check all bytes from offset_check to offset_download */
- off1 = lseek(localhandle, offset_check, SEEK_SET);
- if(off1 < 0) {
- fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in local file %s\n", offset_check, newpath);
- smbc_close(remotehandle); close(localhandle);
+ /* Open local file according to the mode */
+ if(update) {
+ /* if it is up-to-date, skip */
+ if(stat(newpath, &localstat) == 0 &&
+ localstat.st_mtime >= remotestat.st_mtime) {
+ if(verbose)
+ printf("%s is up-to-date, skipping\n", newpath);
+ smbc_close(remotehandle);
return 0;
}
-
- off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
- if(off2 < 0) {
- fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in remote file %s\n", offset_check, newpath);
- smbc_close(remotehandle); close(localhandle);
+ /* else open it for writing and truncate if it exists */
+ localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC, 0775);
+ if(localhandle < 0) {
+ fprintf(stderr, "Can't open %s : %s\n", newpath,
+ strerror(errno));
+ smbc_close(remotehandle);
return 0;
}
-
- if(off1 != off2) {
- fprintf(stderr, "Offset in local and remote files is different (local: "OFF_T_FORMAT", remote: "OFF_T_FORMAT")\n", off1, off2);
+ /* no offset */
+ } else if(!send_stdout) {
+ localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | (!resume?O_EXCL:0), 0755);
+ if(localhandle < 0) {
+ fprintf(stderr, "Can't open %s: %s\n", newpath, strerror(errno));
+ smbc_close(remotehandle);
return 0;
}
-
- if(smbc_read(remotehandle, checkbuf[0], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
- fprintf(stderr, "Can't read %d bytes from remote file %s\n", RESUME_CHECK_SIZE, path);
- smbc_close(remotehandle); close(localhandle);
+
+ if (fstat(localhandle, &localstat) != 0) {
+ fprintf(stderr, "Can't fstat %s: %s\n", newpath, strerror(errno));
+ smbc_close(remotehandle);
+ close(localhandle);
return 0;
}
- if(read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
- fprintf(stderr, "Can't read %d bytes from local file %s\n", RESUME_CHECK_SIZE, name);
- smbc_close(remotehandle); close(localhandle);
- return 0;
+ start_offset = localstat.st_size;
+
+ if(localstat.st_size && localstat.st_size == remotestat.st_size) {
+ if(verbose)fprintf(stderr, "%s is already downloaded completely.\n", path);
+ else if(!quiet)fprintf(stderr, "%s\n", path);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 1;
}
- if(memcmp(checkbuf[0], checkbuf[1], RESUME_CHECK_SIZE) == 0) {
- if(verbose)printf("Current local and remote file appear to be the same. Starting download from offset "OFF_T_FORMAT"\n", offset_download);
- } else {
- fprintf(stderr, "Local and remote file appear to be different, not doing resume for %s\n", path);
- smbc_close(remotehandle); close(localhandle);
- return 0;
+ if(localstat.st_size > RESUME_CHECK_OFFSET && remotestat.st_size > RESUME_CHECK_OFFSET) {
+ offset_download = localstat.st_size - RESUME_DOWNLOAD_OFFSET;
+ offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
+ if(verbose)printf("Trying to start resume of %s at "OFF_T_FORMAT"\n"
+ "At the moment "OFF_T_FORMAT" of "OFF_T_FORMAT" bytes have been retrieved\n",
+ newpath, (OFF_T_FORMAT_CAST)offset_check,
+ (OFF_T_FORMAT_CAST)localstat.st_size,
+ (OFF_T_FORMAT_CAST)remotestat.st_size);
+ }
+
+ if(offset_check) {
+ off_t off1, off2;
+ /* First, check all bytes from offset_check to offset_download */
+ off1 = lseek(localhandle, offset_check, SEEK_SET);
+ if(off1 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in local file %s\n",
+ (OFF_T_FORMAT_CAST)offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
+ if(off2 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in remote file %s\n",
+ (OFF_T_FORMAT_CAST)offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(off1 != off2) {
+ fprintf(stderr, "Offset in local and remote files is different (local: "OFF_T_FORMAT", remote: "OFF_T_FORMAT")\n",
+ (OFF_T_FORMAT_CAST)off1,
+ (OFF_T_FORMAT_CAST)off2);
+ return 0;
+ }
+
+ if(smbc_read(remotehandle, checkbuf[0], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from remote file %s\n", RESUME_CHECK_SIZE, path);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from local file %s\n", RESUME_CHECK_SIZE, name);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(memcmp(checkbuf[0], checkbuf[1], RESUME_CHECK_SIZE) == 0) {
+ if(verbose)printf("Current local and remote file appear to be the same. Starting download from offset "OFF_T_FORMAT"\n", (OFF_T_FORMAT_CAST)offset_download);
+ } else {
+ fprintf(stderr, "Local and remote file appear to be different, not doing resume for %s\n", path);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
}
+ } else {
+ localhandle = STDOUT_FILENO;
+ start_offset = 0;
+ offset_download = 0;
+ offset_check = 0;
}
- readbuf = malloc(blocksize);
+ readbuf = (char *)SMB_MALLOC(blocksize);
/* Now, download all bytes from offset_download to the end */
for(curpos = offset_download; curpos < remotestat.st_size; curpos+=blocksize) {
ssize_t bytesread = smbc_read(remotehandle, readbuf, blocksize);
if(bytesread < 0) {
- fprintf(stderr, "Can't read %d bytes at offset "OFF_T_FORMAT", file %s\n", blocksize, curpos, path);
- smbc_close(remotehandle); close(localhandle);
+ fprintf(stderr, "Can't read %u bytes at offset "OFF_T_FORMAT", file %s\n", (unsigned int)blocksize, (OFF_T_FORMAT_CAST)curpos, path);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
free(readbuf);
return 0;
}
total_bytes += bytesread;
if(write(localhandle, readbuf, bytesread) < 0) {
- fprintf(stderr, "Can't write %d bytes to local file %s at offset "OFF_T_FORMAT"\n", bytesread, path, curpos);
+ fprintf(stderr, "Can't write %u bytes to local file %s at offset "OFF_T_FORMAT"\n", (unsigned int)bytesread, path, (OFF_T_FORMAT_CAST)curpos);
free(readbuf);
- smbc_close(remotehandle); close(localhandle);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
return 0;
}
fputc('\n', stderr);
}
- if(keep_permissions) {
+ if(keep_permissions && !send_stdout) {
if(fchmod(localhandle, remotestat.st_mode) < 0) {
fprintf(stderr, "Unable to change mode of local file %s to %o\n", path, remotestat.st_mode);
smbc_close(remotehandle);
return 0;
}
}
+
smbc_close(remotehandle);
- close(localhandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
return 1;
}
-void clean_exit(void)
+static void clean_exit(void)
{
char bs[100];
human_readable(total_bytes, bs, sizeof(bs));
exit(0);
}
-void signal_quit(int v)
+static void signal_quit(int v)
{
clean_exit();
}
-int readrcfile(const char *name, const struct poptOption long_options[])
+static int readrcfile(const char *name, const struct poptOption long_options[])
{
FILE *fd = fopen(name, "r");
int lineno = 0, i;
break;
case POPT_ARG_STRING:
stringdata = (char **)long_options[i].arg;
- *stringdata = strdup(val);
+ *stringdata = SMB_STRDUP(val);
break;
default:
fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
int main(int argc, const char **argv)
{
- int resume = 0, recursive = 0;
int c = 0;
- int debuglevel = 0;
const char *file = NULL;
char *rcfile = NULL;
- char *outputfile = NULL;
+ bool smb_encrypt = false;
+ int resume = 0, recursive = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
struct poptOption long_options[] = {
{"guest", 'a', POPT_ARG_NONE, NULL, 'a', "Work as user guest" },
+ {"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport (UNIX extended servers only)" },
{"resume", 'r', POPT_ARG_NONE, &resume, 0, "Automatically resume aborted files" },
+ {"update", 'U', POPT_ARG_NONE, &update, 0, "Download only when remote file is newer than local file or local file is missing"},
{"recursive", 'R', POPT_ARG_NONE, &recursive, 0, "Recursively download files" },
{"username", 'u', POPT_ARG_STRING, &username, 'u', "Username to use" },
{"password", 'p', POPT_ARG_STRING, &password, 'p', "Password to use" },
{"nonprompt", 'n', POPT_ARG_NONE, &nonprompt, 'n', "Don't ask anything (non-interactive)" },
{"debuglevel", 'd', POPT_ARG_INT, &debuglevel, 'd', "Debuglevel to use" },
{"outputfile", 'o', POPT_ARG_STRING, &outputfile, 'o', "Write downloaded data to specified file" },
+ {"stdout", 'O', POPT_ARG_NONE, &send_stdout, 'O', "Write data to stdout" },
{"dots", 'D', POPT_ARG_NONE, &dots, 'D', "Show dots as progress indication" },
{"quiet", 'q', POPT_ARG_NONE, &quiet, 'q', "Be quiet" },
{"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
};
poptContext pc;
+ load_case_tables();
+
/* only read rcfile if it exists */
- asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME"));
- if(access(rcfile, F_OK) == 0) readrcfile(rcfile, long_options);
+ if (asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME")) == -1) {
+ return 1;
+ }
+ if(access(rcfile, F_OK) == 0)
+ readrcfile(rcfile, long_options);
free(rcfile);
#ifdef SIGWINCH
case 'a':
username = ""; password = "";
break;
+ case 'e':
+ smb_encrypt = true;
+ break;
}
}
- if(outputfile && recursive) {
- fprintf(stderr, "The -o and -R options can not be used together.\n");
+ if((send_stdout || resume || outputfile) && update) {
+ fprintf(stderr, "The -o, -R or -O and -U options can not be used together.\n");
+ return 1;
+ }
+ if((send_stdout || outputfile) && recursive) {
+ fprintf(stderr, "The -o or -O and -R options can not be used together.\n");
+ return 1;
+ }
+
+ if(outputfile && send_stdout) {
+ fprintf(stderr, "The -o and -O options cannot be used together.\n");
return 1;
}
return 1;
}
+ if (smb_encrypt) {
+ SMBCCTX *smb_ctx = smbc_set_context(NULL);
+ smbc_option_set(smb_ctx,
+ CONST_DISCARD(char *, "smb_encrypt_level"),
+ "require");
+ }
+
columns = get_num_cols();
total_start_time = time(NULL);
- while((file = poptGetArg(pc))) {
- if(!recursive) return smb_download_file(file, "", recursive, resume, outputfile);
- else return smb_download_dir(file, "", resume);
+ while ( (file = poptGetArg(pc)) ) {
+ if (!recursive)
+ return smb_download_file(file, "", recursive, resume, outputfile);
+ else
+ return smb_download_dir(file, "", resume);
}
clean_exit();
-
+ TALLOC_FREE(frame);
return 0;
}