* Cleaned up and fixed kaoupload.c, first pass.
authorUrban Wallasch <urban.wallasch@freenet.de>
Fri, 16 Apr 2021 20:05:21 +0000 (22:05 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Fri, 16 Apr 2021 20:05:21 +0000 (22:05 +0200)
kaoupload.c

index 13f7bbf00af09f4ade31c6d45a3729a16e981cb5..7e7766811904c59f5b5f286836ce5ef330700037 100644 (file)
@@ -1,17 +1,21 @@
-#include <time.h>
+#include <ctype.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
+#include <time.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
+#include <libgen.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <curl/curl.h>
 
 #define URL_BASE "https://kaotan.latice.de/uploadx.php"
+#define CFG_BASE ".config/kaoconf"
 
 static struct kaoconf {
     const char *user;
@@ -20,34 +24,50 @@ static struct kaoconf {
     int bwlup;
 } cfg;
 
+
 static void ie( const char *home ) {
-    FILE *f;
+    FILE *fp;
     char confpath[256];
     char buf[256];
 
-    if ( !home )
+    if ( NULL == home )
         return;
-    sprintf( confpath,"%s/.config/kaoconf", home );
-    //puts(confpath);
-    f = fopen( confpath, "r" );
-    if ( !f )
+    sprintf( confpath, "%s/%s", home, CFG_BASE );
+    if ( NULL == ( fp = fopen( confpath, "r" ) ) )
         return;
-    while ( fgets( buf, sizeof buf - 1, f ) ) {
-        char *key = buf;
-        char *val = strchr(buf, '=');
-        char *x;
-        if ( '#' == *buf || !val )
+    while ( fgets( buf, sizeof buf - 1, fp ) ) {
+        char *key, *val, *x;
+        key = buf;
+        while ( isspace( (unsigned char)*key ) )
+            ++key;
+        if ( '#' == *key )
+            continue;
+        if ( NULL == ( val = strchr( key, '=' ) ) )
             continue;
         *val++ = '\0';
-        x = strrchr( val, '\n' );
-        if (x)
-            *x = 0;
-        x = strrchr( val, '\r' );
-        if (x)
-            *x = 0; //custom config file on Linux with CRLF? it could happen, you never know!
-        setenv( key, val, 0 ); //don't overwrite environment, so "PASS=1234 kaoupload ..." is possible
+        while ( isspace( (unsigned char)*val ) )
+            ++val;
+        if ( '"' == *val || '\'' == *val )
+            ++val;
+        if ( '\0' == *val )
+            continue;
+        x = val + strlen( val ) - 1;
+        while ( isspace( (unsigned char)*x ) && x > val )
+            *x-- = '\0';
+        if ( '"' == *x || '\'' == *x )
+            *x = '\0';
+        // don't overwrite environment, so "PASS=1234 kaoupload ..." is possible
+        setenv( key, val, 0 );
     }
-    fclose( f );
+    fclose( fp );
+}
+
+static void die( const char *msg, int eno ) {
+    fprintf( stderr, "ERROR: %s", msg );
+    if ( eno )
+        fprintf( stderr, ": %s", strerror( eno ) );
+    fprintf( stderr, "\n" );
+    exit( EXIT_FAILURE );
 }
 
 
@@ -65,15 +85,10 @@ static size_t WriteMemoryCallback( void *contents, size_t size, size_t nmemb, vo
     struct MemoryStruct *mem = (struct MemoryStruct *)userp;
     void *newmem;
 
-    newmem = realloc( mem->memory, mem->size + realsize + 1 );
-    if ( newmem == NULL ) {
-    /* out of memory! */
-        printf("not enough memory (realloc returned NULL)\n");
-        return 0;
-    }
-
+    if ( NULL == ( newmem = realloc( mem->memory, mem->size + realsize + 1 ) ) )
+        die( "realloc", errno );
     mem->memory = newmem;
-    memcpy(&(mem->memory[mem->size]), contents, realsize);
+    memcpy( &(mem->memory[mem->size]), contents, realsize );
     mem->size += realsize;
     mem->memory[mem->size] = 0;
 
@@ -82,30 +97,26 @@ static size_t WriteMemoryCallback( void *contents, size_t size, size_t nmemb, vo
 
 struct file_info_t {
     int32_t fd;
-    char name[256];
+    char *name;
     size_t size;
     size_t remn;
     size_t coff;
     curl_off_t speed;
 };
 
+#define FILE_INFO_INITIALIZER ((struct file_info_t){-1, NULL, 0, 0, 0, 0})
+
 static int fill_file_info( const char *fn, struct file_info_t *fi ) {
     struct stat stbuf;
 
+    if ( NULL == ( fi->name = realloc( fi->name, strlen( fn ) + 1 ) ) )
+        die( "malloc", errno );
     fi->fd = open( fn, O_RDONLY );
-
-    if ( fi->fd < 0 ) {
-        perror( "open failed!" );
-        return -1;
-    }
-
-    if ( fstat(fi->fd, &stbuf ) < 0 ) {
-        perror( "fstat failed!" );
-        return -1;
-    }
-
-    strncpy( fi->name, fn, sizeof fi->name - 1 );
-
+    if ( fi->fd < 0 )
+        die( "open", errno );
+    if ( fstat(fi->fd, &stbuf ) < 0 )
+        die( "fstat", errno );
+    strcpy( fi->name, fn );
     fi->remn = fi->size = stbuf.st_size;
     fi->coff = 0;
 //    puts ("all ok");
@@ -113,110 +124,141 @@ static int fill_file_info( const char *fn, struct file_info_t *fi ) {
     return 0;
 }
 
-static const char *urlencode( const char *s ) {
-    return s;
-}
-
 static int loadNext( struct file_info_t *fi, uint64_t csize ) {
     int res = -1;
     struct MemoryStruct chunk;
 
     chunk.memory = NULL;  /* will be grown as needed by the realloc above */
-    chunk.size = 0;    /* no data at this point */
+    chunk.size = 0;       /* no data at this point */
+
+    if ( csize > fi->remn )
+        csize = fi->remn;
+    void *buf = malloc( csize );
+    if ( NULL == buf )
+        die( "malloc", errno );
+    lseek( fi->fd, fi->coff, SEEK_SET );
+    read( fi->fd, buf, csize );
 
     CURL *curl = curl_easy_init();
-    if ( NULL != curl ) {
-        char url[1024];
-        char userpass[256];
-
-        sprintf( url, "%s?fname=%s&fpos=%lu&fsize=%lu", cfg.url, urlencode(fi->name), fi->coff, fi->size );
-        sprintf( userpass, "%s:%s", cfg.user, cfg.pass );
-
-        if ( csize > fi->remn )
-            csize=fi->remn;
-        void *buf = malloc( csize );
-        lseek( fi->fd, fi->coff, SEEK_SET );
-        read( fi->fd, buf, csize );
-
-        CURLcode error = curl_easy_setopt( curl, CURLOPT_URL, url );
-        error |= curl_easy_setopt( curl, CURLOPT_POST, 1 );
-//        error |= curl_easy_setopt(curl, CURLOPT_HEADER, 1L );
-        error |= curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60 );
-        error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, csize );
-        error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDS, buf );
-        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_WRITEDATA, (void *)&chunk );
-        error |= curl_easy_setopt( curl, CURLOPT_CONNECTTIMEOUT, 1000L );
-        if ( CURLE_OK == error )
-            error = curl_easy_perform( curl );
-
-        curl_easy_cleanup( curl );
-        free( buf );
-
-        if ( CURLE_OK == error ) {
-           res = 0;
-           curl_easy_getinfo( curl, CURLINFO_SPEED_UPLOAD_T, &fi->speed );
-           long response_code;
-           curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response_code );
-//           puts(chunk.memory);
-//           res = strtoul(chunk.memory+9, NULL, 10);
-//           printf ("Res: %u\n", res);
-           if ( 200 == response_code || 100 == response_code ) { //simplest case, just uploaded one chunk
-               fi->coff += csize;
-               fi->remn -= csize;
-           }
-           else if ( 428 == response_code ) { //remote already has data up to Skip-to-position
-               char *s = strstr( chunk.memory, "Skip-to-position: " );
-               fi->coff = strtoul( s + strlen("Skip-to-position: "), NULL, 10 );
-//               printf("New coff: %lu\n", fi->coff);
-               fi->remn = fi->size - fi->coff;
-           }
-        }
+    if ( NULL == curl )
+        die( "curl_easy_init failed", 0 );
+
+    char url[1024];
+    char userpass[256];
+    char *ucname;
+    if ( NULL == ( ucname = curl_easy_escape( curl, fi->name, 0 ) ) )
+        die( "curl_easy_escape failed", 0 );
+    snprintf( url, sizeof url, "%s?fname=%s&fpos=%lu&fsize=%lu", cfg.url, ucname, fi->coff, fi->size );
+    curl_free( ucname );
+    snprintf( userpass, sizeof userpass, "%s:%s", cfg.user, cfg.pass );
+
+    CURLcode error;
+    error = curl_easy_setopt( curl, CURLOPT_URL, url );
+    error |= curl_easy_setopt( curl, CURLOPT_POST, 1 );
+//    error |= curl_easy_setopt(curl, CURLOPT_HEADER, 1L );
+    error |= curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60 );
+    error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, csize );
+    error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDS, buf );
+    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_WRITEDATA, (void *)&chunk );
+    error |= curl_easy_setopt( curl, CURLOPT_CONNECTTIMEOUT, 1000L );
+    if ( CURLE_OK != error )
+        die( "curl_easy_setopt failed", 0 );
+    error = curl_easy_perform( curl );
+    if ( CURLE_OK == error ) {
+        char *s;
+        curl_easy_getinfo( curl, CURLINFO_SPEED_UPLOAD_T, &fi->speed );
+        long response_code;
+        curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response_code );
+
+//        puts(chunk.memory);
+//        res = strtoul(chunk.memory+9, NULL, 10);
+//        printf ("Res: %u\n", res);
+
+        switch ( response_code ) {
+        case 100:
+        case 200:
+        case 201:
+            if ( 0 == strncmp( chunk.memory, "Have ", strlen("Have ") ) ) {
+                // simplest case, sucessfully uploaded one chunk
+                fi->coff += csize;
+                fi->remn -= csize;
+                res = 0;
+            }
+            else
+                die( "unexpected response content", 0 );
+            break;
+        case 428:
+            // remote already has data up to Skip-to-position
+            if ( NULL != ( s = strstr( chunk.memory, "Skip-to-position: " ) ) ) {
+                fi->coff = strtoul( s + strlen( "Skip-to-position: " ), NULL, 10 );
+//                printf("New coff: %lu\n", fi->coff);
+                fi->remn = fi->size - fi->coff;
+                res = 0;
+            }
+            else
+                fprintf( stderr, "WARNING: response code 428 w/o Skip-to-position!\n", response_code );
+            break;
+        default:
+            fprintf( stderr, "WARNING: unhandled response code: %l\n", response_code );
+            break;
+       }
     }
     else {
-        puts( "Not curly or not easy" );
+        fprintf( stderr, "WARNING: curl_easy_perform: %s\n", curl_share_strerror( error ) );
     }
