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 @@
# ------------------------------
# Neocities CLI Makefile (Portable)
# ------------------------------
make:
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:
$(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.
No login flow.
No credential storage.
Stateless by design.
Users must generate their own API key via the Neocities dashboard.
Minimal, secure C wrapper for the Neocities API.
---
@@ -16,14 +10,9 @@ Users must generate their own API key via the Neocities dashboard.
* File listing
* Upload files
* Delete files
* Get API key
* TLS verification enabled by default
Authentication is performed exclusively via:
```
Authorization: Bearer <API_KEY>
```
---
## 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
Just an overview. For more detailed information, see: [[Function-Guide.md]]
### Info (public)
```c
int neocities_info(const char *sitename, neocities_info_t *out);
int neocities_info(const char *sitename, char **out);
```
Fetch metadata about a site.
@@ -66,9 +42,7 @@ Fetch metadata about a site.
### List files
```c
int neocities_list(const char *api_key,
const char *path,
neocities_filelist_t *out);
int neocities_list(const char *api_key, const char *path, char** out);
```
List files at a path.
@@ -78,11 +52,7 @@ List files at a path.
### Upload
```c
int neocities_upload(const char *api_key,
const char **local_files,
const char **remote_names,
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);
```
Uploads multiple files.
@@ -95,10 +65,7 @@ Uploads multiple files.
### Delete
```c
int neocities_delete(const char *api_key,
const char **filenames,
size_t count,
char **response);
int neocities_delete(const char *api_key, const char **filenames, size_t count, char **response);
```
Deletes files from the site.
@@ -107,83 +74,17 @@ Deletes files from the site.
### 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
void neocities_free_info(neocities_info_t *info);
void neocities_free_filelist(neocities_filelist_t *list);
int neocities_apikey(const char *user, const char *pass, char **out);
```
`response` returned by upload/delete must also be freed by caller.
Get your API key.
---
## Example Usage
### List files
```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
See example.c
---

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 <curl/curl.h>
#include <jansson.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// -------------------------
// HTTP responde buffer
// -------------------------
// HTTP response buffer
// -------------------------
struct response {
char *data;
@@ -101,36 +100,12 @@ static int perform_request(
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
// -------------------------
int neocities_info(
const char *sitename, neocities_info_t *out
const char *sitename,
char** out
)
{
char url[512];
@@ -143,49 +118,9 @@ int neocities_info(
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");
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;
*out = resp.data;
return 0;
}
// -------------------------
@@ -193,7 +128,8 @@ int neocities_info(
// -------------------------
int neocities_list(
const char *api_key,
const char *path, neocities_filelist_t *out
const char *path,
char **out
)
{
char url[512];
@@ -204,23 +140,8 @@ int neocities_list(
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; 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;
*out = resp.data;
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