neocities.h

a C library for interacting with Neocities' API
git clone https://github.com/tanguyandreani/neocities.h
Log | Files | Refs | README | LICENSE

commit 283a76b4384e4f5f4f7b5b61f3bb6691782a8ba4
parent d908605e01ef93bbdc5df384df76002d4da50748
Author: Tanguy Andreani <dev@tanguy.space>
Date:   Sat, 23 Feb 2019 03:05:16 +0100

Added: batch commit

Diffstat:
MMakefile | 6+++---
Minfo.c | 11+++++++++--
Mlist.c | 9++++++++-
Mneocities.h | 157++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Aprint_error_msg.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mupload.c | 13+++++++++++--
6 files changed, 191 insertions(+), 65 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,13 +4,13 @@ DEBUG=-g -DNEOCITIES_DEBUG all: info list upload -info: info.c neocities.h dtparser.c dtparser.h +info: info.c neocities.h dtparser.c dtparser.h print_error_msg.h apikey.h $(CC) -o $(<:.c=) dtparser.c $< $(FLAGS) $(DEBUG) -list: list.c neocities.h dtparser.c dtparser.h +list: list.c neocities.h dtparser.c dtparser.h print_error_msg.h apikey.h $(CC) -o $(<:.c=) dtparser.c $< $(FLAGS) $(DEBUG) -upload: upload.c neocities.h dtparser.c dtparser.h +upload: upload.c neocities.h dtparser.c dtparser.h print_error_msg.h apikey.h $(CC) -o $(<:.c=) dtparser.c $< $(FLAGS) $(DEBUG) clean: diff --git a/info.c b/info.c @@ -8,6 +8,7 @@ #include "dtparser.h" #include "neocities.h" +#include "print_error_msg.h" #define CURL_ERROR NEOCITIES_LLVL_ERR_CURL_GLOBAL_INIT #define OK NEOCITIES_LLVL_OK @@ -31,8 +32,14 @@ int main(int argc, char *argv[]) if ((err = neocities_api_ex(APIKEY, info, ((argc == 1) ? "" : argv[1]), - &res)) != OK) + &res)) != OK || res.result != 0) { + neocities_print_error_message(err); + if (res.type == NEOCITIES_ERROR_STRUCT) + fprintf(stderr, "error_type: %d\n", res.data.error.type); + else + neocities_destroy(&res); return err; + } if (res.data.info.sitename != NULL) printf("sitename %s\n", res.data.info.sitename); @@ -40,7 +47,7 @@ int main(int argc, char *argv[]) if (res.data.info.views != -1) printf("views %d\n", res.data.info.views); - if (res.data.info.views != -1) + if (res.data.info.hits != -1) printf("hits %d\n", res.data.info.hits); if (res.data.info.created_at != 0) { diff --git a/list.c b/list.c @@ -8,6 +8,7 @@ #include "dtparser.h" #include "neocities.h" +#include "print_error_msg.h" #define CURL_ERROR NEOCITIES_LLVL_ERR_CURL_GLOBAL_INIT #define OK NEOCITIES_LLVL_OK @@ -30,8 +31,14 @@ int main(int argc, char *argv[]) if ((err = neocities_api_ex(APIKEY, list, (argc == 2) ? argv[1] : "", - &res)) != OK) + &res)) != OK || res.result != 0) { + neocities_print_error_message(err); + if (res.type == NEOCITIES_ERROR_STRUCT) + fprintf(stderr, "error_type: %d\n", res.data.error.type); + else + neocities_destroy(&res); return err; + } for (i = 0; i < res.data.list.length; i++) { printf("%s", res.data.list.files[i].path); diff --git a/neocities.h b/neocities.h @@ -3,6 +3,11 @@ #include "apikey.h" +/* + * the following constants are not to be used elsewhere than in this + * file. + */ + #define BASEURL "https://neocities.org/api/" /* POST */ @@ -14,6 +19,7 @@ #define INFO BASEURL "info" #define KEY BASEURL "key" +/* to use with neocities_api[_ex] */ enum neocities_action { upload, delete, @@ -22,9 +28,18 @@ enum neocities_action { key }; +/* this stands for LOW LEVEL ERROR */ + enum neocities_low_level_error { NEOCITIES_LLVL_OK, + NEOCITIES_LLVL_ERR_CURL_GLOBAL_INIT, + + /* + * the following members are not to be used elsewhere than in this + * file; you can throw them in neocities_print_error_message(). + */ + NEOCITIES_LLVL_ERR_JSON_TOKENER_NEW, NEOCITIES_LLVL_ERR_CURL_EASY_INIT, NEOCITIES_LLVL_ERR_CURL_MIME_INIT, @@ -34,14 +49,13 @@ enum neocities_low_level_error { NEOCITIES_LLVL_ERR_CURL_MIME_DATA, NEOCITIES_LLVL_ERR_CURL_SLIST_APPEND, NEOCITIES_LLVL_ERR_CURL_EASY_SETOPT, + NEOCITIES_LLVL_ERR_CURL_EASY_PERFORM, NEOCITIES_LLVL_ERR_UNSUPPORTED_DELETE, NEOCITIES_LLVL_ERR_BAD_ACTION, - NEOCITIES_LLVL_ERR_NEOCITIES_INIT, - NEOCITIES_LLVL_ERR_CURL_EASY_PERFORM, + NEOCITIES_LLVL_ERR_CURL_BUF_INIT, NEOCITIES_LLVL_ERR_JSON_TOKENER_PARSE, NEOCITIES_LLVL_ERR_RECEIVED_UNSUPPORTED_JSON, NEOCITIES_LLVL_ERR_RECEIVED_INVALID_JSON, - NEOCITIES_LLVL_ERR_NO_SUCCESS, NEOCITIES_LLVL_ERR_MALLOC_FAIL, NEOCITIES_LLVL_ERR_EXPECTED_STRING, NEOCITIES_LLVL_ERR_EXPECTED_BOOL, @@ -52,18 +66,11 @@ enum neocities_low_level_error { NEOCITIES_LLVL_ERR_RECEIVED_SOMETHING_ELSE }; -/* Not prefixed for convenience */ -enum neocities_api_level_error { - SITE_NOT_FOUND, - UNSUPPORTED_ERROR -}; - -typedef struct curl_buffer_ { - char *buf; - - int size; - int pos; -} curl_buffer; +/* + * neocities_*_ are structs that holds all the useful data + * + * see neocities_res for a more general type + */ struct neocities_info_ { char *sitename; /* default: NULL */ @@ -89,10 +96,24 @@ struct neocities_file_ { time_t updated_at; }; +/* + * you might want to use those in your code + */ + +enum neocities_api_level_error { + SITE_NOT_FOUND, + UNSUPPORTED_ERROR +}; + struct neocities_error_ { - int type; + enum neocities_api_level_error type; }; +/* + * NEOCITIES_NO_TYPE_YET works for successful request that didn't have an + * info or list field such as the upload call + */ + enum neocities_res_data { NEOCITIES_NO_TYPE_YET, NEOCITIES_INFO_STRUCT, @@ -111,6 +132,17 @@ typedef struct neocities_res_ { } data; } neocities_res; +/* + * this is an internal structure that shouldn't be used elsewhere. + */ + +typedef struct curl_buffer_ { + char *buf; + + int size; + int pos; +} curl_buffer; + int curl_buffer_init(curl_buffer * curl_buf, size_t size) { curl_buf->buf = malloc(sizeof(char) * size); @@ -174,6 +206,10 @@ size_t write_data(void *buffer, size_t size, size_t nmemb, return i; } +/* + * perform an api call and parse the resulting json into a json_object + */ + enum neocities_low_level_error neocities_api(const char *apikey, enum neocities_action action, const char *params, @@ -229,10 +265,11 @@ enum neocities_low_level_error neocities_api(const char *apikey, if (curl_mime_name(field, "filename") != CURLE_OK) return NEOCITIES_LLVL_ERR_CURL_MIME_NAME; - if (curl_mime_data(field, params, CURL_ZERO_TERMINATED) != - CURLE_OK) + if (curl_mime_data(field, params, CURL_ZERO_TERMINATED) != CURLE_OK) return NEOCITIES_LLVL_ERR_CURL_MIME_DATA; + /* filename default to sendfile when invalid */ + field = NULL; headers = curl_slist_append(headers, "Expect:"); @@ -285,7 +322,7 @@ enum neocities_low_level_error neocities_api(const char *apikey, } if (curl_buffer_init(&curl_buf, 700) != 0) - return NEOCITIES_LLVL_ERR_NEOCITIES_INIT; + return NEOCITIES_LLVL_ERR_CURL_BUF_INIT; headers = curl_slist_append(headers, auth_bearer); @@ -295,8 +332,7 @@ enum neocities_low_level_error neocities_api(const char *apikey, if (curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers) != CURLE_OK) return NEOCITIES_LLVL_ERR_CURL_EASY_SETOPT; - if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data) != - CURLE_OK) + if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data) != CURLE_OK) return NEOCITIES_LLVL_ERR_CURL_EASY_SETOPT; if (curl_easy_setopt @@ -310,8 +346,7 @@ enum neocities_low_level_error neocities_api(const char *apikey, curl_mime_free(form); curl_easy_cleanup(curl); - *jobj = - json_tokener_parse_ex(tok, (char *) curl_buf.buf, curl_buf.pos); + *jobj = json_tokener_parse_ex(tok, (char *) curl_buf.buf, curl_buf.pos); /* https://groups.google.com/forum/#!topic/json-c/CMvkXKXqtWs */ if (tok->char_offset != curl_buf.pos) { @@ -330,11 +365,15 @@ enum neocities_low_level_error neocities_api(const char *apikey, return NEOCITIES_LLVL_ERR_JSON_TOKENER_PARSE; if (json_object_get_type(*jobj) != json_type_object) - return NEOCITIES_LLVL_ERR_RECEIVED_UNSUPPORTED_JSON; + return NEOCITIES_LLVL_ERR_EXPECTED_OBJECT; return NEOCITIES_LLVL_OK; } +/* + * those are functions used by neocities_destroy() + */ + void neocities_destroy_list(neocities_res * res) { int i = 0; @@ -373,6 +412,10 @@ void neocities_destroy_error(neocities_res * res) return; } +/* + * you should use this to free memory AND reset values to their defaults + */ + void neocities_destroy(neocities_res * res) { @@ -395,9 +438,18 @@ void neocities_destroy(neocities_res * res) return; } +/* + * i took the guess approach here, this function determines which type + * is the most likely and tries to fill res fields as much as possible + * + * fields are reset to their default values as soon as the type is + * recognized + * + * the function will exit in case their is a type error in the json + */ + enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, - neocities_res * - res) + neocities_res * res) { json_object_iter jobj_iter, jobj_iter_info, jobj_iter_list; @@ -414,9 +466,9 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, json_object_object_foreachC(jobj, jobj_iter) { - if (jobj_iter.key != NULL - && strcmp(jobj_iter.key, "error_type") == 0) { + if (jobj_iter.key != NULL && strcmp(jobj_iter.key, "error_type") == 0) { + res->result = -1; // in case wasn't sent in usual order res->type = NEOCITIES_ERROR_STRUCT; res->data.error.type = UNSUPPORTED_ERROR; @@ -429,6 +481,8 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, "site_not_found") == 0) res->data.error.type = SITE_NOT_FOUND; + break; + } else if (jobj_iter.key != NULL && strcmp(jobj_iter.key, "result") == 0) { @@ -504,8 +558,7 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, json_type_null) return NEOCITIES_LLVL_ERR_EXPECTED_STRING_OR_NULL; - tmp_string = - json_object_get_string(jobj_iter_info.val); + tmp_string = json_object_get_string(jobj_iter_info.val); if (tmp_string != NULL) { res->data.info.domain = strdup(tmp_string); @@ -520,8 +573,7 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, (jobj_iter_info.val) != json_type_string) return NEOCITIES_LLVL_ERR_EXPECTED_STRING; - tmp_string = json_object_get_string - (jobj_iter_info.val); + tmp_string = json_object_get_string(jobj_iter_info.val); rfc5322_date_parse(tmp_string, strlen(tmp_string), &res->data.info.created_at, true); @@ -532,8 +584,7 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, (jobj_iter_info.val) != json_type_string) return NEOCITIES_LLVL_ERR_EXPECTED_STRING; - tmp_string = json_object_get_string - (jobj_iter_info.val); + tmp_string = json_object_get_string(jobj_iter_info.val); rfc5322_date_parse(tmp_string, strlen(tmp_string), &res->data.info.last_updated, true); @@ -569,7 +620,7 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, } - //break; + break; } else if (strcmp(jobj_iter.key, "files") == 0 && res->type == NEOCITIES_NO_TYPE_YET) { @@ -582,12 +633,10 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, if (json_object_get_type(jobj_iter.val) != json_type_array) return NEOCITIES_LLVL_ERR_EXPECTED_ARRAY; - res->data.list.length = - json_object_array_length(jobj_iter.val); + res->data.list.length = json_object_array_length(jobj_iter.val); res->data.list.files = - malloc(sizeof(struct neocities_file_) * - res->data.list.length); + malloc(sizeof(struct neocities_file_) * res->data.list.length); if (res->data.list.files == NULL) return NEOCITIES_LLVL_ERR_MALLOC_FAIL; @@ -611,14 +660,12 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, return NEOCITIES_LLVL_ERR_EXPECTED_STRING; (res->data.list.files)[i].path = - strdup(json_object_get_string - (jobj_iter_list.val)); + strdup(json_object_get_string(jobj_iter_list.val)); if ((res->data.list.files)[i].path == NULL) return NEOCITIES_LLVL_ERR_MALLOC_FAIL; - } else if (strcmp(jobj_iter_list.key, "updated_at") == - 0) { + } else if (strcmp(jobj_iter_list.key, "updated_at") == 0) { if (json_object_get_type (jobj_iter_list.val) != json_type_string) @@ -631,8 +678,8 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, continue; rfc5322_date_parse(tmp_string, strlen(tmp_string), - &(res->data.list.files)[i]. - updated_at, true); + &(res->data.list. + files)[i].updated_at, true); } else if (strcmp(jobj_iter_list.key, "size") == 0) { @@ -667,13 +714,7 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, } - } else { - - if (res->result == 0) // UPLOAD - - return NEOCITIES_LLVL_OK; - - return NEOCITIES_LLVL_ERR_RECEIVED_UNSUPPORTED_JSON; + break; } } @@ -681,8 +722,9 @@ enum neocities_low_level_error neocities_json_to_struct(json_object * jobj, return NEOCITIES_LLVL_OK; } -enum neocities_res_data action_to_res_data_type(enum neocities_action - action) +/* simple function used for convenience */ + +enum neocities_res_data action_to_res_data_type(enum neocities_action action) { switch (action) { case upload: @@ -694,6 +736,8 @@ enum neocities_res_data action_to_res_data_type(enum neocities_action } } +/* the function you probably looked for */ + enum neocities_low_level_error neocities_api_ex(const char *apikey, enum neocities_action action, const char *params, @@ -704,13 +748,12 @@ enum neocities_low_level_error neocities_api_ex(const char *apikey, json_object *jobj = NULL; if ((err = - neocities_api(apikey, action, params, - &jobj)) != NEOCITIES_LLVL_OK) + neocities_api(apikey, action, params, &jobj)) != NEOCITIES_LLVL_OK) return err; err = neocities_json_to_struct(jobj, res); - if (res->type != action_to_res_data_type(action)) + if (res->result == 0 && res->type != action_to_res_data_type(action)) return NEOCITIES_LLVL_ERR_RECEIVED_SOMETHING_ELSE; json_object_put(jobj); diff --git a/print_error_msg.h b/print_error_msg.h @@ -0,0 +1,60 @@ +#define M(m) fprintf(stderr, "error %d: " m, err); break + +void neocities_print_error_message(enum neocities_low_level_error err) +{ + switch (err) { + + case NEOCITIES_LLVL_OK: + M("everything went okay internally, but it seems" + " neocities reported an error, res.data.error.type should be" + " printed after this"); + case NEOCITIES_LLVL_ERR_CURL_GLOBAL_INIT: + case NEOCITIES_LLVL_ERR_CURL_EASY_INIT: + case NEOCITIES_LLVL_ERR_CURL_MIME_INIT: + case NEOCITIES_LLVL_ERR_CURL_MIME_ADDPART: + case NEOCITIES_LLVL_ERR_CURL_MIME_NAME: + case NEOCITIES_LLVL_ERR_CURL_MIME_DATA: + case NEOCITIES_LLVL_ERR_CURL_SLIST_APPEND: + case NEOCITIES_LLVL_ERR_CURL_EASY_SETOPT: + case NEOCITIES_LLVL_ERR_CURL_EASY_PERFORM: + M("libcurl failed, program should probably terminate now"); + case NEOCITIES_LLVL_ERR_CURL_MIME_FILEDATA: + M("there is an issue with the file you want to upload"); + case NEOCITIES_LLVL_ERR_UNSUPPORTED_DELETE: + M("delete is currently unsupported"); + case NEOCITIES_LLVL_ERR_BAD_ACTION: + M("expected action to be a member of neocities_action"); + case NEOCITIES_LLVL_ERR_CURL_BUF_INIT: + M("malloc failed in curl_buffer_init()"); + case NEOCITIES_LLVL_ERR_JSON_TOKENER_PARSE: + M("json_tokener_parse_ex() failed in neocities_api()"); + case NEOCITIES_LLVL_ERR_RECEIVED_UNSUPPORTED_JSON: + M("neocities responded with valid data which i can't understand"); + case NEOCITIES_LLVL_ERR_RECEIVED_INVALID_JSON: + M("neocities responded with invalid json data"); + case NEOCITIES_LLVL_ERR_MALLOC_FAIL: + M("out of memory"); + case NEOCITIES_LLVL_ERR_EXPECTED_STRING: + M("expected the json object to be a string but got something else"); + case NEOCITIES_LLVL_ERR_EXPECTED_BOOL: + M("expected the json object to be a boolean but got something else"); + case NEOCITIES_LLVL_ERR_EXPECTED_INT: + M("expected the json object to be an int but got something else"); + case NEOCITIES_LLVL_ERR_EXPECTED_ARRAY: + M("expected the json object to be an array but got something else"); + case NEOCITIES_LLVL_ERR_EXPECTED_STRING_OR_NULL: + M("expected the json object to be a string or to be null but got" + " something else"); + case NEOCITIES_LLVL_ERR_EXPECTED_OBJECT: + M("expected the json object to be an object but got something else"); + case NEOCITIES_LLVL_ERR_RECEIVED_SOMETHING_ELSE: + M("the type guessed by neocities_json_to_struct() doesn't match" + " with the request that was made"); + } + + fputc('\n', stderr); + + return; +} + +#undef M diff --git a/upload.c b/upload.c @@ -8,6 +8,7 @@ #include "dtparser.h" #include "neocities.h" +#include "print_error_msg.h" #define CURL_ERROR NEOCITIES_LLVL_ERR_CURL_GLOBAL_INIT #define OK NEOCITIES_LLVL_OK @@ -25,6 +26,9 @@ int main(int argc, char *argv[]) ./some-utf8-character/file is valid ./dir/some-utf8-character is invalid + by libcurl design, it will default to + sendfile + after the last /, filename should be [a-zA-Z\.]+ */ @@ -35,10 +39,15 @@ int main(int argc, char *argv[]) if (curl_global_init(CURL_GLOBAL_SSL) != 0) return CURL_ERROR; - if ((err = neocities_api_ex(APIKEY, upload, argv[1], &res)) != OK) + if ((err = neocities_api_ex(APIKEY, upload, argv[1], &res)) != OK + || res.result != 0) { + neocities_print_error_message(err); + if (res.type == NEOCITIES_ERROR_STRUCT) + fprintf(stderr, "error_type: %d\n", res.data.error.type); return err; + } - // neocities_destroy(&res); + //neocities_destroy(&res); curl_global_cleanup();