* Added resuming kaoupload in C using libcurl
authorVolodymyr Poltavets <Volodymyr.Poltavets@feig.de>
Fri, 16 Apr 2021 09:30:56 +0000 (11:30 +0200)
committerVolodymyr Poltavets <Volodymyr.Poltavets@feig.de>
Fri, 16 Apr 2021 09:30:56 +0000 (11:30 +0200)
Makefile [new file with mode: 0644]
kaoupload.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..da40da8
--- /dev/null
@@ -0,0 +1,215 @@
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <curl/curl.h>
+
+#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;
+}