Compare commits

...

10 Commits

Author SHA1 Message Date
synt-xerror
b9b474af40 New: GET /api/key 2026-02-23 08:31:26 -03:00
synt-xerror
0543a9e9ce idk what am i doing 2026-02-20 21:30:22 -03:00
synt-xerror
1ee3da6722 Removing this (oops) 2026-02-19 02:28:40 -03:00
synt-xerror
f3667d615e fix link 2026-02-19 02:25:36 -03:00
synt-xerror
55788ace47 Example files 2026-02-19 02:21:09 -03:00
synt-xerror
94f34439dc update readme 2026-02-19 02:20:52 -03:00
synt-xerror
795cd984cd removing src folder 2026-02-19 02:20:23 -03:00
synt-xerror
cde638aa71 Temporary files 2026-02-19 02:19:49 -03:00
synt-xerror
832bb88ecd Removing jansson, the library won't parse json anymore 2026-02-19 02:18:43 -03:00
synt-xerror
7cdac92582 Fix: removing old and useless files 2026-02-16 22:05:53 -03:00
11 changed files with 116 additions and 276 deletions

4
.gitignore vendored
View File

@@ -1 +1,3 @@
auto-commit.txt example
libneocities.a
neocities.o

44
Makefile Executable file → Normal file
View File

@@ -1,38 +1,10 @@
# ------------------------------ make:
# Neocities CLI Makefile (Portable) gcc -c neocities.c -o neocities.o
# ------------------------------ ar rcs libneocities.a neocities.o
# Default compiler
CC ?= gcc
# Compiler flags
CFLAGS ?= -Wall -O2 -Iinclude
# Libraries
LIBS ?= -lcurl -ljansson
# Source and target
SRC = \
src/main.c \
src/info.c
BIN = neocities
# Detect Windows (MSYS2 / MinGW)
ifeq ($(OS),Windows_NT)
RM = del /Q
BIN_EXT = .exe
else
RM = rm -f
BIN_EXT =
endif
# Build executable
all: $(BIN)$(BIN_EXT)
$(BIN)$(BIN_EXT): $(SRC)
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
# Clean build files
clean: clean:
$(RM) $(BIN)$(BIN_EXT) rm libneocities.a neocities.o
example:
rm -f example
gcc example.c -L. -lneocities -lcurl -o example

119
README.md
View File

