From: Urban Wallasch Date: Sat, 17 Apr 2021 12:59:03 +0000 (+0200) Subject: * Improved kaoupload.c 3rd pass WIP. X-Git-Url: https://git.packet-gain.de/?a=commitdiff_plain;h=7d8f1c8fc24816b683b04661cee9fad23e96eb39;p=kaotools.git * Improved kaoupload.c 3rd pass WIP. --- diff --git a/kaoupload.c b/kaoupload.c index ab523a0..bda95ed 100644 --- a/kaoupload.c +++ b/kaoupload.c @@ -18,11 +18,11 @@ #define CFG_BASE ".config/kaoconf" static struct kaoconf { - const char *user; - const char *pass; - const char *url; - int bwlup; -} cfg; + char *user; + char *pass; + char *url; + curl_off_t bwlup; +} cfg = { NULL, NULL, NULL, (curl_off_t)0 }; struct file_info_t { const char *name; @@ -34,6 +34,50 @@ struct file_info_t { curl_off_t speed; }; +static void cerr( const char *msg, int eno ) { + fprintf( stderr, "ERROR: %s", msg ); + if ( eno ) + fprintf( stderr, ": %s", curl_share_strerror( eno ) ); + fprintf( stderr, "\n" ); +} + +static void err( const char *msg, int eno ) { + fprintf( stderr, "ERROR: %s", msg ); + if ( eno ) + fprintf( stderr, ": %s", strerror( eno ) ); + fprintf( stderr, "\n" ); +} + +static void die( const char *msg, int eno ) { + err( msg, eno ); + exit( EXIT_FAILURE ); +} + +static curl_off_t str_to_bwl( const char *s ) { + curl_off_t byps = 0; + char *end; + + byps = strtoul( s, &end, 10 ); + switch ( *end ) { + case 'k': + case 'K': + byps *= 1024; + break; + case 'm': + case 'M': + byps *= 1024 * 1024; + break; + case 'g': + case 'G': + byps *= 1024 * 1024 * 1024; + break; + default: + die( "say what?", 0 ); + break; + } + return byps; +} + static void ie( const char *home ) { FILE *fp; char confpath[256]; @@ -66,34 +110,24 @@ static void ie( const char *home ) { if ( '"' == *x || '\'' == *x ) *x = '\0'; // don't overwrite environment, so "PASS=1234 kaoupload ..." is possible - if ( 0 == strcmp( key, "USER" ) - || 0 == strcmp( key, "PASS" ) - || 0 == strcmp( key, "BWLUP" ) ) - setenv( key, val, 0 ); + if ( 0 == strcmp( key, "USER" ) ) { + free( cfg.user ); + cfg.user = strdup( val ); + } + else if ( 0 == strcmp( key, "PASS" ) ) { + free( cfg.pass ); + cfg.pass = strdup( val ); + } + else if ( 0 == strcmp( key, "URL" ) ) { + free( cfg.url ); + cfg.url = strdup( val ); + } + else if ( 0 == strcmp( key, "BWLUP" ) ) + cfg.bwlup = str_to_bwl( val ); } fclose( fp ); } -static void cerr( const char *msg, int eno ) { - fprintf( stderr, "ERROR: %s", msg ); - if ( eno ) - fprintf( stderr, ": %s", curl_share_strerror( eno ) ); - fprintf( stderr, "\n" ); -} - -static void err( const char *msg, int eno ) { - fprintf( stderr, "ERROR: %s", msg ); - if ( eno ) - fprintf( stderr, ": %s", strerror( eno ) ); - fprintf( stderr, "\n" ); -} - -static void die( const char *msg, int eno ) { - err( msg, eno ); - exit( EXIT_FAILURE ); -} - - /* WriteMemoryCallback and accompanying code were initially borrowed from * https://curl.haxx.se/libcurl/c/getinmemory.html */ @@ -134,19 +168,29 @@ static int loadNext( CURL *curl, struct file_info_t *fi, off_t csize ) { if ( NULL == ( slice = malloc( csize ) ) ) die( "malloc", errno ); lseek( fi->fd, fi->coff, SEEK_SET ); - br = read( fi->fd, slice, csize ); - if ( 0 > br ) { + if ( 0 > ( br = read( fi->fd, slice, csize ) ) ) { err( "read", errno ); goto err_out; } if ( csize > br ) csize = br; - snprintf( url, sizeof url, "%s?fname=%s&fpos=%lu&fsize=%lu", cfg.url, fi->esc_name, fi->coff, fi->size ); - error = curl_easy_setopt( curl, CURLOPT_URL, url ); + + curl_easy_reset( curl ); + error = curl_easy_setopt( curl, CURLOPT_POST, 1 ); error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, csize ); error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDS, slice ); + error |= curl_easy_setopt( curl, CURLOPT_URL, url ); + error |= curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ); + error |= curl_easy_setopt( curl, CURLOPT_USERNAME, cfg.user ); + error |= curl_easy_setopt( curl, CURLOPT_PASSWORD, cfg.pass ); + error |= curl_easy_setopt( curl, CURLOPT_MAX_SEND_SPEED_LARGE, cfg.bwlup ); + error |= curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback ); error |= curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void *)&reply ); + error |= curl_easy_setopt( curl, CURLOPT_TIMEOUT, 300L ); + error |= curl_easy_setopt( curl, CURLOPT_CONNECTTIMEOUT, 60L ); + error |= curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 0 ); + error |= curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 0 ); if ( CURLE_OK != error ) die( "curl_easy_setopt failed", 0 ); @@ -173,7 +217,7 @@ static int loadNext( CURL *curl, struct file_info_t *fi, off_t csize ) { res = 0; } else - err( "unexpected response: check credentials?", 0 ); + err( "unexpected response, check credentials?", 0 ); break; case 428: // remote already has data up to Skip-to-position @@ -199,11 +243,9 @@ static int loadNext( CURL *curl, struct file_info_t *fi, off_t csize ) { static int uploadFile( const char *fn ) { int res; int csize = 1024; - char userpass[256]; struct file_info_t fi; struct stat stbuf; CURL *curl; - CURLcode error; if ( NULL == ( curl = curl_easy_init() ) ) die( "curl_easy_init failed", 0 ); @@ -222,25 +264,12 @@ static int uploadFile( const char *fn ) { fi.coff = 0; printf( "File: %s\nSize: %lu bytes\n", fi.name, fi.size ); - snprintf( userpass, sizeof userpass, "%s:%s", cfg.user, cfg.pass ); - error = curl_easy_setopt( curl, CURLOPT_POST, 1 ); - error |= curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60 ); - error |= curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ); - error |= curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 0 ); - error |= curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 0 ); - error |= curl_easy_setopt( curl, CURLOPT_USERPWD, userpass ); - error |= curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback ); - error |= curl_easy_setopt( curl, CURLOPT_CONNECTTIMEOUT, 60L ); -// error |= curl_easy_setopt(curl, CURLOPT_HEADER, 1L ); - if ( CURLE_OK != error ) - die( "curl_easy_setopt failed", 0 ); - while ( fi.remn ) { res = loadNext( curl, &fi, csize ); if ( res < 0 ) break; if ( 1024 == csize ) - csize *= 256; //this obviously influences upload speed + csize = 5 * cfg.bwlup; printf( "\r%s: %d%% (%5.2f kB/s) ", fi.name, (int)(100.0 * fi.coff / fi.size), fi.speed / 1024.0 ); fflush( stdout ); } @@ -255,35 +284,81 @@ static int uploadFile( const char *fn ) { } static void usage( char *pname ) { - fprintf( stderr, "Usage: %s \n", pname ); + printf( "Usage: %s [OPTIONS] FILE ...\n", basename( pname ) ); + printf( "Options:\n" + " -h display this help\n" + " -l maxbps limit upload speed in byte per second, e.g. 100k\n" + " -u user set user name\n" + " -p passwd set password\n" + " -U URL set base URL\n" + ); + printf( "Defaults for user, password and URL are taken from environment and\n" + "configuration file, in that order\n" ); exit( EXIT_FAILURE ); } -int main( int argc, char *argv[], char *const envp[] ) { +int main( int argc, char *argv[] ) { int res = 0; + int c; if ( argc < 2 ) - usage( basename( argv[0] ) ); + usage( argv[0] ); + cfg.user = strdup( getenv( "USER" ) ? getenv( "USER" ) : "" ); + cfg.pass = strdup( getenv( "PASS" ) ? getenv( "PASS" ) : "" ); + cfg.url = strdup( getenv( "URL" ) ? getenv( "URL" ) : URL_BASE ); ie( getenv( "HOME" ) ); - cfg.user = getenv( "USER" ); - cfg.pass = getenv( "PASS" ); - cfg.url = getenv( "URL" ) ? getenv( "URL" ) : URL_BASE; - cfg.bwlup = 0; -/* - TODO - * cmd line options - * bandwidth limit -*/ + opterr = 0; + + while ( ( c = getopt( argc, argv, ":hl:p:u:U:" ) ) != -1 ) { + switch (c) { + case 'h': + usage( argv[0] ); + break; + case 'l': + cfg.bwlup = str_to_bwl( optarg ); + break; + case 'p': + free( cfg.pass ); + cfg.pass = strdup( optarg ); + break; + case 'u': + free( cfg.user ); + cfg.user = strdup( optarg ); + break; + case 'U': + free( cfg.url ); + cfg.url = strdup( optarg ); + break; + case ':': + fprintf( stderr, "ERROR: missing argument for option '-%c'\n", optopt ); + usage( argv[0] ); + break; + case '?': + fprintf( stderr, "ERROR: unknown option '-%c'\n", optopt ); + usage( argv[0] ); + break; + default: + die( "ERROR: Huh?!", 0 ); + break; + } + } + + if ( optind >= argc ) { + err( "no file to upload", 0 ); + usage( argv[0] ); + } if ( CURLE_OK != curl_global_init( CURL_GLOBAL_SSL ) ) die( "curl_global_init", 0 ); - res = uploadFile( argv[1] ); + while ( optind < argc ) + res = uploadFile( argv[optind++] ); curl_global_cleanup(); + free( cfg.user ); + free( cfg.pass ); + free( cfg.url ); exit( res ? EXIT_FAILURE : EXIT_SUCCESS ); - - (void)envp; }