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 a252b48..5328ea7 100755 Binary files a/neocities and b/neocities differ 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