diff --git a/landerctl/.landerrc b/landerctl/.landerrc index db525c7..964d3f2 100644 --- a/landerctl/.landerrc +++ b/landerctl/.landerrc @@ -1 +1,2 @@ api_key = test +server_url = http://localhost:18080 diff --git a/landerctl/include/landerctl.h b/landerctl/include/landerctl.h index 59a6301..7e01ed2 100644 --- a/landerctl/include/landerctl.h +++ b/landerctl/include/landerctl.h @@ -1,8 +1,13 @@ #ifndef LANDERCTL #define LANDERCTL +#include + +#include + typedef struct landerctl_cfg { const char *api_key; + const char *server_url; } landerctl_cfg; typedef enum landerctl_cfg_err { @@ -20,4 +25,18 @@ typedef enum landerctl_cfg_err { */ landerctl_cfg_err landerctl_cfg_parse(landerctl_cfg *out, const char *path); +typedef enum landerctl_mode { + landerctl_mode_none = 0, + landerctl_mode_short, + landerctl_mode_paste, + landerctl_mode_file, +} landerctl_mode; + +struct curl_slist *landerctl_set_common(const landerctl_cfg *cfg, CURL *curl, + landerctl_mode mode, bool secure, + const char *key); +void landerctl_post_short(CURL *curl, const char *url); +void landerctl_post_paste(CURL *curl, const char *path); +void landerctl_post_file(CURL *curl, const char *path); + #endif diff --git a/landerctl/src/cfg_parse.c b/landerctl/src/cfg_parse.c index 4a681dc..032a04b 100644 --- a/landerctl/src/cfg_parse.c +++ b/landerctl/src/cfg_parse.c @@ -14,12 +14,27 @@ landerctl_cfg_err landerctl_cfg_parse(landerctl_cfg *out, const char *path) { return landerctl_cfg_err_not_found; } + struct { + const char *key; + const char **var; + } key_to_vars[] = { + {"api_key", &out->api_key}, + {"server_url", &out->server_url}, + }; + size_t key_to_vars_len = sizeof(key_to_vars) / sizeof(key_to_vars[0]); + + // We NULL everything beforehand so we can check if we have all needed + // variables + for (size_t i = 0; i < key_to_vars_len; i++) { + *key_to_vars[i].var = NULL; + } + regex_t cfg_line_regex; regcomp(&cfg_line_regex, cfg_line_regex_expr, REG_EXTENDED); // Accept lines of at most 256 lines char line[256]; - landerctl_cfg_err res = landerctl_cfg_err_incomplete; + landerctl_cfg_err res = landerctl_cfg_err_ok; while (fgets(line, sizeof(line), f) != NULL) { // Last character might be a newline @@ -38,18 +53,29 @@ landerctl_cfg_err landerctl_cfg_parse(landerctl_cfg *out, const char *path) { } // api_key is currently the only value we parse - int key_len = reg_groups[1].rm_eo - reg_groups[1].rm_so; + size_t key_len = reg_groups[1].rm_eo - reg_groups[1].rm_so; - if ((strlen("api_key") == key_len) && - (strncmp("api_key", &line[reg_groups[1].rm_so], key_len) == 0)) { - int val_len = reg_groups[2].rm_eo - reg_groups[2].rm_so; - char *buf = malloc(val_len + 1); - strncpy(buf, &line[reg_groups[2].rm_so], val_len); + for (size_t i = 0; i < key_to_vars_len; i++) { + if ((key_len == strlen(key_to_vars[i].key)) && + (strncmp(&line[reg_groups[1].rm_so], key_to_vars[i].key, key_len) == + 0)) { + int val_len = reg_groups[2].rm_eo - reg_groups[2].rm_so; + char *buf = malloc(val_len + 1); + strncpy(buf, &line[reg_groups[2].rm_so], val_len); + buf[val_len] = '\0'; - out->api_key = buf; + *key_to_vars[i].var = buf; + break; + } + } + } - res = landerctl_cfg_err_ok; - break; + if (res == landerctl_cfg_err_ok) { + for (size_t i = 0; i < key_to_vars_len; i++) { + if (*key_to_vars[i].var == NULL) { + res = landerctl_cfg_err_incomplete; + break; + } } } diff --git a/landerctl/src/main.c b/landerctl/src/main.c index 3dd0dae..a88d0cc 100644 --- a/landerctl/src/main.c +++ b/landerctl/src/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,7 +9,8 @@ #include "landerctl.h" -const char default_cfg_path[] = ".landerrc"; +const char *default_cfg_path = ".landerrc"; +const char *usage = "%s [-SPFsv] arg [key]\n"; int main(int argc, char **argv) { landerctl_cfg cfg; @@ -33,6 +35,99 @@ int main(int argc, char **argv) { exit(1); } + opterr = 0; + + int c; + landerctl_mode mode = landerctl_mode_none; + bool secure = false; + bool verbose = false; + + while ((c = getopt(argc, argv, "SPFsv")) != -1) { + switch (c) { + case 'S': + mode = landerctl_mode_short; + break; + case 'P': + mode = landerctl_mode_paste; + break; + case 'F': + mode = landerctl_mode_file; + break; + case 's': + secure = true; + break; + case 'v': + verbose = true; + break; + case '?': + printf(usage, argv[0]); + exit(2); + } + } + + if (mode == landerctl_mode_none) { + printf("No mode specified.\n\n"); + printf(usage, argv[0]); + exit(2); + } + + if (optind == argc || (argc - optind > 2)) { + printf(usage, argv[0]); + exit(2); + } + + const char *arg = argv[optind]; + const char *key = argc - optind == 2 ? argv[optind + 1] : NULL; + + curl_global_init(CURL_GLOBAL_ALL); + CURL *curl = curl_easy_init(); + + if (curl == NULL) { + exit(255); + } + + struct curl_slist *list = landerctl_set_common(&cfg, curl, mode, secure, key); + + switch (mode) { + case landerctl_mode_short: + landerctl_post_short(curl, arg); + break; + } + + if (verbose) { + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + } + + int exit_code = 0; + + if (curl_easy_perform(curl) == CURLE_OK) { + long response_code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + + if (response_code < 200 || response_code > 299) { + fprintf(stderr, "HTTP status code %li\n", response_code); + exit_code = 3; + } else { + struct curl_header *location_header; + + if (curl_easy_header(curl, "Location", 0, CURLH_HEADER, -1, + &location_header) == CURLHE_OK) { + printf("%s%s\n", cfg.server_url, location_header->value); + } else { + fprintf(stderr, "Server returned a 2xx without a Location header.\n"); + exit_code = 5; + } + } + } else { + fprintf(stderr, "Libcurl encountered an error.\n"); + exit_code = 4; + } + + curl_easy_cleanup(curl); + curl_slist_free_all(list); + + return exit_code; + /* struct stat sb; */ /* stat(argv[1], &sb); */ @@ -46,8 +141,6 @@ int main(int argc, char **argv) { /* exit(1); */ /* } */ - /* curl_global_init(CURL_GLOBAL_ALL); */ - /* CURL *curl = curl_easy_init(); */ /* if (curl == NULL) { */ diff --git a/landerctl/src/post.c b/landerctl/src/post.c new file mode 100644 index 0000000..c0dd653 --- /dev/null +++ b/landerctl/src/post.c @@ -0,0 +1,60 @@ +#include +#include + +#include "landerctl.h" + +struct curl_slist *landerctl_set_common(const landerctl_cfg *cfg, CURL *curl, + landerctl_mode mode, bool secure, + const char *key) { + size_t url_len = strlen(cfg->server_url) + 4; + + if (key != NULL) { + url_len += strlen(key); + } + + char mode_char; + + switch (mode) { + case landerctl_mode_short: + mode_char = 's'; + break; + case landerctl_mode_paste: + mode_char = 'p'; + break; + case landerctl_mode_file: + mode_char = 'f'; + break; + // Shouldn't be able to happen + default: + return NULL; + } + + char url[url_len + 1]; + + if (key == NULL) { + sprintf(url, "%s/%c%s/", cfg->server_url, mode_char, secure ? "l" : ""); + } else { + sprintf(url, "%s/%c%s/%s", cfg->server_url, mode_char, secure ? "l" : "", + key); + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + + // Add API key header + char api_key_header[strlen(cfg->api_key) + 12]; + sprintf(api_key_header, "X-Api-Key: %s", cfg->api_key); + + struct curl_slist *list = NULL; + list = curl_slist_append(list, api_key_header); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + curl_easy_setopt(curl, CURLOPT_USERAGENT, "landerctl/" LANDER_VERSION ""); + + return list; +} + +void landerctl_post_short(CURL *curl, const char *url) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(url)); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url); +}