#include "neocities.h" #include #include #include #include #include // ------------------------- // HTTP responde buffer // ------------------------- struct response { char *data; size_t len; }; static size_t write_cb( void *ptr, size_t size, size_t nmemb, void *userdata ) { struct response *r = (struct response*)userdata; size_t chunk = size * nmemb; char *tmp = realloc(r->data, r->len + chunk + 1); if (!tmp) return 0; r->data = tmp; memcpy(r->data + r->len, ptr, chunk); r->len += chunk; r->data[r->len] = '\0'; return chunk; } // ------------------------- // HTTP helpers // ------------------------- static int perform_request( const char *url, const char *api_key, const char *post_fields, struct response *resp, curl_mime *mime ) { CURL *curl = curl_easy_init(); if (!curl) return 1; resp->data = malloc(1); resp->len = 0; resp->data[0] = '\0'; struct curl_slist *headers = NULL; if (api_key && strlen(api_key) > 0) { char auth_header[512]; snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", api_key); headers = curl_slist_append(headers, auth_header); } curl_easy_setopt(curl, CURLOPT_URL, url); if (headers) curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* SSL hardening */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); // política curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp); if (post_fields) { curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_fields); } if (mime) curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); CURLcode res = curl_easy_perform(curl); curl_slist_free_all(headers); curl_easy_cleanup(curl); long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); if (res != CURLE_OK || http_code >= 400) { curl_slist_free_all(headers); curl_easy_cleanup(curl); free(resp->data); return 2; } return 0; } // ------------------------- // JSON parsers // ------------------------- void neocities_free_info( neocities_info_t *info ) { if (!info) return; free(info->sitename); free(info->created_at); free(info->last_updated); free(info->domain); for (size_t i=0; itag_count; i++) free(info->tags[i]); free(info->tags); } void neocities_free_filelist( neocities_filelist_t *list ) { if (!list) return; for (size_t i=0; icount; i++) free(list->paths[i]); free(list->paths); } // ------------------------- // info — GET /api/info // ------------------------- int neocities_info( const char *sitename, neocities_info_t *out ) { char url[512]; if (sitename) snprintf(url, sizeof(url), "https://neocities.org/api/info?sitename=%s", sitename); else snprintf(url, sizeof(url), "https://neocities.org/api/info"); struct response resp; int ret = perform_request(url, NULL, NULL, &resp, NULL); if (ret) return ret; json_error_t error; json_t *root = json_loads(resp.data, 0, &error); free(resp.data); if (!root) return 3; const char *result = json_string_value(json_object_get(root,"result")); if (!result || strcmp(result,"success")!=0) { json_decref(root); return 4; } json_t *info = json_object_get(root,"info"); out->sitename = strdup(json_string_value(json_object_get(info,"sitename"))); out->hits = (int)json_integer_value(json_object_get(info,"hits")); out->created_at = strdup(json_string_value(json_object_get(info,"created_at"))); out->last_updated= strdup(json_string_value(json_object_get(info,"last_updated"))); out->domain = strdup(json_string_value(json_object_get(info,"domain"))); json_t *tags = json_object_get(info,"tags"); size_t tc = json_array_size(tags); out->tags = malloc(sizeof(char*) * tc); out->tag_count = tc; for (size_t i=0; itags[i] = strdup(json_string_value(json_array_get(tags,i))); json_decref(root); return 0; } // ------------------------- // list — GET /api/list // ------------------------- int neocities_list( const char *api_key, const char *path, neocities_filelist_t *out ) { char url[512]; if (path) snprintf(url, sizeof(url), "https://neocities.org/api/list?path=%s", path); else snprintf(url, sizeof(url), "https://neocities.org/api/list"); struct response resp; int ret = perform_request(url, api_key, NULL, &resp, NULL); if (ret) return ret; json_error_t error; json_t *root = json_loads(resp.data, 0, &error); free(resp.data); if (!root) return 3; json_t *arr = json_object_get(root, "files"); if (!arr) { json_decref(root); return 4; } size_t count = json_array_size(arr); out->paths = malloc(sizeof(char*) * count); out->count = count; for (size_t i=0; ipaths[i] = strdup(json_string_value(json_object_get(f,"path"))); } json_decref(root); return 0; } // ------------------------- // delete — POST /api/delete // ------------------------- int neocities_delete( const char *api_key, const char **filenames, size_t count, char **response ) { char body[1024] = ""; for (size_t i=0; i