+
+    curl_easy_cleanup( curl );
     free( chunk.memory );
+    free( buf );
     return res;
 }
 
+void usage( char *pname ) {
+    fprintf( stderr, "Usage:  %s <file>\n", pname );
+    exit( EXIT_FAILURE );
+}
+
 int main( int argc, char *argv[], char *const envp[] ) {
-    struct file_info_t fi;
+    struct file_info_t fi = FILE_INFO_INITIALIZER;
     int csize = 1024;
     int res;
+
     if ( argc < 2 )
-        goto err_usage;
+        usage( basename( argv[0] ) );
 
     ie( getenv( "HOME" ) );
     cfg.user = getenv( "USER" );
     cfg.pass = getenv( "PASS" );
-    cfg.bwlup = 0;
     cfg.url = getenv( "URL" ) ? getenv( "URL" ) : URL_BASE;
+    cfg.bwlup = 0;
 
     if ( 0 != fill_file_info( argv[1], &fi ) )
         goto err_file;
+
     if ( CURLE_OK != curl_global_init( CURL_GLOBAL_SSL ) )
-        goto err_curl;
+        die( "curl_global_init", 0 );
+
     while ( fi.remn ) {
         res = loadNext( &fi, csize );
         if ( res < 0 )
             break;
         if ( 1024 == csize )
             csize *= 256; //this obviously influences upload speed
-        printf( "%s: %d%% (%5.2f kB/s)\r", fi.name, (int)(100.0 * fi.coff / fi.size), fi.speed / 1024.0 );
+        printf( "\r%s: %d%% (%5.2f kB/s) ", fi.name, (int)(100.0 * fi.coff / fi.size), fi.speed / 1024.0 );
         fflush( stdout );
     }
     //if (res < 0) say something clever;
     close( fi.fd );
     puts( "" );
     curl_global_cleanup();
-    return 0;
-err_usage:
+    exit( EXIT_SUCCESS );
+
 err_file:
 err_curl:
-    return -1;
+    exit( EXIT_FAILURE );
 }