2004-01-07 03:43:52 +03:00
/*
2016-01-24 13:17:20 +03:00
smbget : a wget - like utility with support for recursive downloading of
smb : // urls
2004-01-07 03:43:52 +03:00
Copyright ( C ) 2003 - 2004 Jelmer Vernooij < jelmer @ samba . org >
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-01-07 03:43:52 +03:00
( at your option ) any later version .
2010-07-04 13:02:21 +04:00
2004-01-07 03:43:52 +03:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2010-07-04 13:02:21 +04:00
2004-01-07 03:43:52 +03:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>. */
2004-01-07 03:43:52 +03:00
# include "includes.h"
2011-04-26 16:58:01 +04:00
# include "system/filesys.h"
2018-08-14 01:39:08 +03:00
# include "popt_common_cmdline.h"
2004-01-07 03:43:52 +03:00
# include "libsmbclient.h"
2018-08-22 02:11:02 +03:00
# include "cmdline_contexts.h"
2004-01-07 03:43:52 +03:00
2008-03-09 15:50:56 +03:00
static int columns = 0 ;
2004-01-07 03:43:52 +03:00
2008-03-09 15:50:56 +03:00
static time_t total_start_time = 0 ;
static off_t total_bytes = 0 ;
2004-01-07 03:43:52 +03:00
# define SMB_MAXPATHLEN MAXPATHLEN
2016-01-24 13:17:20 +03:00
/*
* Number of bytes to read when checking whether local and remote file
* are really the same file
*/
# define RESUME_CHECK_SIZE 512
# define RESUME_DOWNLOAD_OFFSET 1024
# define RESUME_CHECK_OFFSET (RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE)
2004-01-07 03:43:52 +03:00
/* Number of bytes to read at once */
2016-01-24 13:17:20 +03:00
# define SMB_DEFAULT_BLOCKSIZE 64000
2004-01-07 03:43:52 +03:00
2015-10-28 14:37:36 +03:00
struct opt {
char * workgroup ;
bool username_specified ;
char * username ;
bool password_specified ;
char * password ;
char * outputfile ;
size_t blocksize ;
bool nonprompt ;
bool quiet ;
bool dots ;
bool verbose ;
bool send_stdout ;
bool update ;
int debuglevel ;
} ;
2016-02-04 23:39:47 +03:00
static struct opt opt = { . blocksize = SMB_DEFAULT_BLOCKSIZE } ;
2004-01-07 03:43:52 +03:00
2016-01-24 13:57:01 +03:00
static bool smb_download_file ( const char * base , const char * name ,
bool recursive , bool resume , bool toplevel ,
char * outfile ) ;
2004-01-07 03:43:52 +03:00
2006-12-19 23:16:52 +03:00
static int get_num_cols ( void )
2004-01-07 03:43:52 +03:00
{
# ifdef TIOCGWINSZ
struct winsize ws ;
2016-01-24 13:17:20 +03:00
if ( ioctl ( STDOUT_FILENO , TIOCGWINSZ , & ws ) < 0 ) {
2004-01-07 03:43:52 +03:00
return 0 ;
}
return ws . ws_col ;
# else
# warning No support for TIOCGWINSZ
char * cols = getenv ( " COLUMNS " ) ;
2016-01-24 13:17:20 +03:00
if ( ! cols ) {
return 0 ;
}
2004-01-07 03:43:52 +03:00
return atoi ( cols ) ;
# endif
}
2006-12-19 23:16:52 +03:00
static void change_columns ( int sig )
2004-01-07 03:43:52 +03:00
{
columns = get_num_cols ( ) ;
}
2006-12-19 23:16:52 +03:00
static void human_readable ( off_t s , char * buffer , int l )
2004-01-07 03:43:52 +03:00
{
2011-04-08 00:03:49 +04:00
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 {
2016-01-24 13:45:59 +03:00
snprintf ( buffer , l , " %jdb " , ( intmax_t ) s ) ;
2011-04-08 00:03:49 +04:00
}
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
static void get_auth_data ( const char * srv , const char * shr , char * wg , int wglen ,
char * un , int unlen , char * pw , int pwlen )
2004-01-07 03:43:52 +03:00
{
2016-01-24 13:57:01 +03:00
static bool hasasked = false ;
2015-12-30 23:25:13 +03:00
static char * savedwg ;
static char * savedun ;
static char * savedpw ;
2004-01-07 03:43:52 +03:00
2015-12-30 23:25:13 +03:00
if ( hasasked ) {
strncpy ( wg , savedwg , wglen - 1 ) ;
strncpy ( un , savedun , unlen - 1 ) ;
strncpy ( pw , savedpw , pwlen - 1 ) ;
return ;
}
2016-01-24 13:57:01 +03:00
hasasked = true ;
2004-01-07 03:43:52 +03:00
2016-08-25 15:24:08 +03:00
/*
* If no user has been specified un is initialized with the current
* username of the user who started smbget .
*/
if ( opt . username_specified ) {
2015-10-28 14:37:36 +03:00
strncpy ( un , opt . username , unlen - 1 ) ;
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
2016-08-25 15:24:08 +03:00
if ( ! opt . nonprompt & & ! opt . password_specified & & pw [ 0 ] = = ' \0 ' ) {
2012-11-23 17:38:14 +04:00
char * prompt ;
2016-08-25 15:24:08 +03:00
int rc ;
rc = asprintf ( & prompt ,
" Password for [%s] connecting to //%s/%s: " ,
un , shr , srv ) ;
if ( rc = = - 1 ) {
2009-01-01 05:06:57 +03:00
return ;
}
2016-01-24 13:17:20 +03:00
( void ) samba_getpass ( prompt , pw , pwlen , false , false ) ;
2004-01-07 03:43:52 +03:00
free ( prompt ) ;
2015-10-28 14:37:36 +03:00
} else if ( opt . password ! = NULL ) {
strncpy ( pw , opt . password , pwlen - 1 ) ;
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
2015-10-28 14:37:36 +03:00
if ( opt . workgroup ! = NULL ) {
strncpy ( wg , opt . workgroup , wglen - 1 ) ;
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
2015-12-30 23:25:13 +03:00
/* save the values found for later */
savedwg = SMB_STRDUP ( wg ) ;
savedun = SMB_STRDUP ( un ) ;
savedpw = SMB_STRDUP ( pw ) ;
2015-10-28 14:37:36 +03:00
if ( ! opt . quiet ) {
2016-01-24 13:17:20 +03:00
char * wgtmp , * usertmp ;
wgtmp = SMB_STRNDUP ( wg , wglen ) ;
usertmp = SMB_STRNDUP ( un , unlen ) ;
2015-10-28 14:37:36 +03:00
printf ( " Using workgroup %s, %s%s \n " ,
wgtmp ,
* usertmp ? " user " : " guest user " ,
usertmp ) ;
2016-01-24 13:17:20 +03:00
free ( wgtmp ) ;
free ( usertmp ) ;
}
2004-01-07 03:43:52 +03:00
}
2016-02-09 01:24:36 +03:00
static bool smb_download_dir ( const char * base , const char * name , int resume )
2004-01-07 03:43:52 +03:00
{
char path [ SMB_MAXPATHLEN ] ;
int dirhandle ;
struct smbc_dirent * dirent ;
const char * relname = name ;
char * tmpname ;
2016-02-09 01:24:36 +03:00
bool ok = false ;
2010-01-28 21:38:24 +03:00
2016-01-24 13:17:20 +03:00
snprintf ( path , SMB_MAXPATHLEN - 1 , " %s%s%s " , base ,
( base [ 0 ] & & name [ 0 ] & & name [ 0 ] ! = ' / ' & &
base [ strlen ( base ) - 1 ] ! = ' / ' ) ? " / " : " " ,
name ) ;
2004-01-07 03:43:52 +03:00
/* List files in directory and call smb_download_file on them */
dirhandle = smbc_opendir ( path ) ;
2016-01-24 13:17:20 +03:00
if ( dirhandle < 1 ) {
2010-07-04 13:32:50 +04:00
if ( errno = = ENOTDIR ) {
2016-01-24 13:57:01 +03:00
return smb_download_file ( base , name , true , resume ,
false , NULL ) ;
2010-07-04 13:32:50 +04:00
}
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " Can't open directory %s: %s \n " , path ,
strerror ( errno ) ) ;
2016-02-09 01:24:36 +03:00
return false ;
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
while ( * relname = = ' / ' ) {
relname + + ;
}
2018-10-22 17:28:21 +03:00
if ( strlen ( relname ) > 0 ) {
int rc = mkdir ( relname , 0755 ) ;
if ( rc = = - 1 & & errno ! = EEXIST ) {
fprintf ( stderr , " Can't create directory %s: %s \n " ,
relname , strerror ( errno ) ) ;
return false ;
}
}
2010-07-04 13:02:21 +04:00
2004-12-17 00:06:33 +03:00
tmpname = SMB_STRDUP ( name ) ;
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
while ( ( dirent = smbc_readdir ( dirhandle ) ) ) {
2004-01-07 03:43:52 +03:00
char * newname ;
2016-01-24 13:17:20 +03:00
if ( ! strcmp ( dirent - > name , " . " ) | | ! strcmp ( dirent - > name , " .. " ) ) {
2018-10-23 21:05:04 +03:00
ok = true ;
2016-01-24 13:17:20 +03:00
continue ;
}
2009-01-01 05:06:57 +03:00
if ( asprintf ( & newname , " %s/%s " , tmpname , dirent - > name ) = = - 1 ) {
2013-12-04 17:01:55 +04:00
free ( tmpname ) ;
2016-02-09 01:24:36 +03:00
return false ;
2009-01-01 05:06:57 +03:00
}
2016-01-24 13:17:20 +03:00
switch ( dirent - > smbc_type ) {
2004-01-07 03:43:52 +03:00
case SMBC_DIR :
2016-02-09 01:24:36 +03:00
ok = smb_download_dir ( base , newname , resume ) ;
2004-01-07 03:43:52 +03:00
break ;
case SMBC_WORKGROUP :
2016-02-09 01:24:36 +03:00
ok = smb_download_dir ( " smb:// " , dirent - > name , resume ) ;
2004-01-07 03:43:52 +03:00
break ;
case SMBC_SERVER :
2016-02-09 01:24:36 +03:00
ok = smb_download_dir ( " smb:// " , dirent - > name , resume ) ;
2004-01-07 03:43:52 +03:00
break ;
case SMBC_FILE :
2016-02-09 01:24:36 +03:00
ok = smb_download_file ( base , newname , true , resume ,
2016-01-24 13:57:01 +03:00
false , NULL ) ;
2004-01-07 03:43:52 +03:00
break ;
case SMBC_FILE_SHARE :
2016-02-09 01:24:36 +03:00
ok = smb_download_dir ( base , newname , resume ) ;
2004-01-07 03:43:52 +03:00
break ;
case SMBC_PRINTER_SHARE :
2015-10-28 14:37:36 +03:00
if ( ! opt . quiet ) {
2016-01-24 13:17:20 +03:00
printf ( " Ignoring printer share %s \n " ,
dirent - > name ) ;
}
2004-01-07 03:43:52 +03:00
break ;
case SMBC_COMMS_SHARE :
2015-10-28 14:37:36 +03:00
if ( ! opt . quiet ) {
2016-01-24 13:17:20 +03:00
printf ( " Ignoring comms share %s \n " ,
dirent - > name ) ;
}
2004-01-07 03:43:52 +03:00
break ;
2010-07-04 13:02:21 +04:00
2004-01-07 03:43:52 +03:00
case SMBC_IPC_SHARE :
2015-10-28 14:37:36 +03:00
if ( ! opt . quiet ) {
2016-01-24 13:17:20 +03:00
printf ( " Ignoring ipc$ share %s \n " ,
dirent - > name ) ;
}
2004-01-07 03:43:52 +03:00
break ;
default :
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " Ignoring file '%s' of type '%d' \n " ,
newname , dirent - > smbc_type ) ;
2004-01-07 03:43:52 +03:00
break ;
}
2016-02-09 01:25:04 +03:00
if ( ! ok ) {
fprintf ( stderr , " Failed to download %s: %s \n " ,
newname , strerror ( errno ) ) ;
2016-06-21 16:56:23 +03:00
free ( tmpname ) ;
2016-02-09 01:25:04 +03:00
return false ;
}
2004-01-07 03:43:52 +03:00
free ( newname ) ;
}
free ( tmpname ) ;
smbc_closedir ( dirhandle ) ;
2016-02-09 01:24:36 +03:00
return ok ;
2004-01-07 03:43:52 +03:00
}
2006-12-19 23:16:52 +03:00
static char * print_time ( long t )
2004-01-07 03:43:52 +03:00
{
static char buffer [ 100 ] ;
int secs , mins , hours ;
2016-01-24 13:17:20 +03:00
if ( t < - 1 ) {
2004-01-07 03:43:52 +03:00
strncpy ( buffer , " Unknown " , sizeof ( buffer ) ) ;
return buffer ;
}
secs = ( int ) t % 60 ;
mins = ( int ) t / 60 % 60 ;
hours = ( int ) t / ( 60 * 60 ) ;
2016-01-24 13:17:20 +03:00
snprintf ( buffer , sizeof ( buffer ) - 1 , " %02d:%02d:%02d " , hours , mins ,
secs ) ;
2004-01-07 03:43:52 +03:00
return buffer ;
}
2016-01-24 13:17:20 +03:00
static void print_progress ( const char * name , time_t start , time_t now ,
off_t start_pos , off_t pos , off_t total )
2004-01-07 03:43:52 +03:00
{
double avg = 0.0 ;
2016-01-24 13:17:20 +03:00
long eta = - 1 ;
2004-01-07 03:43:52 +03:00
double prcnt = 0.0 ;
2018-06-18 11:43:53 +03:00
char hpos [ 22 ] , htotal [ 22 ] , havg [ 22 ] ;
2004-01-07 03:43:52 +03:00
char * status , * filename ;
int len ;
2016-01-24 13:17:20 +03:00
if ( now - start ) {
avg = 1.0 * ( pos - start_pos ) / ( now - start ) ;
}
2004-02-14 01:09:53 +03:00
eta = ( total - pos ) / avg ;
2016-01-24 13:17:20 +03:00
if ( total ) {
prcnt = 100.0 * pos / total ;
}
2004-01-07 03:43:52 +03:00
human_readable ( pos , hpos , sizeof ( hpos ) ) ;
human_readable ( total , htotal , sizeof ( htotal ) ) ;
human_readable ( avg , havg , sizeof ( havg ) ) ;
2016-01-24 13:17:20 +03:00
len = asprintf ( & status , " %s of %s (%.2f%%) at %s/s ETA: %s " , hpos ,
htotal , prcnt , havg , print_time ( eta ) ) ;
2009-01-01 05:06:57 +03:00
if ( len = = - 1 ) {
return ;
}
2010-07-04 13:02:21 +04:00
2016-01-24 13:17:20 +03:00
if ( columns ) {
int required = strlen ( name ) ,
available = columns - len - strlen ( " [] " ) ;
if ( required > available ) {
if ( asprintf ( & filename , " ...%s " ,
name + required - available + 3 ) = = - 1 ) {
2009-01-01 05:06:57 +03:00
return ;
}
} else {
filename = SMB_STRNDUP ( name , available ) ;
}
2016-01-24 13:17:20 +03:00
} else {
filename = SMB_STRDUP ( name ) ;
}
2004-01-07 03:43:52 +03:00
fprintf ( stderr , " \r [%s] %s " , filename , status ) ;
2016-01-24 13:17:20 +03:00
free ( filename ) ;
free ( status ) ;
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:57:01 +03:00
/* Return false on error, true on success. */
2010-01-28 21:38:24 +03:00
2016-01-24 13:57:01 +03:00
static bool smb_download_file ( const char * base , const char * name ,
bool recursive , bool resume , bool toplevel ,
char * outfile )
2010-07-04 13:32:50 +04:00
{
2004-01-07 03:43:52 +03:00
int remotehandle , localhandle ;
2010-09-09 00:29:00 +04:00
time_t start_time = time_mono ( NULL ) ;
2004-01-07 03:43:52 +03:00
const char * newpath ;
char path [ SMB_MAXPATHLEN ] ;
char checkbuf [ 2 ] [ RESUME_CHECK_SIZE ] ;
char * readbuf = NULL ;
2016-01-24 13:17:20 +03:00
off_t offset_download = 0 , offset_check = 0 , curpos = 0 ,
start_offset = 0 ;
2004-01-07 03:43:52 +03:00
struct stat localstat , remotestat ;
2016-01-24 13:17:20 +03:00
snprintf ( path , SMB_MAXPATHLEN - 1 , " %s%s%s " , base ,
( * base & & * name & & name [ 0 ] ! = ' / ' & &
base [ strlen ( base ) - 1 ] ! = ' / ' ) ? " / " : " " ,
name ) ;
2010-07-04 13:02:21 +04:00
2004-01-07 03:43:52 +03:00
remotehandle = smbc_open ( path , O_RDONLY , 0755 ) ;
2016-01-24 13:17:20 +03:00
if ( remotehandle < 0 ) {
switch ( errno ) {
case EISDIR :
if ( ! recursive ) {
fprintf ( stderr ,
" %s is a directory. Specify -R "
" to download recursively \n " ,
path ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
}
2010-01-28 21:38:24 +03:00
return smb_download_dir ( base , name , resume ) ;
2004-01-07 03:43:52 +03:00
case ENOENT :
2016-01-24 13:17:20 +03:00
fprintf ( stderr ,
" %s can't be found on the remote server \n " ,
path ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
case ENOMEM :
fprintf ( stderr , " Not enough memory \n " ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
case ENODEV :
2016-01-24 13:17:20 +03:00
fprintf ( stderr ,
" The share name used in %s does not exist \n " ,
path ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
case EACCES :
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " You don't have enough permissions "
" to access %s \n " ,
path ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
default :
perror ( " smbc_open " ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
if ( smbc_fstat ( remotehandle , & remotestat ) < 0 ) {
2004-01-07 03:43:52 +03:00
fprintf ( stderr , " Can't stat %s: %s \n " , path , strerror ( errno ) ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
if ( outfile ) {
newpath = outfile ;
} else if ( ! name [ 0 ] ) {
2004-01-07 03:43:52 +03:00
newpath = strrchr ( base , ' / ' ) ;
2016-01-24 13:17:20 +03:00
if ( newpath ) {
newpath + + ;
} else {
newpath = base ;
}
} else {
newpath = name ;
}
2004-01-07 03:43:52 +03:00
2010-07-04 13:32:50 +04:00
if ( ! toplevel & & ( newpath [ 0 ] = = ' / ' ) ) {
newpath + + ;
}
2010-07-04 13:02:21 +04:00
2008-03-05 17:20:29 +03:00
/* Open local file according to the mode */
2015-10-28 14:37:36 +03:00
if ( opt . update ) {
2008-03-05 17:20:29 +03:00
/* if it is up-to-date, skip */
2016-01-24 13:17:20 +03:00
if ( stat ( newpath , & localstat ) = = 0 & &
localstat . st_mtime > = remotestat . st_mtime ) {
2015-10-28 14:37:36 +03:00
if ( opt . verbose ) {
2008-03-05 17:20:29 +03:00
printf ( " %s is up-to-date, skipping \n " , newpath ) ;
2016-01-24 13:17:20 +03:00
}
2008-03-05 17:20:29 +03:00
smbc_close ( remotehandle ) ;
2016-01-24 13:57:01 +03:00
return true ;
2008-03-05 17:20:29 +03:00
}
2008-03-05 19:30:18 +03:00
/* else open it for writing and truncate if it exists */
2016-01-24 13:17:20 +03:00
localhandle = open (
newpath , O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC , 0775 ) ;
if ( localhandle < 0 ) {
2008-03-05 17:20:29 +03:00
fprintf ( stderr , " Can't open %s : %s \n " , newpath ,
2016-01-24 13:17:20 +03:00
strerror ( errno ) ) ;
2008-03-05 17:20:29 +03:00
smbc_close ( remotehandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2008-03-05 17:20:29 +03:00
}
/* no offset */
2015-10-28 14:37:36 +03:00
} else if ( ! opt . send_stdout ) {
2016-01-24 13:17:20 +03:00
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 ) ) ;
2004-03-05 20:17:31 +03:00
smbc_close ( remotehandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2010-07-04 13:02:21 +04:00
2008-03-15 01:26:28 +03:00
if ( fstat ( localhandle , & localstat ) ! = 0 ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " Can't fstat %s: %s \n " , newpath ,
strerror ( errno ) ) ;
2008-03-15 01:26:28 +03:00
smbc_close ( remotehandle ) ;
close ( localhandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2008-03-15 01:26:28 +03:00
}
2004-01-07 03:43:52 +03:00
2004-03-05 20:17:31 +03:00
start_offset = localstat . st_size ;
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
if ( localstat . st_size & &
localstat . st_size = = remotestat . st_size ) {
2015-10-28 14:37:36 +03:00
if ( opt . verbose ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " %s is already downloaded "
" completely. \n " ,
path ) ;
2015-10-28 14:37:36 +03:00
} else if ( ! opt . quiet ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " %s \n " , path ) ;
}
2004-03-05 20:17:31 +03:00
smbc_close ( remotehandle ) ;
close ( localhandle ) ;
2016-01-24 13:57:01 +03:00
return true ;
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
if ( localstat . st_size > RESUME_CHECK_OFFSET & &
remotestat . st_size > RESUME_CHECK_OFFSET ) {
offset_download =
localstat . st_size - RESUME_DOWNLOAD_OFFSET ;
2004-03-05 20:17:31 +03:00
offset_check = localstat . st_size - RESUME_CHECK_OFFSET ;
2015-10-28 14:37:36 +03:00
if ( opt . verbose ) {
2016-01-24 13:45:59 +03:00
printf ( " Trying to start resume of %s at %jd \n "
" At the moment %jd of %jd bytes have "
" been retrieved \n " ,
newpath , ( intmax_t ) offset_check ,
( intmax_t ) localstat . st_size ,
( intmax_t ) remotestat . st_size ) ;
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
if ( offset_check ) {
2004-03-05 20:17:31 +03:00
off_t off1 , off2 ;
2016-01-24 13:17:20 +03:00
/* First, check all bytes from offset_check to
* offset_download */
2004-03-05 20:17:31 +03:00
off1 = lseek ( localhandle , offset_check , SEEK_SET ) ;
2016-01-24 13:17:20 +03:00
if ( off1 < 0 ) {
2016-01-24 13:45:59 +03:00
fprintf ( stderr ,
" Can't seek to %jd in local file %s \n " ,
( intmax_t ) offset_check , newpath ) ;
2016-01-24 13:17:20 +03:00
smbc_close ( remotehandle ) ;
close ( localhandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
off2 = smbc_lseek ( remotehandle , offset_check , SEEK_SET ) ;
if ( off2 < 0 ) {
2016-01-24 13:45:59 +03:00
fprintf ( stderr ,
" Can't seek to %jd in remote file %s \n " ,
( intmax_t ) offset_check , newpath ) ;
2016-01-24 13:17:20 +03:00
smbc_close ( remotehandle ) ;
close ( localhandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
if ( off1 ! = off2 ) {
fprintf ( stderr , " Offset in local and remote "
2016-01-24 13:45:59 +03:00
" files are different "
" (local: %jd, remote: %jd) \n " ,
( intmax_t ) off1 , ( intmax_t ) off2 ) ;
2016-01-24 13:17:20 +03:00
smbc_close ( remotehandle ) ;
close ( localhandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
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 ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2016-01-24 13:17:20 +03:00
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 ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2016-01-24 13:17:20 +03:00
if ( memcmp ( checkbuf [ 0 ] , checkbuf [ 1 ] ,
RESUME_CHECK_SIZE ) = = 0 ) {
2015-10-28 14:37:36 +03:00
if ( opt . verbose ) {
2016-01-24 13:45:59 +03:00
printf ( " Current local and remote file "
" appear to be the same. "
" Starting download from "
" offset %jd \n " ,
( intmax_t ) offset_download ) ;
2016-01-24 13:17:20 +03:00
}
2004-03-05 20:17:31 +03:00
} else {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " Local and remote file appear "
" to be different, not "
" doing resume for %s \n " ,
path ) ;
smbc_close ( remotehandle ) ;
close ( localhandle ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-03-05 20:17:31 +03:00
}
2004-01-07 03:43:52 +03:00
}
2004-03-05 20:17:31 +03:00
} else {
localhandle = STDOUT_FILENO ;
start_offset = 0 ;
offset_download = 0 ;
offset_check = 0 ;
2004-01-07 03:43:52 +03:00
}
2015-10-28 14:37:36 +03:00
readbuf = ( char * ) SMB_MALLOC ( opt . blocksize ) ;
2010-09-10 22:56:26 +04:00
if ( ! readbuf ) {
2016-02-04 23:38:20 +03:00
fprintf ( stderr , " Failed to allocate %zu bytes for read "
" buffer (%s) " , opt . blocksize , strerror ( errno ) ) ;
2013-02-20 13:23:45 +04:00
if ( localhandle ! = STDOUT_FILENO ) {
close ( localhandle ) ;
}
2016-01-24 13:57:01 +03:00
return false ;
2010-09-10 22:56:26 +04:00
}
2004-01-07 03:43:52 +03:00
/* Now, download all bytes from offset_download to the end */
2016-01-24 13:17:20 +03:00
for ( curpos = offset_download ; curpos < remotestat . st_size ;
2015-10-28 14:37:36 +03:00
curpos + = opt . blocksize ) {
2016-02-09 01:27:09 +03:00
ssize_t bytesread ;
ssize_t byteswritten ;
bytesread = smbc_read ( remotehandle , readbuf , opt . blocksize ) ;
2004-01-07 03:43:52 +03:00
if ( bytesread < 0 ) {
2016-01-24 13:45:59 +03:00
fprintf ( stderr ,
2015-10-28 14:37:36 +03:00
" Can't read %zu bytes at offset %jd, file %s \n " ,
opt . blocksize , ( intmax_t ) curpos , path ) ;
2004-03-05 20:17:31 +03:00
smbc_close ( remotehandle ) ;
2016-01-24 13:17:20 +03:00
if ( localhandle ! = STDOUT_FILENO ) {
close ( localhandle ) ;
}
2004-01-07 03:43:52 +03:00
free ( readbuf ) ;
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
}
total_bytes + = bytesread ;
2016-02-09 01:27:09 +03:00
byteswritten = write ( localhandle , readbuf , bytesread ) ;
if ( byteswritten ! = bytesread ) {
2016-01-24 13:45:59 +03:00
fprintf ( stderr ,
" Can't write %zd bytes to local file %s at "
" offset %jd \n " , bytesread , path ,
( intmax_t ) curpos ) ;
2004-01-07 03:43:52 +03:00
free ( readbuf ) ;
2004-03-05 20:17:31 +03:00
smbc_close ( remotehandle ) ;
2016-01-24 13:17:20 +03:00
if ( localhandle ! = STDOUT_FILENO ) {
close ( localhandle ) ;
}
2016-01-24 13:57:01 +03:00
return false ;
2004-01-07 03:43:52 +03:00
}
2015-10-28 14:37:36 +03:00
if ( opt . dots ) {
2016-01-24 13:17:20 +03:00
fputc ( ' . ' , stderr ) ;
2015-10-28 14:37:36 +03:00
} else if ( ! opt . quiet ) {
2010-09-09 00:29:00 +04:00
print_progress ( newpath , start_time , time_mono ( NULL ) ,
2016-01-24 13:17:20 +03:00
start_offset , curpos ,
remotestat . st_size ) ;
2004-01-07 03:43:52 +03:00
}
}
free ( readbuf ) ;
2015-10-28 14:37:36 +03:00
if ( opt . dots ) {
2004-01-07 03:43:52 +03:00
fputc ( ' \n ' , stderr ) ;
printf ( " %s downloaded \n " , path ) ;
2015-10-28 14:37:36 +03:00
} else if ( ! opt . quiet ) {
2004-01-07 03:43:52 +03:00
int i ;
fprintf ( stderr , " \r %s " , path ) ;
2016-01-24 13:17:20 +03:00
if ( columns ) {
for ( i = strlen ( path ) ; i < columns ; i + + ) {
2004-01-07 03:43:52 +03:00
fputc ( ' ' , stderr ) ;
}
}
fputc ( ' \n ' , stderr ) ;
}
smbc_close ( remotehandle ) ;
2016-01-24 13:17:20 +03:00
if ( localhandle ! = STDOUT_FILENO ) {
close ( localhandle ) ;
}
2016-01-24 13:57:01 +03:00
return true ;
2004-01-07 03:43:52 +03:00
}
2006-12-19 23:16:52 +03:00
static void clean_exit ( void )
2004-01-07 03:43:52 +03:00
{
char bs [ 100 ] ;
human_readable ( total_bytes , bs , sizeof ( bs ) ) ;
2015-10-28 14:37:36 +03:00
if ( ! opt . quiet ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " Downloaded %s in %lu seconds \n " , bs ,
( unsigned long ) ( time_mono ( NULL ) - total_start_time ) ) ;
}
2004-01-07 03:43:52 +03:00
exit ( 0 ) ;
}
2006-12-19 23:16:52 +03:00
static void signal_quit ( int v )
2004-01-07 03:43:52 +03:00
{
clean_exit ( ) ;
}
2006-12-19 23:16:52 +03:00
static int readrcfile ( const char * name , const struct poptOption long_options [ ] )
2004-01-07 03:43:52 +03:00
{
FILE * fd = fopen ( name , " r " ) ;
int lineno = 0 , i ;
char var [ 101 ] , val [ 101 ] ;
2016-01-24 13:57:01 +03:00
bool found ;
2016-01-24 13:17:20 +03:00
int * intdata ;
char * * stringdata ;
if ( ! fd ) {
2004-01-07 03:43:52 +03:00
fprintf ( stderr , " Can't open RC file %s \n " , name ) ;
return 1 ;
}
2016-01-24 13:17:20 +03:00
while ( ! feof ( fd ) ) {
2004-01-07 03:43:52 +03:00
lineno + + ;
2016-01-24 13:17:20 +03:00
if ( fscanf ( fd , " %100s %100s \n " , var , val ) < 2 ) {
fprintf ( stderr ,
" Can't parse line %d of %s, ignoring. \n " ,
lineno , name ) ;
2004-01-07 03:43:52 +03:00
continue ;
}
2016-01-24 13:57:01 +03:00
found = false ;
2004-01-07 03:43:52 +03:00
2016-01-27 23:56:10 +03:00
for ( i = 0 ; long_options [ i ] . argInfo ; i + + ) {
2016-01-24 13:17:20 +03:00
if ( ! long_options [ i ] . longName ) {
continue ;
}
if ( strcmp ( long_options [ i ] . longName , var ) ) {
continue ;
}
if ( ! long_options [ i ] . arg ) {
continue ;
}
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
switch ( long_options [ i ] . argInfo ) {
2004-01-07 03:43:52 +03:00
case POPT_ARG_NONE :
intdata = ( int * ) long_options [ i ] . arg ;
2016-01-24 13:17:20 +03:00
if ( ! strcmp ( val , " on " ) ) {
* intdata = 1 ;
} else if ( ! strcmp ( val , " off " ) ) {
* intdata = 0 ;
} else {
fprintf ( stderr , " Illegal value %s for "
" %s at line %d in %s \n " ,
val , var , lineno , name ) ;
}
2004-01-07 03:43:52 +03:00
break ;
case POPT_ARG_INT :
intdata = ( int * ) long_options [ i ] . arg ;
* intdata = atoi ( val ) ;
break ;
case POPT_ARG_STRING :
stringdata = ( char * * ) long_options [ i ] . arg ;
2004-12-17 00:06:33 +03:00
* stringdata = SMB_STRDUP ( val ) ;
2016-01-28 00:00:31 +03:00
if ( long_options [ i ] . shortName = = ' U ' ) {
char * p ;
opt . username_specified = true ;
p = strchr ( * stringdata , ' % ' ) ;
if ( p ! = NULL ) {
* p = ' \0 ' ;
opt . password = p + 1 ;
opt . password_specified = true ;
}
}
2004-01-07 03:43:52 +03:00
break ;
default :
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " Invalid variable %s at "
" line %d in %s \n " ,
var , lineno , name ) ;
2004-01-07 03:43:52 +03:00
break ;
}
2016-01-24 13:57:01 +03:00
found = true ;
2004-01-07 03:43:52 +03:00
}
2016-01-24 13:17:20 +03:00
if ( ! found ) {
fprintf ( stderr ,
" Invalid variable %s at line %d in %s \n " , var ,
lineno , name ) ;
2004-01-07 03:43:52 +03:00
}
}
fclose ( fd ) ;
return 0 ;
}
2015-10-28 14:37:36 +03:00
int main ( int argc , char * * argv )
2004-01-07 03:43:52 +03:00
{
int c = 0 ;
const char * file = NULL ;
char * rcfile = NULL ;
2008-01-05 11:51:50 +03:00
bool smb_encrypt = false ;
2008-03-11 21:49:08 +03:00
int resume = 0 , recursive = 0 ;
2007-09-04 09:39:06 +04:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2016-01-24 13:57:01 +03:00
bool ret = true ;
2015-10-28 14:37:36 +03:00
char * p ;
const char * * argv_const = discard_const_p ( const char * , argv ) ;
2004-01-07 03:43:52 +03:00
struct poptOption long_options [ ] = {
POPT_AUTOHELP
2015-10-28 14:37:36 +03:00
{ " workgroup " , ' w ' , POPT_ARG_STRING , & opt . workgroup , ' w ' , " Workgroup to use (optional) " } ,
{ " user " , ' U ' , POPT_ARG_STRING , & opt . username , ' U ' , " Username to use " } ,
{ " guest " , ' a ' , POPT_ARG_NONE , NULL , ' a ' , " Work as user guest " } ,
2016-02-20 23:11:51 +03:00
{ " nonprompt " , ' n ' , POPT_ARG_NONE , NULL , ' n ' , " Don't ask anything (non-interactive) " } ,
2015-10-28 14:37:36 +03:00
{ " debuglevel " , ' d ' , POPT_ARG_INT , & opt . debuglevel , ' d ' , " Debuglevel to use " } ,
{ " encrypt " , ' e ' , POPT_ARG_NONE , NULL , ' e ' , " Encrypt SMB transport " } ,
2016-02-20 23:11:51 +03:00
{ " resume " , ' r ' , POPT_ARG_NONE , NULL , ' r ' , " Automatically resume aborted files " } ,
{ " update " , ' u ' , POPT_ARG_NONE , NULL , ' u ' , " Download only when remote file is newer than local file or local file is missing " } ,
{ " recursive " , ' R ' , POPT_ARG_NONE , NULL , ' R ' , " Recursively download files " } ,
2015-10-28 14:37:36 +03:00
{ " blocksize " , ' b ' , POPT_ARG_INT , & opt . blocksize , ' b ' , " Change number of bytes in a block " } ,
{ " outputfile " , ' o ' , POPT_ARG_STRING , & opt . outputfile , ' o ' , " Write downloaded data to specified file " } ,
2016-02-20 23:11:51 +03:00
{ " stdout " , ' O ' , POPT_ARG_NONE , NULL , ' O ' , " Write data to stdout " } ,
{ " dots " , ' D ' , POPT_ARG_NONE , NULL , ' D ' , " Show dots as progress indication " } ,
{ " quiet " , ' q ' , POPT_ARG_NONE , NULL , ' q ' , " Be quiet " } ,
{ " verbose " , ' v ' , POPT_ARG_NONE , NULL , ' v ' , " Be verbose " } ,
2015-10-28 14:37:36 +03:00
{ " rcfile " , ' f ' , POPT_ARG_STRING , NULL , ' f ' , " Use specified rc file " } ,
2004-01-07 03:43:52 +03:00
POPT_TABLEEND
} ;
poptContext pc ;
2015-03-21 22:00:06 +03:00
smb_init_locale ( ) ;
2005-12-29 01:48:54 +03:00
2004-01-07 03:43:52 +03:00
/* only read rcfile if it exists */
2009-01-01 05:06:57 +03:00
if ( asprintf ( & rcfile , " %s/.smbgetrc " , getenv ( " HOME " ) ) = = - 1 ) {
return 1 ;
}
2016-01-24 13:17:20 +03:00
if ( access ( rcfile , F_OK ) = = 0 ) {
2005-11-21 18:52:10 +03:00
readrcfile ( rcfile , long_options ) ;
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
free ( rcfile ) ;
# ifdef SIGWINCH
signal ( SIGWINCH , change_columns ) ;
# endif
signal ( SIGINT , signal_quit ) ;
signal ( SIGTERM , signal_quit ) ;
2015-10-28 14:37:36 +03:00
pc = poptGetContext ( argv [ 0 ] , argc , argv_const , long_options , 0 ) ;
2004-01-07 03:43:52 +03:00
2016-02-20 23:11:51 +03:00
while ( ( c = poptGetNextOpt ( pc ) ) > 0 ) {
2016-01-24 13:17:20 +03:00
switch ( c ) {
2004-01-07 03:43:52 +03:00
case ' f ' :
readrcfile ( poptGetOptArg ( pc ) , long_options ) ;
break ;
case ' a ' :
2015-10-28 14:37:36 +03:00
opt . username_specified = true ;
opt . username = talloc_strdup ( frame , " " ) ;
opt . password_specified = true ;
opt . password = talloc_strdup ( frame , " " ) ;
2004-01-07 03:43:52 +03:00
break ;
2008-01-05 11:51:50 +03:00
case ' e ' :
smb_encrypt = true ;
break ;
2015-10-28 14:37:36 +03:00
case ' U ' :
opt . username_specified = true ;
opt . username = talloc_strdup ( frame , opt . username ) ;
p = strchr ( opt . username , ' % ' ) ;
if ( p ! = NULL ) {
* p = ' \0 ' ;
opt . password = p + 1 ;
opt . password_specified = true ;
}
break ;
2016-02-20 23:11:51 +03:00
case ' n ' :
opt . nonprompt = true ;
break ;
case ' r ' :
resume = true ;
break ;
case ' u ' :
opt . update = true ;
break ;
case ' R ' :
recursive = true ;
break ;
case ' O ' :
opt . send_stdout = true ;
break ;
case ' D ' :
opt . dots = true ;
break ;
case ' q ' :
opt . quiet = true ;
break ;
case ' v ' :
opt . verbose = true ;
break ;
2004-01-07 03:43:52 +03:00
}
}
2016-02-20 23:11:51 +03:00
if ( c < - 1 ) {
fprintf ( stderr , " %s: %s \n " ,
poptBadOption ( pc , POPT_BADOPTION_NOALIAS ) ,
poptStrerror ( c ) ) ;
return 1 ;
}
2015-10-28 14:37:36 +03:00
if ( ( opt . send_stdout | | resume | | opt . outputfile ) & & opt . update ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " The -o, -R or -O and -U options can not be "
" used together. \n " ) ;
2008-03-05 17:20:29 +03:00
return 1 ;
}
2015-10-28 14:37:36 +03:00
if ( ( opt . send_stdout | | opt . outputfile ) & & recursive ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " The -o or -O and -R options can not be "
" used together. \n " ) ;
2004-03-05 20:17:31 +03:00
return 1 ;
}
2015-10-28 14:37:36 +03:00
if ( opt . outputfile & & opt . send_stdout ) {
2016-01-24 13:17:20 +03:00
fprintf ( stderr , " The -o and -O options can not be "
" used together. \n " ) ;
2004-01-07 03:43:52 +03:00
return 1 ;
}
2015-10-28 14:37:36 +03:00
popt_burn_cmdline_password ( argc , argv ) ;
2018-08-22 02:11:02 +03:00
cmdline_messaging_context ( get_dyn_CONFIGFILE ( ) ) ;
2015-10-28 14:37:36 +03:00
if ( smbc_init ( get_auth_data , opt . debuglevel ) < 0 ) {
2004-01-07 03:43:52 +03:00
fprintf ( stderr , " Unable to initialize libsmbclient \n " ) ;
return 1 ;
}
2008-01-05 11:51:50 +03:00
if ( smb_encrypt ) {
SMBCCTX * smb_ctx = smbc_set_context ( NULL ) ;
smbc_option_set ( smb_ctx ,
2016-01-24 13:17:20 +03:00
discard_const_p ( char , " smb_encrypt_level " ) ,
" require " ) ;
2008-01-05 11:51:50 +03:00
}
2010-07-04 13:02:21 +04:00
2004-01-07 03:43:52 +03:00
columns = get_num_cols ( ) ;
2010-09-09 00:29:00 +04:00
total_start_time = time_mono ( NULL ) ;
2004-01-07 03:43:52 +03:00
2016-01-24 13:17:20 +03:00
while ( ( file = poptGetArg ( pc ) ) ) {
if ( ! recursive ) {
2016-01-24 13:57:01 +03:00
ret = smb_download_file ( file , " " , recursive , resume ,
2015-10-28 14:37:36 +03:00
true , opt . outputfile ) ;
2016-01-24 13:17:20 +03:00
} else {
2010-01-28 21:38:24 +03:00
ret = smb_download_dir ( file , " " , resume ) ;
2016-01-24 13:17:20 +03:00
}
2004-01-07 03:43:52 +03:00
}
2007-09-04 09:39:06 +04:00
TALLOC_FREE ( frame ) ;
2016-01-24 13:57:01 +03:00
if ( ret ) {
2010-01-28 21:38:24 +03:00
clean_exit ( ) ;
}
2016-01-24 13:57:01 +03:00
return ret ? 0 : 1 ;
2004-01-07 03:43:52 +03:00
}