diff --git a/Makefile b/Makefile index 4bced34..758b6ee 100755 --- a/Makefile +++ b/Makefile @@ -12,7 +12,10 @@ CFLAGS ?= -Wall -O2 -Iinclude LIBS ?= -lcurl -ljansson # Source and target -SRC = src/main.c +SRC = \ + src/main.c \ + src/info.c + BIN = neocities # Detect Windows (MSYS2 / MinGW) diff --git a/neocities b/neocities index 2a1b388..a252b48 100755 Binary files a/neocities and b/neocities differ diff --git a/src/info.c b/src/info.c index cd3efde..f850ea0 100644 --- a/src/info.c +++ b/src/info.c @@ -1,39 +1,63 @@ -#include "main.h" -#include -#include -#include -#include +#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"); + // 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); + 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'; + 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)); @@ -41,51 +65,92 @@ int fetch_neocities_info() { 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; - json_t *obj = json_loads(resp.data, 0, &error); - free(resp.data); + // 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) { - const char *err_type = json_string_value(json_object_get(obj, "error_type")); - const char *err_msg = json_string_value(json_object_get(obj, "message")); + // 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) { - json_t *info_obj = json_object_get(obj, "info"); - 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"))); + // 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( + "#%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; -} \ No newline at end of file +} diff --git a/src/info.h b/src/info.h index 4096c3f..7cd9304 100644 --- a/src/info.h +++ b/src/info.h @@ -1,6 +1,12 @@ #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 \ No newline at end of file +#endif +// ^ Fim do include guard diff --git a/src/login.sh b/src/login.sh old mode 100644 new mode 100755 index dfba924..7c2da36 --- a/src/login.sh +++ b/src/login.sh @@ -1,16 +1,27 @@ #!/bin/bash +set -e -BASHRC="$HOME/.bashrc" +CONFIG_DIR="$HOME/.config/neocities" +CONFIG_FILE="$CONFIG_DIR/credentials" -read -p "Username: " NEOCITIES_USER -read -p "Password: " NEOCITIES_PASS +mkdir -p "$CONFIG_DIR" +chmod 700 "$CONFIG_DIR" -# Remove linhas existentes se houver -sed -i '/^NEOCITIES_USER=/d' "$BASHRC" -sed -i '/^NEOCITIES_PASS=/d' "$BASHRC" +read -p "Username: " USER +read -s -p "Password: " PASS +echo -# Adiciona no final -echo "NEOCITIES_USER=\"$NEOCITIES_USER\"" >> "$BASHRC" -echo "NEOCITIES_PASS=\"$NEOCITIES_PASS\"" >> "$BASHRC" +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 -echo "Successfully logged in. Saved on $HOME/.bashrc" +cat > "$CONFIG_FILE" < -#include -#include -#include +#include // printf, fprintf +#include // malloc, realloc, free +#include // strcmp, memcpy +#include // libcurl (necessário para callbacks e tipos) -#include "main.h" -#include "info.h" +#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; - size_t len; + 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} + {"--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; } - for (size_t i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + // 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 index f8ad691..053d496 100644 --- a/src/main.h +++ b/src/main.h @@ -1,16 +1,31 @@ #ifndef MAIN_H #define MAIN_H +// Include guard: +// Impede múltiplas inclusões deste header no mesmo processo de compilação. -#include // para size_t +#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; - size_t len; + char *data; // Buffer dinâmico contendo os dados acumulados + size_t len; // Quantidade de bytes válidos em data }; -size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata); +// 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