/* * Description: This is intended as a helper script for fb only. * * Synopsis: ./fb-helper * * action can be: * d = download * u = upload to * * Author: Florian "Bluewind" Pritz * * Licensed under GPLv3 * (see COPYING for full license text) */ #include #include #include #include #include #include #include #include #define FORMAT_ARRAY_SIZE 5 /* struct which holds the persistent data for progress_callback */ struct progressData { struct timeval starttime; struct timeval last; double ullast; int lastStringLength; }; /* load the contents of file fn into data */ int load_file(const char *fn, char **data, size_t *data_size) { FILE *fp; size_t buf_size = 1024*1024; /* use 1MiB chunks */ fp = fopen(fn, "rb"); if (fp == NULL) { perror("load_file"); return 1; } /* read the file in buf_size chunks and appened the data to *data */ while (!feof(fp)) { *data = realloc(*data, *data_size + buf_size); if (*data == NULL) { perror("load_file"); return 1; } *data_size += fread(*data + *data_size, sizeof(char), buf_size, fp); } fclose(fp); return 0; } void format_bytes(double bytes, char *buf) { double size = bytes; int suffix_pos = 0; static const char *suffix[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}; static const int suffix_count = sizeof(suffix) / sizeof(suffix[0]); static const double boundary = 2048.0; for(suffix_pos = 0; suffix_pos + 1 < suffix_count; suffix_pos++) { if(size <= boundary && size >= -boundary) { break; } size /= 1024.0; } // don't print decimals for bytes if (suffix_pos != 0) snprintf(buf, 64, "%.2f%s", size, suffix[suffix_pos]); else snprintf(buf, 64, "%.0f%s", size, suffix[suffix_pos]); } int progress_callback( void *cb_data, double dltotal, double dlnow, double ultotal, double ulnow ){ struct timeval now; struct progressData *data = (struct progressData *)cb_data; double timeSpent = 0; int printed = 0; char speed[64]; char total[64]; if (0 == ulnow) return 0; /* upload complete; clean up */ if (ulnow >= ultotal) { fprintf(stderr, "%*s\r", data->lastStringLength + 1, ""); return 0; } gettimeofday(&now, NULL); /* calculate time between this and the last call in seconds */ timeSpent = (double)(now.tv_sec - data->last.tv_sec) + (double)(now.tv_usec - data->last.tv_usec) / 1000000.0; format_bytes((ulnow - data->ullast) / timeSpent, (char *)&speed); /* don't refresh too often, catch if time went backwards */ if (timeSpent < 0.2 && timeSpent > -1.0) return 0; format_bytes(ulnow, (char *)&total); /* print the progress */ printed = fprintf(stderr, "\r%s/s uploaded: %.1f%% = %s", speed, /* upload speed */ ulnow * 100.0 / ultotal, /* percent uploaded */ total /* total data uploaded */ ); /* pad the string if the last one was longer to remove left over characters */ if (data->lastStringLength > printed) fprintf(stderr, "%*s", data->lastStringLength - printed, ""); /* save current values for the next run */ data->ullast = ulnow; data->last = now; data->lastStringLength = printed; return 0; } int main(int argc, char *argv[]) { CURL *curl; CURLcode res; char *userAgent = "fb-client/"VERSION; struct progressData cb_data = { .starttime = {.tv_sec = 0, .tv_usec = 0}, .last = {.tv_sec = 0, .tv_usec = 0}, .ullast = 0.0, .lastStringLength = 0 }; struct stat statbuf; struct curl_httppost *formpost=NULL; struct curl_httppost *lastptr=NULL; struct curl_slist *headerlist=NULL; static const char buf[] = "Expect:"; struct curl_forms forms[4]; char *mode = NULL; char *data = NULL; char *url = NULL; char *file = NULL; size_t data_size = 0; int ret = 0; if(argc == 1) return 1; mode = argv[1]; if (strncmp(mode, "u", 1) == 0) file = argv[3]; url = argv[2]; if (curl_global_init(CURL_GLOBAL_ALL) != 0) { fprintf(stderr, "Error initializing curl"); return 10; } if (file) { if(stat(file, &statbuf) == -1) { fprintf(stderr, "fb-helper: %s: ", file); perror(NULL); return 1; } /* load files with 0 size (/proc files for example) into memory so we can * determine their real length */ if (statbuf.st_size == 0) { if (load_file(file, &data, &data_size) != 0) { return 1; } forms[0].option = CURLFORM_BUFFER; forms[0].value = basename(file); forms[1].option = CURLFORM_BUFFERPTR; forms[1].value = data; forms[2].option = CURLFORM_BUFFERLENGTH; forms[2].value = (char *)data_size; forms[3].option = CURLFORM_END; } else { forms[0].option = CURLFORM_FILE; forms[0].value = file; forms[1].option = CURLFORM_END; } /* Fill in the file upload field */ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "file", CURLFORM_ARRAY, forms, CURLFORM_END); } curl = curl_easy_init(); /* initialize custom header list (stating that Expect: 100-continue is not wanted */ headerlist = curl_slist_append(headerlist, buf); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); /* only display progress bar if uploading */ if (file) { curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &cb_data); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); } curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent); if (formpost) curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, (long)1); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, (long)30); gettimeofday(&cb_data.starttime, NULL); res = curl_easy_perform(curl); if (res != 0) { fprintf(stderr, "\n%s\n", curl_easy_strerror(res)); ret = 1; } /* cleanup */ curl_easy_cleanup(curl); if (formpost) curl_formfree(formpost); curl_slist_free_all (headerlist); curl_global_cleanup(); free(data); } else { fprintf(stderr, "Error initializing curl"); ret = 1; } return ret; }