123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /**
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
- * You should have received a copy of the
- * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
- * along with this program. If not, see http://www.sun.com/cddl/cddl.html
- *
- * 2019 - 2020 https://://www.bananas-playground.net/projekt/selfpaste
- */
- /**
- * !WARNING!
- * This is a very simple, with limited experience written, windows C program.
- * Use at own risk and feel free to improve.
- *
- * for requirements and how to build it, read the README
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <direct.h>
- #include <dirent.h>
- #include <errno.h>
- /* https://www.argtable.org */
- #include <argtable3.h>
- /* https://github.com/curl/curl-for-win curl+openssl */
- #include <curl/curl.h>
- /* https://github.com/DaveGamble/cJSON */
- #include <cJSON.h>
- /**
- * global arg_xxx structs
- * https://www.argtable.org/
- */
- struct arg_lit *verbose, *quiet, *help, *createConfigFile;
- struct arg_file *fileToPaste;
- struct arg_end *end;
- const char *program_version = "1.1";
- const char *program_bug_address = "https://://www.bananas-playground.net/projekt/selfpaste";
- struct cmdArguments {
- int quiet, verbose, create_config_file;
- char *file_to_paste;
- };
- /**
- * Simple random string generation
- */
- const char availableChars[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
- int intN(int n) { return rand() % n; }
- char *randomString(int len) {
- char *rstr = malloc((len + 1) * sizeof(char));
- int i;
- for (i = 0; i < len; i++) {
- rstr[i] = availableChars[intN(strlen(availableChars))];
- }
- rstr[len] = '\0';
- return rstr;
- }
- /**
- * struct to hold the config options loaded from config file
- * Extend if the options file changes.
- */
- struct configOptions {
- char *secret;
- char *endpoint;
- };
- /**
- * struct to hold the returned data from the http post call
- * done with curl
- * see: https://curl.haxx.se/libcurl/c/getinmemory.html
- */
- struct MemoryStruct {
- char *memory;
- size_t size;
- };
- /**
- * callback function from the curl call
- * see: https://curl.haxx.se/libcurl/c/getinmemory.html
- */
- static size_t
- WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
- struct MemoryStruct *mem = (struct MemoryStruct *)userp;
- size_t realsize = size * nmemb;
- char *ptr = realloc(mem->memory, mem->size + realsize + 1);
- if(ptr == NULL) {
- /* out of memory! */
- printf("not enough memory (realloc returned NULL)\n");
- return 0;
- }
- mem->memory = ptr;
- memcpy(&(mem->memory[mem->size]), contents, realsize);
- mem->size += realsize;
- mem->memory[mem->size] = 0;
- return realsize;
- }
- /**
- * make a post curl call to upload the given file
- * and receive the URL as a answer
- * see: https://curl.haxx.se/libcurl/c/getinmemory.html
- */
- int uploadCall(struct configOptions cfgo, struct cmdArguments arguments) {
- CURL *curl_handle;
- CURLcode res;
- struct MemoryStruct chunk;
- chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
- chunk.size = 0; /* no data at this point */
- res = curl_global_init(CURL_GLOBAL_ALL);
- if(res != CURLE_OK) {
- printf("ERROR: curl_global_init() failed: %s\n", curl_easy_strerror(res));
- return 1;
- }
- /* init the curl session */
- curl_handle = curl_easy_init();
- /* specify URL to get */
- curl_easy_setopt(curl_handle, CURLOPT_URL, cfgo.endpoint);
- /* send all data to this function */
- curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- /* we pass our 'chunk' struct to the callback function */
- curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
- /* some servers don't like requests that are made without a user-agent */
- /* field, so we provide one */
- curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "selfpaseCurlAgent/1.0");
- /* add the POST data */
- /* https://curl.haxx.se/libcurl/c/postit2.html */
- curl_mime *form = NULL;
- curl_mimepart *field = NULL;
- form = curl_mime_init(curl_handle);
- field = curl_mime_addpart(form);
- curl_mime_name(field, "pasty");
- curl_mime_filedata(field, arguments.file_to_paste);
- field = curl_mime_addpart(form);
- curl_mime_name(field, "dl");
- curl_mime_data(field, cfgo.secret, CURL_ZERO_TERMINATED);
- curl_easy_setopt(curl_handle, CURLOPT_MIMEPOST, form);
- /* execute it! */
- res = curl_easy_perform(curl_handle);
- /* check for errors */
- if(res != CURLE_OK || chunk.size < 1) {
- printf("ERROR: curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
- exit(1);
- }
- if (chunk.memory != NULL) {
- if(arguments.verbose) printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
- if(arguments.verbose) printf("CURL returned:\n%s\n", chunk.memory);
- /* https://spacesciencesoftware.wordpress.com/2013/09/10/a-good-way-to-read-json-with-c/ */
- cJSON *json_root = cJSON_Parse(chunk.memory);
- if (json_root != NULL) {
- cJSON *json_result_status = cJSON_GetObjectItem(json_root, "status");
- if (json_result_status != NULL) {
- printf("Status: %s\n", json_result_status->valuestring);
- } else {
- printf("ERROR: Invalid payload returned. Missing 'status'\n%s\n", chunk.memory);
- }
- cJSON *json_result_message = cJSON_GetObjectItem(json_root, "message");
- if (json_result_message != NULL) {
- printf("Message: %s\n", json_result_message->valuestring);
- } else {
- printf("ERROR: Invalid payload returned. Missing 'message'\n%s\n", chunk.memory);
- }
- }
- }
- /* cleanup curl stuff */
- curl_easy_cleanup(curl_handle);
- curl_mime_free(form);
- free(chunk.memory);
- curl_global_cleanup();
- return 0;
- }
- /**
- * the main part starts here
- */
- int main(int argc, char *argv[]) {
- srand(time(NULL));
- /**
- * command line argument default values
- */
- struct cmdArguments arguments;
- arguments.quiet = 0;
- arguments.verbose = 0;
- arguments.create_config_file = 0;
- arguments.file_to_paste = "-";
- /**
- * https://www.argtable.org/
- */
- void *argtable[] = {
- help = arg_litn(NULL, "help", 0, 1, "Display this help and exit"),
- quiet = arg_litn("q", "quiet", 0, 1, "Don't produce any output"),
- verbose = arg_litn("v", "verbose", 0, 1, "Verbose output"),
- createConfigFile = arg_litn("c", "create-config-file", 0, 1, "Create default config file"),
- fileToPaste = arg_filen(NULL, NULL, "<file>", 0, 1, "File to paste"),
- end = arg_end(20),
- };
- /* argtable parsing */
- int nerrors;
- nerrors = arg_parse(argc,argv,argtable);
- /* special case: '--help' takes precedence over error reporting */
- if (help->count > 0) {
- printf("Usage: selfpaste.exe");
- arg_print_syntax(stdout, argtable, "\n");
- arg_print_glossary(stdout, argtable, " %-25s %s\n");
- arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
- return(1);
- }
- /* If the parser returned any errors then display them and exit */
- if (nerrors > 0) {
- /* Display the error details contained in the arg_end struct.*/
- arg_print_errors(stdout, end, "selfpaste.exe");
- printf("Try '%s --help' for more information.\n", "selfpaste.exe");
- arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
- return(1);
- }
- else {
- arguments.quiet = quiet->count;
- arguments.verbose = verbose->count;
- arguments.create_config_file = createConfigFile->count;
- arguments.file_to_paste = fileToPaste->filename[0];
- }
- if(arguments.verbose) {
- printf ("File = %s\n"
- "Verbose = %s\n"
- "Quiet = %s\n"
- "Create config file = %s\n",
- arguments.file_to_paste,
- arguments.verbose ? "yes" : "no",
- arguments.quiet ? "yes" : "no",
- arguments.create_config_file ? "yes" : "no"
- );
- }
- /**
- * Config file check.
- * Also create if not is available and command line option
- * to create it is set.
- */
- char* homedir = getenv("USERPROFILE");
- if(homedir == NULL) {
- printf("ERROR: USERPROFILE directory not found?\n");
- return(1);
- }
- if(arguments.verbose) printf("Homedir: '%s'\n", homedir);
- char selfpasteSaveDir[12] = "\\selfpaste";
- char storagePath[strlen(homedir) + strlen(selfpasteSaveDir)];
- strcpy(storagePath, homedir);
- strcat(storagePath, selfpasteSaveDir);
- DIR* checkStoragePathDir = opendir(storagePath);
- if(checkStoragePathDir) {
- if(arguments.verbose) printf("Storage directory exists: '%s'\n", storagePath);
- closedir(checkStoragePathDir);
- } else if (ENOENT == errno) {
- if(_mkdir(storagePath) == 0) {
- if(arguments.verbose) printf("Storage directory created: '%s'\n", storagePath);
- }
- else {
- printf("ERROR: Storage directory '%s' could not created\n", storagePath);
- return(1);
- }
- }
- else {
- printf("ERROR: Storage directory '%s' could not validated.\n", storagePath);
- return(1);
- }
- char configFileName[16] = "\\selfpaste.cfg";
- char configFilePath[strlen(storagePath) + strlen(configFileName)];
- strcpy(configFilePath, storagePath);
- strcat(configFilePath, configFileName);
- if(arguments.verbose) printf("Configfilepath: '%s'\n", configFilePath);
- if(access(configFilePath, F_OK) != -1) {
- if(arguments.verbose) printf("Using configfile: '%s'\n", configFilePath);
- if(arguments.create_config_file == 1) {
- printf("INFO: Re creating configfile by manually deleting it.\n");
- return(1);
- }
- } else {
- printf("ERROR: Configfile '%s' not found.\n",configFilePath);
- if(arguments.create_config_file == 1) {
- printf("Creating configfile: '%s'\n", configFilePath);
- FILE *fp = fopen(configFilePath, "w");
- if (fp) {
- fputs("# selfpaste config file.\n", fp);
- fprintf(fp, "# See %s for more details.\n", program_bug_address);
- fprintf(fp, "# Version: %s\n", program_version);
- fprintf(fp, "SELFPASTE_UPLOAD_SECRET=%s\n", randomString(50));
- fputs("ENDPOINT=http://you-seflpaste-endpoi.nt\n", fp);
- fclose(fp);
- printf("Config file '%s' created.\nPlease update your settings!\n", configFilePath);
- return(0);
- }
- else {
- printf("ERROR: Configfile '%s' could not be written.\n",configFilePath);
- }
- }
- return(1);
- }
- /**
- * Reading and parsing the config file.
- * populate configOptions struct
- *
- * https://rosettacode.org/wiki/Read_a_configuration_file#C
- * https://github.com/welljsjs/Config-Parser-C
- * https://hyperrealm.github.io/libconfig/
- * https://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html
- */
- struct configOptions configOptions;
- FILE* fp;
- if ((fp = fopen(configFilePath, "r")) == NULL) {
- printf("ERROR: Configfile '%s' could not be opened.\n",configFilePath);
- exit(1);
- }
- if(arguments.verbose) printf("Reading configfile: '%s'\n", configFilePath);
- char line[128];
- char *optKey,*optValue, *workwith;
- while (fgets(line, sizeof line, fp) != NULL ) {
- if(arguments.verbose) printf("- Line: %s", line);
- if (line[0] == '#') continue;
- /* important. strok modifies the string it works with */
- workwith = strdup(line);
- optKey = strtok(workwith, "=");
- if(arguments.verbose) printf("Option: %s\n", optKey);
- optValue = strtok(NULL, "\n\r");
- if(arguments.verbose) printf("Value: %s\n", optValue);
- if(strcmp("ENDPOINT",optKey) == 0) {
- configOptions.endpoint = optValue;
- }
- if(strcmp("SELFPASTE_UPLOAD_SECRET",optKey) == 0) {
- configOptions.secret = optValue;
- }
- }
- fclose(fp);
- if(arguments.verbose) {
- printf("Using\n- Secret: %s\n- Endpoint: %s\n- File: %s\n",
- configOptions.secret,configOptions.endpoint,
- arguments.file_to_paste);
- }
- /* do the upload */
- uploadCall(configOptions, arguments);
- return(0);
- }
|