#include "exploit_cli.h" // Main function int main(int argc, char *argv[]) { if (argc < 2) { printf("\033[36m╔══════════════════════════════════════════════════════════════╗\033[0m\n"); printf("\033[36m║ 🚀 Exploit.or.id CLI ║\033[0m\n"); printf("\033[36m╠══════════════════════════════════════════════════════════════╣\033[0m\n"); printf("\033[33m║ Usage: exp [arguments...] ║\033[0m\n"); printf("\033[36m╠══════════════════════════════════════════════════════════════╣\033[0m\n"); printf("\033[32m║ 📋 Available Commands: ║\033[0m\n"); printf("\033[36m║ ║\033[0m\n"); printf("\033[37m║ 🔐 login - Login to exploit.or.id account ║\033[0m\n"); printf("\033[37m║ 📁 init - Initialize folder as repository ║\033[0m\n"); printf("\033[37m║ 🔄 change-repo - Change active repository ║\033[0m\n"); printf("\033[37m║ 📊 status - Show current repository status ║\033[0m\n"); printf("\033[37m║ 📤 upload - Upload file/folder to repository ║\033[0m\n"); printf("\033[90m║ Options: [--path ] [--replace] ║\033[0m\n"); printf("\033[37m║ 🏷️ release - Create release for repository ║\033[0m\n"); printf("\033[37m║ 🗑️ delete - Delete file/folder or repository ║\033[0m\n"); printf("\033[37m║ 🏷️ delete-release - Delete release from repository ║\033[0m\n"); printf("\033[37m║ 📥 clone - Clone repository to local directory ║\033[0m\n"); printf("\033[36m║ ║\033[0m\n"); printf("\033[32m║ ⚙️ Options: ║\033[0m\n"); printf("\033[37m║ ❓ --help, -h - Show this help message ║\033[0m\n"); printf("\033[36m╚══════════════════════════════════════════════════════════════╝\033[0m\n"); return 1; } // Create configuration directory if it doesn't exist char *config_dir = get_config_dir(); if (config_dir) { create_dir_if_not_exist(config_dir); free(config_dir); } // Process command const char *command = argv[1]; if (strcmp(command, "login") == 0) { return cmd_login(argc - 1, argv + 1); } else if (strcmp(command, "init") == 0) { return cmd_init(argc - 1, argv + 1); } else if (strcmp(command, "change-repo") == 0) { return cmd_change_repo(argc - 1, argv + 1); } else if (strcmp(command, "status") == 0) { return cmd_status(argc - 1, argv + 1); } else if (strcmp(command, "upload") == 0) { return cmd_upload(argc - 1, argv + 1); } else if (strcmp(command, "release") == 0) { return cmd_release(argc - 1, argv + 1); } else if (strcmp(command, "delete") == 0) { return cmd_delete(argc - 1, argv + 1); } else if (strcmp(command, "delete-release") == 0) { return cmd_delete_release(argc - 1, argv + 1); } else if (strcmp(command, "clone") == 0) { return cmd_clone(argc - 1, argv + 1); } else if (strcmp(command, "--help") == 0 || strcmp(command, "-h") == 0) { printf("\033[36m╔══════════════════════════════════════════════════════════════╗\033[0m\n"); printf("\033[36m║ 🚀 Exploit.or.id CLI ║\033[0m\n"); printf("\033[36m╠══════════════════════════════════════════════════════════════╣\033[0m\n"); printf("\033[33m║ Usage: exp [arguments...] ║\033[0m\n"); printf("\033[36m╠══════════════════════════════════════════════════════════════╣\033[0m\n"); printf("\033[32m║ 📋 Available Commands: ║\033[0m\n"); printf("\033[36m║ ║\033[0m\n"); printf("\033[37m║ 🔐 login - Login to exploit.or.id account ║\033[0m\n"); printf("\033[37m║ 📁 init - Initialize folder as repository ║\033[0m\n"); printf("\033[37m║ 🔄 change-repo - Change active repository ║\033[0m\n"); printf("\033[37m║ 📊 status - Show current repository status ║\033[0m\n"); printf("\033[37m║ 📤 upload - Upload file/folder to repository ║\033[0m\n"); printf("\033[90m║ Options: [--path ] [--replace] ║\033[0m\n"); printf("\033[37m║ 🏷️ release - Create release for repository ║\033[0m\n"); printf("\033[37m║ 🗑️ delete - Delete file/folder or repository ║\033[0m\n"); printf("\033[37m║ 🏷️ delete-release - Delete release from repository ║\033[0m\n"); printf("\033[37m║ 📥 clone - Clone repository to local directory ║\033[0m\n"); printf("\033[36m║ ║\033[0m\n"); printf("\033[32m║ ⚙️ Options: ║\033[0m\n"); printf("\033[37m║ ❓ --help, -h - Show this help message ║\033[0m\n"); printf("\033[36m╚══════════════════════════════════════════════════════════════╝\033[0m\n"); return 0; } else { printf("\033[31m❌ Unknown command: %s\033[0m\n", command); printf("\033[33m💡 Use 'exp --help' to see available commands\033[0m\n"); return 1; } return 0; } // Utility function implementation void create_dir_if_not_exist(const char *path) { struct stat st = {0}; if (stat(path, &st) == -1) { mkdir(path, 0700); } } char *get_config_dir() { char *home_dir = getenv("HOME"); // On Windows/WSL, try USERPROFILE if HOME is not available if (!home_dir) { home_dir = getenv("USERPROFILE"); } // If still no home directory found if (!home_dir) { fprintf(stderr, "Error: Could not determine home directory\n"); return NULL; } char *config_dir = malloc(MAX_PATH); if (!config_dir) { fprintf(stderr, "Error: Memory allocation failed\n"); return NULL; } snprintf(config_dir, MAX_PATH, "%s/%s", home_dir, CONFIG_DIR_NAME); // Create config directory if it doesn't exist struct stat st = {0}; if (stat(config_dir, &st) == -1) { #ifdef _WIN32 mkdir(config_dir); #else mkdir(config_dir, 0700); #endif } return config_dir; } char *get_token_path() { char *config_dir = get_config_dir(); if (!config_dir) { return NULL; } char *token_path = malloc(MAX_PATH); if (!token_path) { free(config_dir); return NULL; } snprintf(token_path, MAX_PATH, "%s/%s", config_dir, TOKEN_FILE_NAME); free(config_dir); return token_path; } char *get_repo_config_path() { char *config_dir = get_config_dir(); if (!config_dir) { return NULL; } char *repo_config_path = malloc(MAX_PATH); if (!repo_config_path) { free(config_dir); return NULL; } snprintf(repo_config_path, MAX_PATH, "%s/repo_config", config_dir); free(config_dir); return repo_config_path; } char *get_token() { char *token_path = get_token_path(); if (!token_path) { return NULL; } FILE *fp = fopen(token_path, "r"); free(token_path); if (!fp) { return NULL; } char *token = malloc(MAX_BUFFER); if (!token) { fclose(fp); return NULL; } if (fgets(token, MAX_BUFFER, fp) == NULL) { free(token); fclose(fp); return NULL; } // Remove newline if present size_t len = strlen(token); if (len > 0 && token[len-1] == '\n') { token[len-1] = '\0'; } fclose(fp); return token; } void save_token(const char *token) { // Get config directory path char *config_dir = get_config_dir(); if (!config_dir) { print_error("Cannot get config directory"); return; } // Create config directory if it doesn't exist create_dir_if_not_exist(config_dir); // Get token file path char *token_path = get_token_path(); if (!token_path) { print_error("Cannot get token file path"); free(config_dir); return; } FILE *fp = fopen(token_path, "w"); if (!fp) { print_error("Cannot save token"); free(config_dir); free(token_path); return; } free(config_dir); free(token_path); fprintf(fp, "%s", token); fclose(fp); // Set file permissions for owner only token_path = get_token_path(); if (token_path) { chmod(token_path, 0600); free(token_path); } } void print_error(const char *message) { fprintf(stderr, "\033[31m❌ Error: %s\033[0m\n", message); } void print_success(const char *message) { printf("\033[32m✅ Success: %s\033[0m\n", message); } void print_info(const char *message) { printf("\033[34mℹ️ Info: %s\033[0m\n", message); } void print_warning(const char *message) { printf("\033[33m⚠️ Warning: %s\033[0m\n", message); } void print_progress(const char *message) { printf("\033[36m🔄 %s\033[0m\n", message); } // Callback to receive data from libcurl size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; http_response_t *mem = (http_response_t *)userp; char *ptr = realloc(mem->data, mem->size + realsize + 1); if (!ptr) { print_error("Not enough memory"); return 0; } mem->data = ptr; memcpy(&(mem->data[mem->size]), contents, realsize); mem->size += realsize; mem->data[mem->size] = 0; return realsize; } // HTTP function implementation http_response_t http_get(const char *url, const char *token) { CURL *curl; CURLcode res; http_response_t response = {0}; response.data = malloc(1); response.size = 0; curl = curl_easy_init(); if (curl) { struct curl_slist *headers = NULL; if (token) { char auth_header[MAX_BUFFER]; snprintf(auth_header, MAX_BUFFER, "Authorization: Bearer %s", token); headers = curl_slist_append(headers, auth_header); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); // Enable following redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Set maximum number of redirects curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); // Disable SSL verification for testing curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); res = curl_easy_perform(curl); if (res != CURLE_OK) { print_error(curl_easy_strerror(res)); } curl_slist_free_all(headers); curl_easy_cleanup(curl); } return response; } http_response_t http_post(const char *url, const char *token, const char *data) { CURL *curl; CURLcode res; http_response_t response = {0}; response.data = malloc(1); response.size = 0; // No debug output in production mode curl = curl_easy_init(); if (curl) { struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); if (token) { char auth_header[MAX_BUFFER]; snprintf(auth_header, MAX_BUFFER, "Authorization: Bearer %s", token); headers = curl_slist_append(headers, auth_header); } // Set URL and POST data curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); // Set callback function to handle response curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); // Enable following redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Ensure POST method is maintained during redirects curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); // Set maximum number of redirects curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); // Disable SSL verification for testing curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); res = curl_easy_perform(curl); if (res != CURLE_OK) { print_error(curl_easy_strerror(res)); } curl_slist_free_all(headers); curl_easy_cleanup(curl); } return response; } http_response_t http_post_multipart(const char *url, const char *token, const char *filepath, const char *fieldname, const char *path) { CURL *curl; CURLcode res; http_response_t response = {0}; response.data = malloc(1); response.size = 0; curl = curl_easy_init(); if (curl) { struct curl_slist *headers = NULL; if (token) { char auth_header[MAX_BUFFER]; snprintf(auth_header, MAX_BUFFER, "Authorization: Bearer %s", token); headers = curl_slist_append(headers, auth_header); } curl_mime *mime = curl_mime_init(curl); curl_mimepart *part = curl_mime_addpart(mime); curl_mime_name(part, fieldname); curl_mime_filedata(part, filepath); // Tambahkan parameter path jika disediakan if (path && strlen(path) > 0) { curl_mimepart *path_part = curl_mime_addpart(mime); curl_mime_name(path_part, "path"); curl_mime_data(path_part, path, CURL_ZERO_TERMINATED); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); res = curl_easy_perform(curl); if (res != CURLE_OK) { print_error(curl_easy_strerror(res)); } curl_mime_free(mime); curl_slist_free_all(headers); curl_easy_cleanup(curl); } return response; } http_response_t http_post_multipart_mime(const char *url, const char *token, curl_mime *mime) { CURL *curl; CURLcode res; http_response_t response = {0}; response.data = malloc(1); response.size = 0; curl = curl_easy_init(); if (curl) { struct curl_slist *headers = NULL; if (token) { char auth_header[MAX_BUFFER]; snprintf(auth_header, MAX_BUFFER, "Authorization: Bearer %s", token); headers = curl_slist_append(headers, auth_header); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); res = curl_easy_perform(curl); if (res != CURLE_OK) { print_error(curl_easy_strerror(res)); } // Note: We don't free the mime structure here because it's passed in // and should be freed by the caller curl_slist_free_all(headers); curl_easy_cleanup(curl); } return response; } http_response_t http_delete(const char *url, const char *token) { CURL *curl; CURLcode res; http_response_t response = {0}; response.data = malloc(1); response.size = 0; curl = curl_easy_init(); if (curl) { struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); if (token) { char auth_header[MAX_BUFFER]; snprintf(auth_header, MAX_BUFFER, "Authorization: Bearer %s", token); headers = curl_slist_append(headers, auth_header); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); // No data payload for DELETE requests curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); res = curl_easy_perform(curl); if (res != CURLE_OK) { print_error(curl_easy_strerror(res)); } curl_slist_free_all(headers); curl_easy_cleanup(curl); } return response; }