From: Volodymyr Poltavets Date: Fri, 16 Apr 2021 09:30:56 +0000 (+0200) Subject: * Added resuming kaoupload in C using libcurl X-Git-Url: https://git.packet-gain.de/?a=commitdiff_plain;h=1773c9e2c694c3d39fbfb38fb74ac8850a7d5054;p=kaotools.git * Added resuming kaoupload in C using libcurl --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e1173aa --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +.PHONY: all clean + +CFLAGS+=`pkg-config --cflags libcurl` +LDFLAGS+=`pkg-config --libs libcurl` + +all: kaoupload + +clean: + rm -rf kaoupload \ No newline at end of file diff --git a/kaoupload.c b/kaoupload.c new file mode 100644 index 0000000..da40da8 --- /dev/null +++ b/kaoupload.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define URL_BASE "https://kaotan.latice.de/uploadx.php" + +static struct kaoconf { +const char *user; +const char *pass; +const char *url; +int bwlup; +} cfg; + +static void ie(const char *home){ + FILE *f; + char confpath[256]; + char buf[256]; + + if (!home) return; + sprintf(confpath,"%s/.config/kaoconf", home); + //puts(confpath); + f = fopen(confpath, "r"); + if (!f) return; + while (fgets(buf, sizeof buf - 1, f)){ + char *key = buf; + char *val = strchr(buf, '='); + char *x; + if ('#' == *buf || !val) 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 + } + fclose(f); +} + + +/* WriteMemoryCallback and accompanying code were initially borrowed from + * https://curl.haxx.se/libcurl/c/getinmemory.html + */ +struct MemoryStruct { + char *memory; + size_t size; +}; + +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; + + newmem = realloc(mem->memory, mem->size + realsize + 1); + if(newmem == NULL) { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + mem->memory = newmem; + 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[256]; + size_t size; + size_t remn; + size_t coff; + curl_off_t speed; +}; + +static int fill_file_info(const char *fn, struct file_info_t *fi){ + struct stat stbuf; + + 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); + + 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 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 */ + + 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; + } + } + } + else + { + puts("Not curly or not easy"); + } + free(chunk.memory); + return res; +} + +int main(int argc, char *argv[], char *const envp[]){ + struct file_info_t fi; + int csize = 1024; + int res; + if (argc < 2) goto err_usage; + + + ie(getenv("HOME")); + cfg.user = getenv("USER"); + cfg.pass = getenv("PASS"); + cfg.bwlup = 0; + cfg.url = getenv("URL") ? getenv("URL"): URL_BASE; + + if (0 != fill_file_info(argv[1], &fi)) goto err_file; + if (CURLE_OK != curl_global_init(CURL_GLOBAL_SSL)) goto err_curl; + 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); + fflush(stdout); + } + //if (res < 0) say something clever; + close(fi.fd); + puts(""); + curl_global_cleanup(); + return 0; +err_usage: +err_file: +err_curl: + return -1; +}