mirror of
https://github.com/vanhauser-thc/thc-hydra.git
synced 2025-08-19 21:03:52 -07:00
feat: Convert dpl4hydra from shell script to C for performance
- Rewrite dpl4hydra.sh in C for improved performance and portability - Implement multi-threaded downloads for faster data retrieval - Add enhanced brand listing with pagination and search functionality - Improve error handling and memory management - Maintain original functionality while adding quality-of-life improvements
This commit is contained in:
parent
03cdc31f98
commit
23a5585965
1 changed files with 503 additions and 0 deletions
503
dpl4hydra.c
Normal file
503
dpl4hydra.c
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
// dpl4hydra.c
|
||||||
|
// Author: Volker Schwaberow <volker@schwaberow.de>
|
||||||
|
// Copyright (c) 2012 Roland Kessler (@rokessler)
|
||||||
|
// Copyright (c) 2024 Volker Schwaberow
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define MAX_LINE_LENGTH 2048
|
||||||
|
#define MAX_FILENAME_LENGTH 256
|
||||||
|
#define MAX_FIELD_LENGTH 256
|
||||||
|
#define MAX_THREADS 8
|
||||||
|
#define MAX_VENDORS 1000
|
||||||
|
#define HREF_PREFIX_LENGTH 24
|
||||||
|
#define COLUMN_WIDTH 25
|
||||||
|
#define NUM_COLUMNS 2
|
||||||
|
#define TABLE_WIDTH (COLUMN_WIDTH * NUM_COLUMNS + NUM_COLUMNS + 1)
|
||||||
|
#define BRANDS_PER_PAGE 20
|
||||||
|
|
||||||
|
#define BOX_HORIZONTAL "─"
|
||||||
|
#define BOX_VERTICAL "│"
|
||||||
|
#define BOX_TOP_LEFT "┌"
|
||||||
|
#define BOX_TOP_RIGHT "┐"
|
||||||
|
#define BOX_BOTTOM_LEFT "└"
|
||||||
|
#define BOX_BOTTOM_RIGHT "┘"
|
||||||
|
#define BOX_T_DOWN "┬"
|
||||||
|
#define BOX_T_UP "┴"
|
||||||
|
#define BOX_T_RIGHT "├"
|
||||||
|
#define BOX_T_LEFT "┤"
|
||||||
|
#define BOX_CROSS "┼"
|
||||||
|
|
||||||
|
const char *SITE = "https://cirt.net/passwords";
|
||||||
|
const char *FULLFILE = "dpl4hydra_full.csv";
|
||||||
|
const char *OLDFILE = "dpl4hydra_full.old";
|
||||||
|
const char *LOCALFILE = "dpl4hydra_local.csv";
|
||||||
|
const char *INDEXSITE = "dpl4hydra_index.tmp";
|
||||||
|
const char *SUBSITES = "dpl4hydra_subs.tmp";
|
||||||
|
const char *CLEANFILE = "dpl4hydra_clean.tmp";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *url;
|
||||||
|
const char *filename;
|
||||||
|
} DownloadTask;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char vendor[MAX_FIELD_LENGTH];
|
||||||
|
char system[MAX_FIELD_LENGTH];
|
||||||
|
char url[MAX_FIELD_LENGTH];
|
||||||
|
char username[MAX_FIELD_LENGTH];
|
||||||
|
char password[MAX_FIELD_LENGTH];
|
||||||
|
} VendorEntry;
|
||||||
|
|
||||||
|
void usage(const char *program_name);
|
||||||
|
size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
void *download_file_thread(void *arg);
|
||||||
|
void download_files_parallel(DownloadTask *tasks, int num_tasks);
|
||||||
|
void refresh();
|
||||||
|
void generate(const char *brand);
|
||||||
|
int case_insensitive_search(const char *haystack, const char *needle);
|
||||||
|
void parse_vendor_page(const char *filename, const char *vendor);
|
||||||
|
void clean_string(char *str);
|
||||||
|
|
||||||
|
void usage(const char *program_name) {
|
||||||
|
printf("dpl4hydra v1.0.0 (c) 2024\n\n");
|
||||||
|
printf("(c) 2012 Roland Kessler (@rokessler)\n");
|
||||||
|
printf("(c) 2024 Volker Schwaberow <volker@schwaberow.de>\n\n");
|
||||||
|
printf("Syntax: %s [help] | [refresh] | [list] | [BRAND] | [all]\n\n", program_name);
|
||||||
|
printf("Options:\n");
|
||||||
|
printf(" help Show this help message\n");
|
||||||
|
printf(" refresh Download and refresh the full default password list\n");
|
||||||
|
printf(" list List all available brands\n");
|
||||||
|
printf(" BRAND Generate a default password list for a specific BRAND\n");
|
||||||
|
printf(" all Generate a list of all system credentials\n\n");
|
||||||
|
printf("Example:\n");
|
||||||
|
printf(" %s linksys\n", program_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||||
|
size_t realsize = size * nmemb;
|
||||||
|
FILE *fp = (FILE *)userp;
|
||||||
|
return fwrite(contents, size, nmemb, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_horizontal_line(const char *left, const char *middle, const char *right) {
|
||||||
|
printf("%s", left);
|
||||||
|
for (int i = 0; i < NUM_COLUMNS; i++) {
|
||||||
|
for (int j = 0; j < COLUMN_WIDTH; j++) {
|
||||||
|
printf("%s", BOX_HORIZONTAL);
|
||||||
|
}
|
||||||
|
if (i < NUM_COLUMNS - 1) {
|
||||||
|
printf("%s", middle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%s\n", right);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_word_wrapped(const char *text, int width) {
|
||||||
|
int len = strlen(text);
|
||||||
|
printf("%-*.*s", width, width, text);
|
||||||
|
if (len > width) {
|
||||||
|
printf("...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_brands() {
|
||||||
|
FILE *input = fopen(FULLFILE, "r");
|
||||||
|
if (!input) {
|
||||||
|
fprintf(stderr, "Error: Cannot open input file %s.\n", FULLFILE);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_LINE_LENGTH];
|
||||||
|
char brand[MAX_FIELD_LENGTH];
|
||||||
|
char prev_brand[MAX_FIELD_LENGTH] = "";
|
||||||
|
char **brands = NULL;
|
||||||
|
int brand_count = 0;
|
||||||
|
int capacity = 10;
|
||||||
|
|
||||||
|
brands = malloc(capacity * sizeof(char *));
|
||||||
|
if (!brands) {
|
||||||
|
fprintf(stderr, "Error: Memory allocation failed.\n");
|
||||||
|
fclose(input);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), input)) {
|
||||||
|
if (sscanf(line, "%[^,]", brand) == 1) {
|
||||||
|
if (strcmp(brand, prev_brand) != 0) {
|
||||||
|
if (brand_count >= capacity) {
|
||||||
|
capacity *= 2;
|
||||||
|
char **temp = realloc(brands, capacity * sizeof(char *));
|
||||||
|
if (!temp) {
|
||||||
|
fprintf(stderr, "Error: Memory reallocation failed.\n");
|
||||||
|
fclose(input);
|
||||||
|
for (int i = 0; i < brand_count; i++) {
|
||||||
|
free(brands[i]);
|
||||||
|
}
|
||||||
|
free(brands);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
brands = temp;
|
||||||
|
}
|
||||||
|
brands[brand_count] = strdup(brand);
|
||||||
|
if (!brands[brand_count]) {
|
||||||
|
fprintf(stderr, "Error: Memory allocation failed for brand.\n");
|
||||||
|
fclose(input);
|
||||||
|
for (int i = 0; i < brand_count; i++) {
|
||||||
|
free(brands[i]);
|
||||||
|
}
|
||||||
|
free(brands);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
strcpy(prev_brand, brand);
|
||||||
|
brand_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(input);
|
||||||
|
|
||||||
|
int total_brands = brand_count;
|
||||||
|
int filtered_brand_count = brand_count;
|
||||||
|
int current_page = 1;
|
||||||
|
int start_index = 0;
|
||||||
|
char search_term[MAX_FIELD_LENGTH] = "";
|
||||||
|
int search_mode = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (search_mode) {
|
||||||
|
filtered_brand_count = 0;
|
||||||
|
for (int i = 0; i < brand_count; i++) {
|
||||||
|
if (case_insensitive_search(brands[i], search_term)) {
|
||||||
|
filtered_brand_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int total_pages = (filtered_brand_count + BRANDS_PER_PAGE - 1) / BRANDS_PER_PAGE;
|
||||||
|
if (total_pages == 0)
|
||||||
|
total_pages = 1;
|
||||||
|
|
||||||
|
printf("Available brands");
|
||||||
|
if (search_mode) {
|
||||||
|
printf(" (filtered by: %s)", search_term);
|
||||||
|
}
|
||||||
|
printf(" (Page %d of %d):\n\n", current_page, total_pages);
|
||||||
|
|
||||||
|
print_horizontal_line(BOX_TOP_LEFT, BOX_T_DOWN, BOX_TOP_RIGHT);
|
||||||
|
|
||||||
|
int displayed_brands = 0;
|
||||||
|
int filtered_index = 0;
|
||||||
|
for (int i = 0; i < brand_count && displayed_brands < BRANDS_PER_PAGE; i++) {
|
||||||
|
if (!search_mode || case_insensitive_search(brands[i], search_term)) {
|
||||||
|
if (filtered_index >= start_index) {
|
||||||
|
if (displayed_brands % NUM_COLUMNS == 0 && displayed_brands > 0) {
|
||||||
|
printf("%s\n", BOX_VERTICAL);
|
||||||
|
print_horizontal_line(BOX_T_RIGHT, BOX_CROSS, BOX_T_LEFT);
|
||||||
|
}
|
||||||
|
printf("%s ", BOX_VERTICAL);
|
||||||
|
print_word_wrapped(brands[i], COLUMN_WIDTH - 2);
|
||||||
|
printf(" ");
|
||||||
|
displayed_brands++;
|
||||||
|
}
|
||||||
|
filtered_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (displayed_brands % NUM_COLUMNS != 0) {
|
||||||
|
printf("%s %-*s ", BOX_VERTICAL, COLUMN_WIDTH - 2, "");
|
||||||
|
displayed_brands++;
|
||||||
|
}
|
||||||
|
printf("%s\n", BOX_VERTICAL);
|
||||||
|
|
||||||
|
print_horizontal_line(BOX_BOTTOM_LEFT, BOX_T_UP, BOX_BOTTOM_RIGHT);
|
||||||
|
|
||||||
|
printf("\nTotal number of brands: %d", filtered_brand_count);
|
||||||
|
if (search_mode) {
|
||||||
|
printf(" (filtered from %d)", total_brands);
|
||||||
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
printf("Enter 'n' for next page, 'p' for previous page, 's' to search, 'c' to clear search, or 'q' to quit: ");
|
||||||
|
char choice;
|
||||||
|
scanf(" %c", &choice);
|
||||||
|
|
||||||
|
switch (choice) {
|
||||||
|
case 'n':
|
||||||
|
if (current_page < total_pages) {
|
||||||
|
current_page++;
|
||||||
|
start_index += BRANDS_PER_PAGE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (current_page > 1) {
|
||||||
|
current_page--;
|
||||||
|
start_index -= BRANDS_PER_PAGE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
printf("Enter search term: ");
|
||||||
|
scanf("%s", search_term);
|
||||||
|
search_mode = 1;
|
||||||
|
current_page = 1;
|
||||||
|
start_index = 0;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
search_mode = 0;
|
||||||
|
current_page = 1;
|
||||||
|
start_index = 0;
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
printf("Invalid choice. Please try again.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\033[2J\033[H");
|
||||||
|
|
||||||
|
for (int i = 0; i < brand_count; i++) {
|
||||||
|
free(brands[i]);
|
||||||
|
}
|
||||||
|
free(brands);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *download_file_thread(void *arg) {
|
||||||
|
DownloadTask *task = (DownloadTask *)arg;
|
||||||
|
CURL *curl;
|
||||||
|
FILE *fp;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (curl) {
|
||||||
|
fp = fopen(task->filename, "wb");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, task->url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
fprintf(stderr, "Error: Download failed for %s - %s\n", task->url, curl_easy_strerror(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void download_files_parallel(DownloadTask *tasks, int num_tasks) {
|
||||||
|
pthread_t threads[MAX_THREADS];
|
||||||
|
int i, thread_index = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < num_tasks; i++) {
|
||||||
|
pthread_create(&threads[thread_index], NULL, download_file_thread, &tasks[i]);
|
||||||
|
thread_index = (thread_index + 1) % MAX_THREADS;
|
||||||
|
|
||||||
|
if (thread_index == 0 || i == num_tasks - 1) {
|
||||||
|
for (int j = 0; j < MAX_THREADS && j <= i; j++) {
|
||||||
|
pthread_join(threads[j], NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clean_string(char *str) {
|
||||||
|
char *src = str;
|
||||||
|
char *dst = str;
|
||||||
|
|
||||||
|
while (*src) {
|
||||||
|
if (*src != '\r' && *src != '\n' && *src != '\t') {
|
||||||
|
*dst = *src;
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_vendor_page(const char *filename, const char *vendor) {
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
if (!fp) {
|
||||||
|
fprintf(stderr, "Error: Cannot open file %s\n", filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *out = fopen(FULLFILE, "a");
|
||||||
|
if (!out) {
|
||||||
|
fprintf(stderr, "Error: Cannot open file %s for writing\n", FULLFILE);
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_LINE_LENGTH];
|
||||||
|
char username[256], password[256], url[256], system[256];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
clean_string(line);
|
||||||
|
if (sscanf(line, "%*[^>]>%255[^<]</td>%*[^>]>%255[^<]</td>%*[^>]>%255[^<]</td>%*[^>]>%255[^<]", username, password, url, system) == 4) {
|
||||||
|
fprintf(out, "%s,%s,%s,%s,%s,%s\n", vendor, system, url, "", username, password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
printf("Refreshing password list...\n");
|
||||||
|
|
||||||
|
DownloadTask index_task = {SITE, INDEXSITE};
|
||||||
|
download_files_parallel(&index_task, 1);
|
||||||
|
|
||||||
|
FILE *index_file = fopen(INDEXSITE, "r");
|
||||||
|
if (!index_file) {
|
||||||
|
fprintf(stderr, "Error: Cannot open index file\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_LINE_LENGTH];
|
||||||
|
char vendor_urls[MAX_VENDORS][256];
|
||||||
|
char vendor_names[MAX_VENDORS][64];
|
||||||
|
int vendor_count = 0;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), index_file) && vendor_count < MAX_VENDORS) {
|
||||||
|
char *start = strstr(line, "href=\"/passwords?vendor=");
|
||||||
|
if (start) {
|
||||||
|
start += HREF_PREFIX_LENGTH;
|
||||||
|
char *end = strchr(start, '"');
|
||||||
|
if (end) {
|
||||||
|
*end = '\0';
|
||||||
|
snprintf(vendor_urls[vendor_count], sizeof(vendor_urls[vendor_count]), "https://cirt.net/passwords?vendor=%s", start);
|
||||||
|
strncpy(vendor_names[vendor_count], start, sizeof(vendor_names[vendor_count]));
|
||||||
|
vendor_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(index_file);
|
||||||
|
|
||||||
|
printf("Found %d vendors. Downloading vendor pages...\n", vendor_count);
|
||||||
|
|
||||||
|
DownloadTask *vendor_tasks = malloc(vendor_count * sizeof(DownloadTask));
|
||||||
|
for (int i = 0; i < vendor_count; i++) {
|
||||||
|
vendor_tasks[i].url = vendor_urls[i];
|
||||||
|
vendor_tasks[i].filename = malloc(MAX_FILENAME_LENGTH);
|
||||||
|
snprintf((char *)vendor_tasks[i].filename, MAX_FILENAME_LENGTH, "vendor_%s.html", vendor_names[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
download_files_parallel(vendor_tasks, vendor_count);
|
||||||
|
|
||||||
|
if (access(FULLFILE, F_OK) != -1) {
|
||||||
|
rename(FULLFILE, OLDFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < vendor_count; i++) {
|
||||||
|
parse_vendor_page(vendor_tasks[i].filename, vendor_names[i]);
|
||||||
|
remove(vendor_tasks[i].filename);
|
||||||
|
free((void *)vendor_tasks[i].filename);
|
||||||
|
}
|
||||||
|
free(vendor_tasks);
|
||||||
|
|
||||||
|
printf("Refreshed password list created.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int case_insensitive_search(const char *haystack, const char *needle) {
|
||||||
|
const char *p1 = haystack, *p2 = needle;
|
||||||
|
while (*p1 && *p2) {
|
||||||
|
if (tolower((unsigned char)*p1) != tolower((unsigned char)*p2))
|
||||||
|
return 0;
|
||||||
|
p1++;
|
||||||
|
p2++;
|
||||||
|
}
|
||||||
|
return *p2 == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate(const char *brand) {
|
||||||
|
char output_filename[MAX_FILENAME_LENGTH];
|
||||||
|
snprintf(output_filename, sizeof(output_filename), "dpl4hydra_%s.lst", brand);
|
||||||
|
|
||||||
|
FILE *input = fopen(FULLFILE, "r");
|
||||||
|
FILE *output = fopen(output_filename, "w");
|
||||||
|
|
||||||
|
if (!input || !output) {
|
||||||
|
fprintf(stderr, "Error: Cannot open input or output file.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_LINE_LENGTH];
|
||||||
|
int entries = 0;
|
||||||
|
int all_brands = strcmp(brand, "all") == 0;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), input)) {
|
||||||
|
if (all_brands || case_insensitive_search(line, brand)) {
|
||||||
|
char *username = strchr(line, ',');
|
||||||
|
if (username) {
|
||||||
|
username = strchr(username + 1, ',');
|
||||||
|
if (username) {
|
||||||
|
username = strchr(username + 1, ',');
|
||||||
|
if (username) {
|
||||||
|
username = strchr(username + 1, ',');
|
||||||
|
if (username) {
|
||||||
|
char *password = strchr(username + 1, ',');
|
||||||
|
if (password) {
|
||||||
|
*password = '\0';
|
||||||
|
password++;
|
||||||
|
char *end = strchr(password, ',');
|
||||||
|
if (end)
|
||||||
|
*end = '\0';
|
||||||
|
|
||||||
|
username++;
|
||||||
|
if (*username && *password) {
|
||||||
|
fprintf(output, "%s:%s\n", username, password);
|
||||||
|
entries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(input);
|
||||||
|
fclose(output);
|
||||||
|
|
||||||
|
printf("File %s was created with %d entries.\n", output_filename, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup() {
|
||||||
|
remove(INDEXSITE);
|
||||||
|
remove(SUBSITES);
|
||||||
|
remove(CLEANFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "help") == 0) {
|
||||||
|
usage(argv[0]);
|
||||||
|
} else if (strcmp(argv[1], "refresh") == 0) {
|
||||||
|
refresh();
|
||||||
|
} else if (strcmp(argv[1], "list") == 0) {
|
||||||
|
list_brands();
|
||||||
|
} else if (strcmp(argv[1], "all") == 0) {
|
||||||
|
generate("all");
|
||||||
|
} else {
|
||||||
|
generate(argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_global_cleanup();
|
||||||
|
cleanup();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue