* Improved kaoupload.c 3rd pass WIP.
authorUrban Wallasch <urban.wallasch@freenet.de>
Sat, 17 Apr 2021 12:59:03 +0000 (14:59 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Sat, 17 Apr 2021 12:59:03 +0000 (14:59 +0200)
kaoupload.c

index ab523a02d7c86855f0d68a9710d0ddb829173842..bda95ed4cfad5659f1869e2ba2ba32727fb07541 100644 (file)
 #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 <file>\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;
 }