-#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;
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 );
}
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;
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");
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 );
}