#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;
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];
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
*/
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 );
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
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 );
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 );
}
}
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;
}