diff --git a/src/db.c b/src/db.c index f64a516..de7fdfd 100644 --- a/src/db.c +++ b/src/db.c @@ -1,33 +1,62 @@ +#include "db.h" #include #include #include #include -void print_usage() { - printf("Usage: db \n"); - printf("\tadd [custom_name] \n"); - printf("\tgenerate \n"); - printf("\tupdate \n"); - printf("\tdelete \n"); -} +sqlite3 *db; -void exit_nicely(sqlite3 *db, const char *msg) { - if (msg != NULL) { - fprintf(stderr, "%s\n", msg); +int h_db_open(const char *db_name) { + int ret; + char *err_msg; + + if (db != NULL) { + fprintf(stderr, "There is already a sqlite db open\n"); + return 1; } - print_usage(); - sqlite3_close(db); - exit(1); + + ret = sqlite3_open(db_name, &db); + if (ret != SQLITE_OK) { + fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + h_db_close(); + return 1; + } + + char *sql = "CREATE TABLE IF NOT EXISTS hentai (url TEXT PRIMARY KEY NOT NULL, artist_name TEXT NOT NULL, website_name TEXT NOT NULL) STRICT;"; + ret = sqlite3_exec(db, sql, NULL, 0, &err_msg); + if (ret != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + h_db_close(); + return 1; + } + + return 0; } -static int print_callback(void *unused, int argc, char **argv, char **col_name) { - for (size_t i = 0; i < argc; i++) { - printf("%s", argv[i] ? argv[i] : "NULL"); +void h_db_close() { + sqlite3_close(db); +} + +static int fprintf_callback(void *fptr, int argc, char **argv, char **col_name) { + if (fptr == NULL) { + fprintf(stderr, "fptr is NULL\n"); + return 1; + } + + size_t i; + for (i = 0; i < argc; i++) { + fprintf(fptr, "%s", argv[i] ? argv[i] : "NULL"); if (i < argc - 1) { - printf(" "); + fprintf(fptr, " "); } } - printf("\n"); + fprintf(fptr, "\n"); + return 0; +} + +static int db_row_callback(void *db_row, int argc, char **argv, char **col_name) { + snprintf(db_row, strlen(argv[0]) * 2, "%s %s %s", argv[0], argv[1], argv[2]); return 0; } @@ -54,175 +83,176 @@ char* get_artist_from_url(const char *url, const char *website_name) { } size_t name_length = strlen(url) - offset; - char *artist_name = malloc(name_length + 1); - memcpy(artist_name, url + offset, name_length); - artist_name[name_length] = '\0'; + size_t char_count = name_length; + char *tmp_name = malloc(name_length + 1); + memcpy(tmp_name, url + offset, name_length); + tmp_name[name_length] = '\0'; + + size_t i; + size_t j = 0; + for (i = 0; i < strlen(tmp_name); i++) { + if (tmp_name[i] == '%') { + char url_encoded[4]; + url_encoded[0] = tmp_name[i]; + url_encoded[1] = tmp_name[i + 1]; + url_encoded[2] = tmp_name[i + 2]; + url_encoded[3] = '\0'; + if (strstr("%20%21%22%23%24%26%27%28%29%2A%2C%2E%2F%3B%3C%3E%3F%5B%5C%5D%5E%60%7B%7C%7D%7E", url_encoded) != NULL) { + tmp_name[i] = ' '; + char_count -= 1; + } else if (strcmp(url_encoded, "%25") == 0) { + tmp_name[i] = '%'; + } else if (strcmp(url_encoded, "%2B") == 0) { + tmp_name[i] = '+'; + } else if (strcmp(url_encoded, "%2D") == 0) { + tmp_name[i] = '-'; + } else if (strcmp(url_encoded, "%2E") == 0) { + tmp_name[i] = '.'; + } else if (strcmp(url_encoded, "%2F") == 0) { + tmp_name[i] = '/'; + } else if (strcmp(url_encoded, "%3A") == 0) { + tmp_name[i] = ':'; + } else if (strcmp(url_encoded, "%3D") == 0) { + tmp_name[i] = '='; + } else if (strcmp(url_encoded, "%40") == 0) { + tmp_name[i] = '@'; + } else if (strcmp(url_encoded, "%5F") == 0) { + tmp_name[i] = '_'; + } else { + return NULL; + } + tmp_name[i += 1] = ' '; + tmp_name[i += 1] = ' '; + char_count -= 2; + } + } + + char *artist_name = malloc(char_count + 1); + memset(artist_name, 0, char_count + 1); + for (i = 0; i < name_length; i++) { + if (tmp_name[i] == ' ') { + continue; + } + artist_name[j] = tmp_name[i]; + j++; + } + artist_name[char_count] = '\0'; + + j = strlen(artist_name) - strlen("artist"); + if (strcmp(artist_name + j, "artist") == 0) { + for (; j < strlen(artist_name); j++) { + artist_name[j] = '\0'; + } + } + + j = strlen(artist_name) - 1; + while (artist_name[j] == '_') { + artist_name[j] = '\0'; + j--; + } + + if (strlen(artist_name) < 2) { + return NULL; + } return artist_name; } -bool add(sqlite3 *db, const char *url) { - char *sql; +int h_db_add(char *db_row, const char *url) { + char *sql = malloc(10000); int result; char *error_msg; char *website_name = get_website_from_url(url); if (website_name == NULL) { fprintf(stderr, "Couldn't get website_name from url %s\n", url); - return false; + return 1; } char *artist_name = get_artist_from_url(url, website_name); if (artist_name == NULL) { fprintf(stderr, "Couldn't get artist_name from url %s\n", url); - return false; + return 1; } - sprintf(sql, "INSERT INTO hentai VALUES ('%s', '%s', '%s');", url, artist_name, website_name); - result = sqlite3_exec(db, sql, print_callback, 0, &error_msg); + sprintf(sql, "INSERT INTO hentai VALUES ('%s', '%s', '%s'); SELECT * from hentai WHERE url = '%s';", url, artist_name, website_name, url); + result = sqlite3_exec(db, sql, db_row_callback, db_row, &error_msg); + free(sql); + free(artist_name); if (result != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error_msg); sqlite3_free(error_msg); - return false; + return 1; } - return true; + return 0; } -bool add_custom_name(sqlite3 *db, const char *url, const char *custom_name) { - char *sql; +int h_db_add_custom_name(char *db_row, const char *url, const char *custom_name) { + char *sql = malloc(10000); int result; char *error_msg; char *website_name = get_website_from_url(url); if (website_name == NULL) { fprintf(stderr, "Couldn't get website_name from url %s\n", url); - return false; + return 1; } - sprintf(sql, "INSERT INTO hentai VALUES ('%s', '%s', '%s');", url, custom_name, website_name); - result = sqlite3_exec(db, sql, print_callback, 0, &error_msg); + sprintf(sql, "INSERT INTO hentai VALUES ('%s', '%s', '%s'); SELECT * from hentai WHERE url = '%s';", url, custom_name, website_name, url); + result = sqlite3_exec(db, sql, db_row_callback, db_row, &error_msg); + free(sql); if (result != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error_msg); sqlite3_free(error_msg); - return false; + return 1; } - return true; + return 0; } -bool generate(sqlite3 *db, const char *website_name) { - char *sql; +int h_db_generate_file(FILE *fptr, const char *website_name) { + char *sql = malloc(10000); int result; char *error_msg; sprintf(sql, "SELECT * FROM hentai WHERE website_name = '%s';", website_name); - result = sqlite3_exec(db, sql, print_callback, 0, &error_msg); + result = sqlite3_exec(db, sql, fprintf_callback, fptr, &error_msg); + free(sql); if (result != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error_msg); sqlite3_free(error_msg); - return false; + return 1; } - return true; + return 0; } -bool update_artist_name(sqlite3 *db, const char *url, const char *new_artist_name) { - char *sql; +int h_db_update_artist_name(const char *url, const char *new_artist_name) { + char *sql = malloc(10000); int result; char *error_msg; sprintf(sql, "UPDATE hentai set artist_name = '%s' WHERE url = '%s';", new_artist_name, url); - result = sqlite3_exec(db, sql, print_callback, 0, &error_msg); + result = sqlite3_exec(db, sql, fprintf_callback, stdout, &error_msg); + free(sql); if (result != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error_msg); sqlite3_free(error_msg); - return false; + return 1; } - return true; + return 0; } -bool delete(sqlite3 *db, const char *url) { - char *sql; +int h_db_delete(const char *url) { + char *sql = malloc(10000); int result; char *error_msg; sprintf(sql, "DELETE FROM hentai WHERE url = '%s';", url); - result = sqlite3_exec(db, sql, print_callback, 0, &error_msg); + result = sqlite3_exec(db, sql, fprintf_callback, stdout, &error_msg); + free(sql); if (result != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error_msg); sqlite3_free(error_msg); - return false; + return 1; } - return true; -} - -int main(int argc, char **argv) { - sqlite3 *db; - int result; - char *error_msg; - char *sql; - bool successful; - - result = sqlite3_open("/tmp/Hentai.db", &db); - if (result != SQLITE_OK) { - exit_nicely(db, sqlite3_errmsg(db)); - } - - sql = "CREATE TABLE IF NOT EXISTS hentai (url TEXT PRIMARY KEY NOT NULL, artist_name TEXT NOT NULL, website_name TEXT NOT NULL) STRICT;"; - result = sqlite3_exec(db, sql, NULL, 0, &error_msg); - if (result != SQLITE_OK) { - fprintf(stderr, "SQL error: %s\n", error_msg); - sqlite3_free(error_msg); - exit_nicely(db, NULL); - } - - if (argc < 2) { - exit_nicely(db, "Incorrect amount of arguments"); - } - - if (strcmp(argv[1], "add") == 0) { - if (argc == 3) { - successful = add(db, argv[2]); - if (!successful) { - exit_nicely(db, NULL); - } - } else if (argc == 4) { - successful = add_custom_name(db, argv[2], argv[3]); - if (!successful) { - exit_nicely(db, NULL); - } - } else { - exit_nicely(db, "Incorrect amount of arguments"); - } - } else if (strcmp(argv[1], "generate") == 0) { - if (argc == 3) { - successful = generate(db, argv[2]); - if (!successful) { - exit_nicely(db, NULL); - } - } else { - exit_nicely(db, "Incorrect amount of arguments"); - } - } else if (strcmp(argv[1], "update") == 0) { - if (argc == 4) { - successful = update_artist_name(db, argv[3], argv[2]); - if (!successful) { - exit_nicely(db, NULL); - } - } else { - exit_nicely(db, "Incorrect amount of arguments"); - } - - } else if (strcmp(argv[1], "delete") == 0) { - if (argc == 3) { - successful = delete(db, argv[2]); - if (!successful) { - exit_nicely(db, NULL); - } - } else { - exit_nicely(db, "Incorrect amount of arguments"); - } - } else { - exit_nicely(db, "Invalid argument"); - } - - sqlite3_close(db); return 0; } + diff --git a/src/db.h b/src/db.h new file mode 100644 index 0000000..37816bb --- /dev/null +++ b/src/db.h @@ -0,0 +1,14 @@ +#ifndef HENTAI_DB_H +#define HENTAI_DB_H + +#include + +int h_db_open(const char *db_name); +void h_db_close(); +int h_db_add(char *db_row, const char *url); +int h_db_add_custom_name(char *db_row, const char *url, const char *artist_name); +int h_db_generate_file(FILE *fptr, const char *website_name); +int h_db_update_artist_name(const char *url, const char *new_artist_name); +int h_db_delete(const char *url); + +#endif /* HENTAI_DB_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..8dc5919 --- /dev/null +++ b/src/main.c @@ -0,0 +1,303 @@ +#define _POSIX_C_SOURCE 200112L /* needed for struct addrinfo */ + +#include "db.h" +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *data_dir; + char *log_dir; + char *tmp_dir; + char *gallery_dl_conf; + char *yt_dlp_conf; +} Config; + +int h_config_parser(Config *config) { + char *config_location = malloc(10000); + char *user_name = getenv("USER"); + if (user_name == NULL) { + fprintf(stderr, "Couldn't read env variable USER\n"); + return 1; + } + sprintf(config_location, "/home/%s/.config/Hentai.conf", user_name); + + FILE *fptr = fopen(config_location, "r"); + if (fptr == NULL) { + fprintf(stderr, "Failed to open the config %s\n", config_location); + return 1; + } + + char *line = malloc(10000); + char *left = malloc(10000); + char *right = malloc(10000); + + while(fgets(line, 10000, fptr) != NULL) { + line[strcspn(line, "\n#")] = '\0'; + if (strcmp(line, "") == 0) { + continue; + } + if ( + strchr(line, '=') == NULL + || line[strcspn(line, "=") + 1] == ' ' + || line[strcspn(line, "=") - 1] == ' ' + ) { + fprintf(stderr, "Config line doesn't contain a valid assignment. line = '%s'\n", line); + continue; + } + /* TODO: support env variables */ + if (strchr(line, '$') != NULL) { + fprintf(stderr, "Config doesn't support env variables. line = '%s'\n", line); + continue; + } + + left = strtok(line, "="); + right = strtok(NULL, "\0"); + if (strcmp(left, "data_dir") == 0) { + config->data_dir = malloc(10000); + memcpy(config->data_dir, right, strlen(right)); + } else if (strcmp(left, "log_dir") == 0) { + config->log_dir = malloc(10000); + memcpy(config->log_dir, right, strlen(right)); + } else if (strcmp(left, "tmp_dir") == 0) { + config->tmp_dir = malloc(10000); + memcpy(config->tmp_dir, right, strlen(right)); + } else if (strcmp(left, "gallery-dl_conf") == 0) { + config->gallery_dl_conf = malloc(10000); + memcpy(config->gallery_dl_conf, right, strlen(right)); + } else if (strcmp(left, "yt-dlp_conf") == 0) { + config->yt_dlp_conf = malloc(10000); + memcpy(config->yt_dlp_conf, right, strlen(right)); + } else { + fprintf(stderr, "Unknown config option. left = '%s'\n", left); + } + } + fclose(fptr); + free(line); + free(config_location); + return 0; +} + +int h_command_builder(char *command, char **args) { + char *tmp_command = malloc(strlen(command)); + memcpy(tmp_command, command, strlen(command)); + char *tmp_token; + size_t index = 0; + + if (tmp_command == NULL) { return 1; } + + tmp_token = strtok(tmp_command, " "); + while(tmp_token != NULL) { + args[index] = malloc(strlen(tmp_token)); + memcpy(args[index], tmp_token, strlen(tmp_token)); + tmp_token = strtok(NULL, " "); + index++; + } + free(tmp_command); + args[index++] = NULL; + return 0; +} + +int h_run_cmd_and_wait(char *command) { + char **args = malloc(strlen(command)); + int ret = h_command_builder(command, args); + if (ret != 0) { + return 1; + } + + pid_t pid = fork(); + int status; + + if (pid == -1) { + printf("Couldn't fork'\n"); + return 1; + } else if (pid > 0) { + waitpid(pid, &status, 0); + } else { + execvp(args[0], args); + } + free(args); + + return status; +} + +int h_has_internet() { + int ret; + + /* https://www.man7.org/linux/man-pages/man3/getaddrinfo.3.html */ + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + struct addrinfo *res, *rp; + ret = getaddrinfo("hopeless-cloud.xyz", "80", 0, &res); + if (ret != 0) { + return 1; + } + int sockfd; + for (rp = res; rp != NULL; rp = rp->ai_next) { + /* https://www.man7.org/linux/man-pages/man2/socket.2.html */ + sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sockfd == -1) { continue; } + + /* https://www.man7.org/linux/man-pages/man2/connect.2.html */ + if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0) { break; } + + close(sockfd); + } + freeaddrinfo(res); + if (rp == NULL) { + return 1; + } + + /* https://www.man7.org/linux/man-pages/man2/write.2.html */ + char *req_msg = "GET / HTTP/1.1\r\n\r\n"; + if (write(sockfd, req_msg, strlen(req_msg)) != strlen(req_msg)) { + return 1; + } + + /* https://www.man7.org/linux/man-pages/man2/recv.2.html */ + size_t count = 1000000; + char buf[count]; + if (recv(sockfd, buf, count-1, MSG_WAITALL) == -1 ) { + return 1; + } + close(sockfd); + return 0; +} + +int h_download(char *db_row, Config config) { + int ret; + char *url = malloc(strlen(db_row)); + char *artist_name = malloc(strlen(db_row)); + char *website_name = malloc(strlen(db_row)); + sscanf(db_row, "%s %s %s", url, artist_name, website_name); + char *path = malloc(strlen(artist_name) + strlen(website_name) + 2); + sprintf(path, "%s/%s", artist_name, website_name); + char *command = malloc(10000); + if (strstr(website_name, "Iwara") == 0) { + sprintf(command, "yt-dlp_linux --config-locations %s -o %s/%(id)s.%(ext)s %s", config.yt_dlp_conf, path, url); + } else { + sprintf(command, "gallery-dl --config %s --directory %s/%s %s", config.gallery_dl_conf, config.data_dir, path, url); + } + free(url); + free(artist_name); + free(website_name); + + size_t i = 5; + for (; i > 0; i--) { + printf("%s\n", command); + /* h_run_cmd_and_wait(command) */ + ret = h_has_internet(); + if (ret != 0) { + fprintf(stderr, "No internet\n"); + return 1; + } + } + free(command); + return 0; +} + +int main(int argc, char **argv) { + int ret; + Config config = { 0 }; + + ret = h_config_parser(&config); + if (ret != 0) { + fprintf(stderr, "Failed to parse config\n"); + return 1; + } + + if (argc < 2) { + fprintf(stderr, "Incorrect amount of arguments\n"); + return 1; + } + + ret = h_db_open(":memory:"); + if (ret != 0) { + return 1; + } + + if (strcmp(argv[1], "add") == 0) { + char *db_row = malloc(strlen(argv[2]) * 2); + memset(db_row, 0, strlen(argv[2])); + + if (argc == 3) { + ret = h_db_add(db_row, argv[2]); + if (ret != 0) { + h_db_close(); + return 1; + } + } else if (argc == 4) { + ret = h_db_add_custom_name(db_row, argv[2], argv[3]); + if (ret != 0) { + h_db_close(); + return 1; + } + } else { + h_db_close(); + fprintf(stderr, "Incorrect amount of arguments\n"); + return 1; + } + ret = h_download(db_row, config); + free(db_row); + if (ret != 0) { + h_db_close(); + return 1; + } + } else if (strcmp(argv[1], "download_website") == 0) { + char *website_name = argv[2]; + char *file_name = malloc(10000); + if (argc == 3) { + sprintf(file_name, "%s_generated.txt", website_name); + FILE *fptr = fopen(file_name, "w+"); + free(file_name); + if (fptr == NULL) { + fprintf(stderr, "Couldn't open file\n"); + h_db_close(); + return 1; + } + + ret = h_db_generate_file(fptr, argv[2]); + fclose(fptr); + if (ret != 0) { + h_db_close(); + return 1; + } + } else if (argc == 4) { + sprintf(file_name, "%s/%s_generated.txt", argv[3], website_name); + FILE *fptr = fopen(file_name, "w+"); + free(file_name); + if (fptr == NULL) { + fprintf(stderr, "Couldn't open file\n"); + h_db_close(); + return 1; + } + + ret = h_db_generate_file(fptr, argv[2]); + fclose(fptr); + if (ret != 0) { + h_db_close(); + return 1; + } + } else { + fprintf(stderr, "Incorrect amount of arguments\n"); + h_db_close(); + return 1; + } + } else { + fprintf(stderr, "Invalid argument\n"); + h_db_close(); + return 1; + } + + h_db_close(); + return 0; +}