@@ -1,12 +1,6 @@
# Neocities C API # Neocities API C library
Minimal, secure C wrapper for the Neocities API using API key authentication. Minimal, secure C wrapper for the Neocities API.
No login flow.
No credential storage.
Stateless by design.
Users must generate their own API key via the Neocities dashboard.
--- ---
@@ -16,14 +10,9 @@ Users must generate their own API key via the Neocities dashboard.
* File listing * File listing
* Upload files * Upload files
* Delete files * Delete files
* Get API key
* TLS verification enabled by default * TLS verification enabled by default
Authentication is performed exclusively via:
```
Authorization: Bearer <API_KEY>
```
--- ---
## Requirements ## Requirements
@@ -38,25 +27,12 @@ sudo apt install libcurl4-openssl-dev
--- ---
## Authentication
Generate your API key at:
Neocities > Settings > Manage Site Settings > API
(or https://neocities.org/settings/YOUR-USERNAME#api_key)
If you're coding a CLI or application, you must provide the key at runtime (env var, config file, etc).
---
## API Overview ## API Overview
Just an overview. For more detailed information, see: [[Function-Guide.md]]
### Info (public) ### Info (public)
```c ```c
int neocities_info(const char *sitename, neocities_info_t *out); int neocities_info(const char *sitename, char **out);
``` ```
Fetch metadata about a site. Fetch metadata about a site.
@@ -66,9 +42,7 @@ Fetch metadata about a site.
### List files ### List files
```c ```c
int neocities_list(const char *api_key, int neocities_list(const char *api_key, const char *path, char** out);
const char *path,
neocities_filelist_t *out);
``` ```
List files at a path. List files at a path.
@@ -78,11 +52,7 @@ List files at a path.
### Upload ### Upload
```c ```c
int neocities_upload(const char *api_key, int neocities_upload(const char *api_key, const char **local_files, const char **remote_names, size_t count, char **response);
const char **local_files,
const char **remote_names,
size_t count,
char **response);
``` ```
Uploads multiple files. Uploads multiple files.
@@ -95,10 +65,7 @@ Uploads multiple files.
### Delete ### Delete
```c ```c
int neocities_delete(const char *api_key, int neocities_delete(const char *api_key, const char **filenames, size_t count, char **response);
const char **filenames,
size_t count,
char **response);
``` ```
Deletes files from the site. Deletes files from the site.
@@ -107,83 +74,17 @@ Deletes files from the site.
### API Key ### API Key
For security reasons, you should get the API key via Neocities settings. This function is not avaiable.
---
## Memory Management
Always free parsed outputs:
```c ```c
void neocities_free_info(neocities_info_t *info); int neocities_apikey(const char *user, const char *pass, char **out);
void neocities_free_filelist(neocities_filelist_t *list);
``` ```
`response` returned by upload/delete must also be freed by caller. Get your API key.
--- ---
## Example Usage ## Example Usage
### List files See example.c
```c
neocities_filelist_t list;
if (neocities_list(api_key, "/", &list) == 0) {
for (size_t i = 0; i < list.count; i++)
printf("%s\n", list.paths[i]);
neocities_free_filelist(&list);
}
```
---
### Upload
```c
const char *local[] = {"index.html"};
const char *remote[] = {"index.html"};
char *resp;
neocities_upload(api_key, local, remote, 1, &resp);
free(resp);
```
---
## Security Notes
* TLS verification is enforced
* Redirects are disabled intentionally
* API key is never persisted internally
Recommended:
* Store key in env vars
* Avoid hardcoding
---
## Design Philosophy
* No login/password support
* API key only
* Explicit memory ownership
* Minimal abstraction
---
## Future Extensions (Optional)
Possible areas to explore:
* Structured responses for upload/delete
* JSON passthrough mode
* Streaming uploads
* Error codes standardization
--- ---

35
example.c Normal file
View File

@@ -0,0 +1,35 @@
#include <stdio.h>
#include "neocities.h"
#include <stdlib.h>
int main() {
static const char* username = "YOUR_USERNAME";
static const char* api_key = "YOUR_API_KEY";
char* out = NULL;
// GET /api/key
if (neocities_apikey(username, "YOUR_PASSWORD", &out) == 0) printf("%s", out);
free(out);
// GET /api/info
if (neocities_info(username, &out) == 0) printf("%s", out);
free(out);
// GET /api/list
if (neocities_list(api_key, "/", &out) == 0) printf("%s", out);
free(out);
static const char* files[] = { "test.txt" };
static const int qnt_files = sizeof(files) / sizeof(files[0]);
// GET /api/upload
if (neocities_upload(api_key, files, files, qnt_files, &out) == 0) printf("%s", out);
free(out);
// GET /api/delete
if (neocities_delete(api_key, files, qnt_files, &out) == 0) printf("%s", out);
free(out);
return 0;
}

BIN
neocities

Binary file not shown.

View File

@@ -1,12 +1,11 @@
#include "neocities.h" #include "neocities.h"
#include <curl/curl.h> #include <curl/curl.h>
#include <jansson.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
// ------------------------- // -------------------------
// HTTP responde buffer // HTTP response buffer
// ------------------------- // -------------------------
struct response { struct response {
char *data; char *data;
@@ -101,36 +100,12 @@ static int perform_request(
return 0; 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; i<info->tag_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; i<list->count; i++) free(list->paths[i]);
free(list->paths);
}
// ------------------------- // -------------------------
// info — GET /api/info // info — GET /api/info
// ------------------------- // -------------------------
int neocities_info( int neocities_info(
const char *sitename, neocities_info_t *out const char *sitename,
char** out
) )
{ {
char url[512]; char url[512];
@@ -143,49 +118,9 @@ int neocities_info(
int ret = perform_request(url, NULL, NULL, &resp, NULL); int ret = perform_request(url, NULL, NULL, &resp, NULL);
if (ret) return ret; if (ret) return ret;
json_error_t error; *out = resp.data;
json_t *root = json_loads(resp.data, 0, &error);
free(resp.data); return 0;
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");
if (!json_is_object(info)) {
json_decref(root);
return 4;
}
json_t *j_sitename = json_object_get(info,"sitename");
json_t *j_created = json_object_get(info,"created_at");
json_t *j_updated = json_object_get(info,"last_updated");
json_t *j_domain = json_object_get(info,"domain");
json_t *j_hits = json_object_get(info,"hits");
out->sitename = json_is_string(j_sitename) ? strdup(json_string_value(j_sitename)) : NULL;
out->created_at = json_is_string(j_created) ? strdup(json_string_value(j_created)) : NULL;
out->last_updated = json_is_string(j_updated) ? strdup(json_string_value(j_updated)) : NULL;
out->domain = json_is_string(j_domain) ? strdup(json_string_value(j_domain)) : NULL;
out->hits = json_is_integer(j_hits) ? json_integer_value(j_hits) : 0;
json_t *tags = json_object_get(info,"tags");
if (!json_is_array(tags)) {
out->tags = NULL;
out->tag_count = 0;
} else {
size_t tc = json_array_size(tags);
out->tags = malloc(sizeof(char*) * tc);
out->tag_count = tc;
for (size_t i=0; i<tc; i++) {
json_t *tag = json_array_get(tags,i);
out->tags[i] = json_is_string(tag) ? strdup(json_string_value(tag)) : NULL;
}
}
json_decref(root);
return 0;
} }
// ------------------------- // -------------------------
@@ -193,7 +128,8 @@ int neocities_info(
// ------------------------- // -------------------------
int neocities_list( int neocities_list(
const char *api_key, const char *api_key,
const char *path, neocities_filelist_t *out const char *path,
char **out
) )
{ {
char url[512]; char url[512];
@@ -204,23 +140,8 @@ int neocities_list(
int ret = perform_request(url, api_key, NULL, &resp, NULL); int ret = perform_request(url, api_key, NULL, &resp, NULL);
if (ret) return ret; if (ret) return ret;
json_error_t error; *out = resp.data;
json_t *root = json_loads(resp.data, 0, &error); return 0;
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; i<count; i++) {
json_t *f = json_array_get(arr, i);
out->paths[i] = strdup(json_string_value(json_object_get(f,"path")));
}
json_decref(root);
return 0;
} }
// ------------------------- // -------------------------
@@ -316,6 +237,37 @@ int neocities_upload(
} }
// ------------------------- // -------------------------
// get_api_key — GET /api/key // key — GET /api/key
// ------------------------- // -------------------------
// no. for security reasons of course. int neocities_apikey(const char *user, const char *pass, char **out) {
struct response resp = {0};
char userpass[256];
snprintf(userpass, sizeof(userpass), "%s:%s", user, pass);
CURL *curl = curl_easy_init();
if (!curl) return 1;
resp.data = malloc(1);
resp.len = 0;
resp.data[0] = '\0';
curl_easy_setopt(curl, CURLOPT_URL, "https://neocities.org/api/key");
curl_easy_setopt(curl, CURLOPT_USERPWD, userpass); // aqui é equivalente ao -u do curl
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
CURLcode res = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
curl_easy_cleanup(curl);
if (res != CURLE_OK || http_code >= 400) {
free(resp.data);
return 2;
}
*out = resp.data; // resposta da API
return 0;
}

15
neocities.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef NEOCITIES_H
#define NEOCITIES_H
#include <stddef.h>
// -------------------------
// API functions
// -------------------------
int neocities_info(const char *sitename, char **out);
int neocities_list(const char *api_key, const char *path, char **out);
int neocities_delete(const char *api_key, const char **filenames, size_t count, char **response);
int neocities_upload(const char *api_key, const char **local_files, const char **remote_names, size_t count, char **response);
int neocities_apikey(const char *user, const char *pass, char **out);
#endif // NEOCITIES_H

Binary file not shown.

View File

@@ -1,38 +0,0 @@
#ifndef NEOCITIES_H
#define NEOCITIES_H
#include <stddef.h>
// -------------------------
// Output structs
// -------------------------
typedef struct {
char *sitename;
int hits;
char *created_at;
char *last_updated;
char *domain;
char **tags;
size_t tag_count;
} neocities_info_t;
typedef struct {
char **paths;
size_t count;
} neocities_filelist_t;
// -------------------------
// API functions
// -------------------------
int neocities_info(const char *sitename, neocities_info_t *out);
int neocities_list(const char *api_key, const char *path, neocities_filelist_t *out);
int neocities_delete(const char *api_key, const char **filenames, size_t count, char **response);
int neocities_upload(const char *api_key, const char **local_files, const char **remote_names, size_t count, char **response);
// -------------------------
// Free functions
// -------------------------
void neocities_free_info(neocities_info_t *info);
void neocities_free_filelist(neocities_filelist_t *list);
#endif // NEOCITIES_H

Binary file not shown.

1
test.txt Normal file
View File

@@ -0,0 +1 @@
hellooo