--- /dev/null
+#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;
+}