From: Urban Wallasch Date: Sat, 17 Apr 2021 09:49:44 +0000 (+0200) Subject: * Refactoring kaoupload.c 2nd pass WIP. X-Git-Url: https://git.packet-gain.de/?a=commitdiff_plain;h=dd31ca813b4bc571628c064ed4611e64f401735c;p=kaotools.git * Refactoring kaoupload.c 2nd pass WIP. --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d019ef --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +kaoupload diff --git a/Makefile b/Makefile index d731f4d..d412d9e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ .PHONY: all clean -CFLAGS+=`pkg-config --cflags libcurl` -LDFLAGS+=`pkg-config --libs libcurl` +CFLAGS += -W -Wall +CFLAGS += `pkg-config --cflags libcurl` +LDFLAGS += `pkg-config --libs libcurl` all: kaoupload diff --git a/kaoupload.c b/kaoupload.c index 7e77668..ab523a0 100644 --- a/kaoupload.c +++ b/kaoupload.c @@ -24,6 +24,15 @@ static struct kaoconf { int bwlup; } cfg; +struct file_info_t { + const char *name; + char *esc_name; + int fd; + off_t size; + off_t remn; + off_t coff; + curl_off_t speed; +}; static void ie( const char *home ) { FILE *fp; @@ -32,7 +41,7 @@ static void ie( const char *home ) { if ( NULL == home ) return; - sprintf( confpath, "%s/%s", home, CFG_BASE ); + snprintf( confpath, sizeof confpath, "%s/%s", home, CFG_BASE ); if ( NULL == ( fp = fopen( confpath, "r" ) ) ) return; while ( fgets( buf, sizeof buf - 1, fp ) ) { @@ -57,16 +66,30 @@ static void ie( const char *home ) { if ( '"' == *x || '\'' == *x ) *x = '\0'; // don't overwrite environment, so "PASS=1234 kaoupload ..." is possible - setenv( key, val, 0 ); + if ( 0 == strcmp( key, "USER" ) + || 0 == strcmp( key, "PASS" ) + || 0 == strcmp( key, "BWLUP" ) ) + setenv( key, val, 0 ); } fclose( fp ); } -static void die( const char *msg, int eno ) { +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 ); } @@ -79,8 +102,7 @@ struct MemoryStruct { size_t size; }; -static size_t WriteMemoryCallback( void *contents, size_t size, size_t nmemb, void *userp ) -{ +static size_t WriteMemoryCallback( void *contents, size_t size, size_t nmemb, void *userp ) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; void *newmem; @@ -91,142 +113,154 @@ static size_t WriteMemoryCallback( void *contents, size_t size, size_t nmemb, vo memcpy( &(mem->memory[mem->size]), contents, realsize ); mem->size += realsize; mem->memory[mem->size] = 0; - return realsize; } -struct file_info_t { - int32_t fd; - 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 ) - 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"); -// printf ("File: %s\nSize: %lu bytes\n", fi->name, fi->size); - return 0; -} - -static int loadNext( struct file_info_t *fi, uint64_t csize ) { +static int loadNext( CURL *curl, struct file_info_t *fi, off_t csize ) { int res = -1; - struct MemoryStruct chunk; + int response_code; + ssize_t br; + char url[2048]; + void *slice; + char *s; + CURLcode error; + struct MemoryStruct reply; - chunk.memory = NULL; /* will be grown as needed by the realloc above */ - chunk.size = 0; /* no data at this point */ + reply.memory = NULL; /* will be grown as needed */ + reply.size = 0; /* no data at this point */ if ( csize > fi->remn ) csize = fi->remn; - void *buf = malloc( csize ); - if ( NULL == buf ) + if ( NULL == ( slice = malloc( csize ) ) ) die( "malloc", errno ); lseek( fi->fd, fi->coff, SEEK_SET ); - read( fi->fd, buf, csize ); + br = read( fi->fd, slice, csize ); + if ( 0 > br ) { + err( "read", errno ); + goto err_out; + } + if ( csize > br ) + csize = br; - CURL *curl = curl_easy_init(); - if ( NULL == curl ) - die( "curl_easy_init failed", 0 ); + 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 ); + error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, csize ); + error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDS, slice ); + error |= curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void *)&reply ); + if ( CURLE_OK != error ) + die( "curl_easy_setopt failed", 0 ); - char url[1024]; + error = curl_easy_perform( curl ); + if ( CURLE_OK != error ) { + cerr( "curl_easy_perform", error ); + goto err_out; + } + + curl_easy_getinfo( curl, CURLINFO_SPEED_UPLOAD_T, &fi->speed ); + curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response_code ); + + puts(reply.memory); + + switch ( response_code ) { + case 100: + case 200: + case 201: + // simplest case, uploaded one chunk + if ( 0 == strncmp( reply.memory, "Have ", strlen("Have ") ) + || 0 == strncmp( reply.memory, "Upload complete", strlen("Upload complete") ) ) { + fi->coff += csize; + fi->remn -= csize; + res = 0; + } + else + err( "unexpected response: check credentials?", 0 ); + break; + case 428: + // remote already has data up to Skip-to-position + if ( NULL != ( s = strstr( reply.memory, "Skip-to-position: " ) ) ) { + fi->coff = strtoul( s + strlen( "Skip-to-position: " ), NULL, 10 ); + fi->remn = fi->size - fi->coff; + res = 0; + } + else + err( "response code 428 w/o 'Skip-to-position'", 0 ); + break; + default: + fprintf( stderr, "WARNING: unexpected response code: %d\n", response_code ); + break; + } + + err_out: + free( reply.memory ); + free( slice ); + return res; +} + +static int uploadFile( const char *fn ) { + int res; + int csize = 1024; char userpass[256]; - char *ucname; - if ( NULL == ( ucname = curl_easy_escape( curl, fi->name, 0 ) ) ) + struct file_info_t fi; + struct stat stbuf; + CURL *curl; + CURLcode error; + + if ( NULL == ( curl = curl_easy_init() ) ) + die( "curl_easy_init failed", 0 ); + + fi.name = fn; + if ( NULL == ( fi.esc_name = curl_easy_escape( curl, fn, 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 ); + fi.fd = open( fn, O_RDONLY ); + if ( fi.fd < 0 ) + err( fn, errno ); + if ( fstat(fi.fd, &stbuf ) < 0 ) { + err( fn, errno ); + return -1; + } + fi.remn = fi.size = stbuf.st_size; + fi.coff = 0; + printf( "File: %s\nSize: %lu bytes\n", fi.name, fi.size ); - 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 ); + 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_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 ); + 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 ); - 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 ); + + while ( fi.remn ) { + res = loadNext( curl, &fi, csize ); + if ( res < 0 ) break; - } - } - else { - fprintf( stderr, "WARNING: curl_easy_perform: %s\n", curl_share_strerror( error ) ); + if ( 1024 == csize ) + csize *= 256; //this obviously influences upload speed + printf( "\r%s: %d%% (%5.2f kB/s) ", fi.name, (int)(100.0 * fi.coff / fi.size), fi.speed / 1024.0 ); + fflush( stdout ); } + curl_free( fi.esc_name ); curl_easy_cleanup( curl ); - free( chunk.memory ); - free( buf ); + close( fi.fd ); + puts( "" ); + if ( res < 0 ) + err( "file upload aborted", 0 ); return res; } -void usage( char *pname ) { +static void usage( char *pname ) { fprintf( stderr, "Usage: %s \n", pname ); exit( EXIT_FAILURE ); } int main( int argc, char *argv[], char *const envp[] ) { - struct file_info_t fi = FILE_INFO_INITIALIZER; - int csize = 1024; - int res; + int res = 0; if ( argc < 2 ) usage( basename( argv[0] ) ); @@ -237,28 +271,19 @@ int main( int argc, char *argv[], char *const envp[] ) { cfg.url = getenv( "URL" ) ? getenv( "URL" ) : URL_BASE; cfg.bwlup = 0; - if ( 0 != fill_file_info( argv[1], &fi ) ) - goto err_file; +/* + TODO + * cmd line options + * bandwidth limit +*/ if ( CURLE_OK != curl_global_init( CURL_GLOBAL_SSL ) ) 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( "\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( "" ); + res = uploadFile( argv[1] ); + curl_global_cleanup(); - exit( EXIT_SUCCESS ); + exit( res ? EXIT_FAILURE : EXIT_SUCCESS ); -err_file: -err_curl: - exit( EXIT_FAILURE ); + (void)envp; }