From 9ba4be8a9228bbdff1d91c31d3dba3ed3f7d63a1 Mon Sep 17 00:00:00 2001 From: synt-xerror <169557594+synt-xerror@users.noreply.github.com> Date: Mon, 16 Feb 2026 17:01:21 -0300 Subject: [PATCH] Library is done for test and improvements --- .gitignore | 0 LICENSE.txt => LICENSE | 0 neocities | Bin 16888 -> 16712 bytes src/info.c | 156 ---------------------------- src/info.h | 12 --- src/login.sh | 27 ----- src/main.c | 93 ----------------- src/main.h | 31 ------ src/neocities.c | 226 +++++++++++++++++++++++++++++++++++++++++ src/neocities.h | 39 +++++++ src/neogities.c | 0 11 files changed, 265 insertions(+), 319 deletions(-) mode change 100644 => 100755 .gitignore rename LICENSE.txt => LICENSE (100%) delete mode 100644 src/info.c delete mode 100644 src/info.h delete mode 100755 src/login.sh delete mode 100755 src/main.c delete mode 100644 src/main.h create mode 100644 src/neocities.c create mode 100644 src/neocities.h create mode 100644 src/neogities.c diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/neocities b/neocities index a252b481c3e8856a712a7b5c0429d9416042b805..5328ea7e9361ecb8c8bdd080b7f3c032c410520b 100755 GIT binary patch literal 16712 zcmeHOdvILUc|VdYS@^M%4W)k79VYR#6 zy$d#F5)oNUwnB|zC=>b$1k$AKq@^jt5QaJe8Jm_&ifM+>fnbM$WH%0AAb>-`+uwK2 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 diff --git a/src/info.c b/src/info.c deleted file mode 100644 index f850ea0..0000000 --- a/src/info.c +++ /dev/null @@ -1,156 +0,0 @@ -#include "main.h" // Header local do projeto (declarações próprias: struct response, write_callback, etc.) -#include // printf, fprintf -#include // getenv, malloc, free -#include // libcurl: HTTP/HTTPS client -#include // Jansson: parsing e manipulação de JSON -#include - -// Função responsável por buscar informações do site do usuário no Neocities -int fetch_neocities_info() { - - // Lê variáveis de ambiente com credenciais - // NEOCITIES_USER e NEOCITIES_PASS são usadas para autenticação HTTP Basic - const char *user = getenv("NEOCITIES_USER"); - const char *pass = getenv("NEOCITIES_PASS"); - // const char *enc = getenv("NEOCITIES_PASS_ENC"); // Não usado aqui (possível uso futuro) - - // Verificação básica: se usuário ou senha não existirem, aborta - if (!user || !pass) { - fprintf(stderr, "You're not logged!\n"); - return 1; - } - - // Buffer para "user:password", formato exigido por CURLOPT_USERPWD - char auth[256]; - snprintf(auth, sizeof(auth), "%s:%s", user, pass); - - // Inicializa um handle do libcurl - CURL *curl = curl_easy_init(); - if (!curl) { - fprintf(stderr, "Erro ao inicializar cURL\n"); - return 1; - } - - // Monta a URL da API do Neocities para obter informações do site - char infourl[256]; - snprintf( - infourl, - sizeof(infourl), - "https://neocities.org/api/info?sitename=%s", - user - ); - - // Define a URL da requisição - curl_easy_setopt(curl, CURLOPT_URL, infourl); - - // Define autenticação HTTP Basic (user:pass) - curl_easy_setopt(curl, CURLOPT_USERPWD, auth); - - // Estrutura usada para armazenar a resposta HTTP em memória - // resp.data será expandido pelo write_callback - struct response resp = { .data = malloc(1), .len = 0 }; - resp.data[0] = '\0'; // Garante string vazia inicial - - // Função chamada pelo libcurl sempre que dados são recebidos - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - - // Ponteiro passado para o write_callback (estado da resposta) - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp); - - // Executa a requisição HTTP de forma síncrona - CURLcode res = curl_easy_perform(curl); - if (res != CURLE_OK) { - fprintf(stderr, "Erro na requisição: %s\n", curl_easy_strerror(res)); - curl_easy_cleanup(curl); - free(resp.data); - return 1; - } - - // Libera o handle do curl (a resposta já está em resp.data) - curl_easy_cleanup(curl); - - // Estrutura para capturar erros de parsing JSON - json_error_t error; - - // Converte a string JSON recebida em um objeto JSON manipulável - json_t *obj = json_loads(resp.data, 0, &error); - free(resp.data); // Não é mais necessária após o parsing - - // Caso o JSON seja inválido - if (!obj) { - fprintf(stderr, "Erro ao parsear JSON: %s\n", error.text); - return 1; - } - - // Obtém o campo "result" do JSON raiz - json_t *result_obj = json_object_get(obj, "result"); - const char *result_str = json_string_value(result_obj); - - // Caso a API tenha retornado erro - if (strcmp(result_str, "error") == 0) { - - // Tipo de erro retornado pela API - const char *err_type = - json_string_value(json_object_get(obj, "error_type")); - - // Mensagem de erro legível - const char *err_msg = - json_string_value(json_object_get(obj, "message")); - - // Tratamento específico para erro de autenticação - if (strcmp(err_type, "invalid_auth") == 0) { - printf("Usuário ou senha incorretos!\n"); - } else { - printf("Erro! %s\n", err_msg); - } - - // Diminui o contador de referência do objeto JSON - json_decref(obj); - return 1; - } - - // Caso a requisição tenha sido bem-sucedida - if (strcmp(result_str, "success") == 0) { - - // Objeto "info" contém os dados do site - json_t *info_obj = json_object_get(obj, "info"); - - // Impressão direta dos campos retornados - printf("\nSitename: %s\n", - json_string_value(json_object_get(info_obj, "sitename"))); - - printf("Hits: %d\n", - (int)json_integer_value(json_object_get(info_obj, "hits"))); - - printf("Created at: %s\n", - json_string_value(json_object_get(info_obj, "created_at"))); - - printf("Last updated: %s\n", - json_string_value(json_object_get(info_obj, "last_updated"))); - - printf("Domain: %s\n", - json_string_value(json_object_get(info_obj, "domain"))); - - // Array de tags associadas ao site - json_t *tags = json_object_get(info_obj, "tags"); - - printf("Tags: "); - size_t index; - json_t *tag; - - // Itera sobre o array JSON "tags" - json_array_foreach(tags, index, tag) { - printf( - "#%s%s", - json_string_value(tag), - (index < json_array_size(tags) - 1) ? ", " : "." - ); - } - printf("\n\n"); - } - - // Libera o objeto JSON principal - json_decref(obj); - - return 0; -} diff --git a/src/info.h b/src/info.h deleted file mode 100644 index 7cd9304..0000000 --- a/src/info.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef INFO -#define INFO -// ^ Guardas de inclusão (include guards) -// Evitam que este header seja processado mais de uma vez -// durante a compilação, o que causaria redefinições. - -// Declaração da função que busca informações do Neocities. -// O `void` indica explicitamente que a função não recebe parâmetros. -int fetch_neocities_info(void); - -#endif -// ^ Fim do include guard diff --git a/src/login.sh b/src/login.sh deleted file mode 100755 index 7c2da36..0000000 --- a/src/login.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -set -e - -CONFIG_DIR="$HOME/.config/neocities" -CONFIG_FILE="$CONFIG_DIR/credentials" - -mkdir -p "$CONFIG_DIR" -chmod 700 "$CONFIG_DIR" - -read -p "Username: " USER -read -s -p "Password: " PASS -echo - -read -p "Would you like to see the credentials on your screen to confirm? [Y/n]: " CHOICE -case $CHOICE in - y|Y) echo -e "Username: $USER\nPassword: $PASS" ;; - *) echo "" ;; -esac - -cat > "$CONFIG_FILE" < // printf, fprintf -#include // malloc, realloc, free -#include // strcmp, memcpy -#include // libcurl (necessário para callbacks e tipos) - -#include "main.h" // Declarações globais do projeto (upload_func, etc.) -#include "info.h" // Declaração de fetch_neocities_info() - -// Estrutura usada para acumular a resposta HTTP em memória. -// É preenchida incrementalmente pelo write_callback. -struct response { - char *data; // Buffer dinâmico com os dados recebidos - size_t len; // Quantidade atual de bytes válidos em data -}; - -// Callback chamado pelo libcurl sempre que um novo bloco de dados chega. -// Não garante tamanho fixo nem chamada única. -size_t write_callback(void *data, size_t size, size_t nmemb, void *userdata) { - - // userdata aponta para a struct response passada via CURLOPT_WRITEDATA - struct response *resp = (struct response *)userdata; - - // Tamanho real do bloco recebido - size_t chunk_size = size * nmemb; - - // Realoca o buffer para caber os novos dados + '\0' - char *tmp = realloc(resp->data, resp->len + chunk_size + 1); - if (!tmp) { - // Se realloc falhar, aborta a escrita - // Retornar 0 sinaliza erro ao libcurl - fprintf(stderr, "Erro de memória\n"); - return 0; - } - - // Atualiza o ponteiro com o novo buffer - resp->data = tmp; - - // Copia o bloco recebido para o final do buffer atual - memcpy(resp->data + resp->len, data, chunk_size); - - // Atualiza o tamanho total acumulado - resp->len += chunk_size; - - // Garante que o buffer seja uma string C válida - resp->data[resp->len] = '\0'; - - // Retornar o número de bytes processados indica sucesso - return chunk_size; -} - -// Tipo de ponteiro para função que representa um comando CLI. -// Todas as funções de comando retornam int e não recebem argumentos. -typedef int (*cmd_func_t)(void); - -// Estrutura que associa: -// - nome do comando (string digitada no terminal) -// - função que implementa esse comando -typedef struct { - const char *name; - cmd_func_t func; -} command_entry; - -int main(int argc, char *argv[]) { - - // Tabela de comandos suportados pelo programa. - // Facilita adicionar novos comandos sem alterar a lógica principal. - command_entry commands[] = { - {"--info", fetch_neocities_info}, - // {"--upload", upload_func} - }; - - // Verifica se ao menos um argumento foi passado - if (argc < 2) { - printf("No command provided.\n"); - return 0; - } - - // Percorre a tabela de comandos procurando correspondência - for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { - - // Compara o argumento passado com o nome do comando - if (strcmp(argv[1], commands[i].name) == 0) { - - // Executa a função associada ao comando - // e retorna imediatamente o código dela - return commands[i].func(); - } - } - - // Se nenhum comando conhecido foi encontrado - printf("Unknown command.\n"); - return 0; -} diff --git a/src/main.h b/src/main.h deleted file mode 100644 index 053d496..0000000 --- a/src/main.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H -// Include guard: -// Impede múltiplas inclusões deste header no mesmo processo de compilação. - -#include // size_t -// Necessário para o tipo size_t, usado em tamanhos e contadores de memória. - -// Declarações externas de variáveis globais. -// O `extern` indica que a definição real existe em outro arquivo .c. -extern const char *user; -extern const char *pass; - -// Estrutura usada para armazenar dados recebidos dinamicamente, -// geralmente como resposta de uma requisição HTTP. -struct response { - char *data; // Buffer dinâmico contendo os dados acumulados - size_t len; // Quantidade de bytes válidos em data -}; - -// Protótipo da função de callback usada pelo libcurl. -// Essa função será chamada repetidamente conforme dados chegam. -size_t write_callback( - void *ptr, // Ponteiro para os dados recebidos - size_t size, // Tamanho de cada elemento - size_t nmemb, // Número de elementos - void *userdata // Ponteiro de contexto definido pelo usuário -); - -#endif -// Fim do include guard diff --git a/src/neocities.c b/src/neocities.c new file mode 100644 index 0000000..5d430a5 --- /dev/null +++ b/src/neocities.c @@ -0,0 +1,226 @@ +#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 *user, const char *pass, + const char *post_fields, struct response *resp) +{ + CURL *curl = curl_easy_init(); + if (!curl) return 1; + resp->data = malloc(1); + resp->len = 0; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp); + + if (user && pass) { + char auth[256]; + snprintf(auth, sizeof(auth), "%s:%s", user, pass); + curl_easy_setopt(curl, CURLOPT_USERPWD, auth); + } + + if (post_fields) { + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_fields); + } + + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (res != CURLE_OK) { + 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, NULL, &resp); + 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 *user, const char *pass, 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, user, pass, NULL, &resp); + 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 *user, const char *pass, const char **filenames, size_t count, char **response) { + char body[1024] = ""; + for (size_t i=0; i + +// ------------------------- +// 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 *user, const char *pass, const char *path, neocities_filelist_t *out); +int neocities_upload(const char *user, const char *pass, const char **local_files, const char **remote_names, size_t count, char **response); +int neocities_delete(const char *user, const char *pass, const char **filenames, size_t count, char **response); +int neocities_get_api_key(const char *user, const char *pass, char **api_key); + +// ------------------------- +// Free functions +// ------------------------- +void neocities_free_info(neocities_info_t *info); +void neocities_free_filelist(neocities_filelist_t *list); + +#endif // NEOCITIES_H \ No newline at end of file diff --git a/src/neogities.c b/src/neogities.c new file mode 100644 index 0000000..e69de29