#define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" // Global variables for preferences static char default_pad_path[1024] = ""; void show_progress(uint64_t current, uint64_t total, time_t start_time) { time_t now = time(NULL); double elapsed = difftime(now, start_time); if (elapsed < 1.0) elapsed = 1.0; // Avoid division by zero double percentage = (double)current / total * 100.0; double speed = (double)current / elapsed / (1024.0 * 1024.0); // MB/s uint64_t remaining_bytes = total - current; double eta = remaining_bytes / (current / elapsed); printf("\rProgress: %.1f%% (%.1f MB/s, ETA: %.0fs) ", percentage, speed, eta); fflush(stdout); } const char* get_files_directory(void) { struct stat st = {0}; if (stat(FILES_DIR, &st) == 0 && S_ISDIR(st.st_mode)) { return FILES_DIR; } return "."; // Fall back to current directory } void get_default_file_path(const char* filename, char* result_path, size_t result_size) { const char* files_dir = get_files_directory(); // If filename already has a path (contains '/'), use it as-is if (strchr(filename, '/') != NULL) { strncpy(result_path, filename, result_size - 1); result_path[result_size - 1] = '\0'; return; } // Otherwise, prepend the files directory snprintf(result_path, result_size, "%s/%s", files_dir, filename); } char* get_preferred_editor(void) { // Check EDITOR environment variable first char* editor = getenv("EDITOR"); if (editor && strlen(editor) > 0) { // Verify the editor exists char command[512]; snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", editor); if (system(command) == 0) { return strdup(editor); } } // Check VISUAL environment variable editor = getenv("VISUAL"); if (editor && strlen(editor) > 0) { char command[512]; snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", editor); if (system(command) == 0) { return strdup(editor); } } // Try common editors in order of preference const char* common_editors[] = {"vim", "vi", "nano", "emacs", "gedit", NULL}; for (int i = 0; common_editors[i] != NULL; i++) { char command[512]; snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", common_editors[i]); if (system(command) == 0) { return strdup(common_editors[i]); } } return NULL; // No editor found } int launch_text_editor(const char* initial_content, char* result_buffer, size_t buffer_size) { char* editor = get_preferred_editor(); if (!editor) { printf("Error: No text editor found. Set EDITOR environment variable or install vim/nano.\n"); return 1; } // Create temporary file char temp_filename[64]; snprintf(temp_filename, sizeof(temp_filename), "/tmp/otp_edit_%ld.tmp", time(NULL)); // Write initial content to temp file if provided if (initial_content && strlen(initial_content) > 0) { FILE* temp_file = fopen(temp_filename, "w"); if (temp_file) { fputs(initial_content, temp_file); fclose(temp_file); } } else { // Create empty temp file FILE* temp_file = fopen(temp_filename, "w"); if (temp_file) { fclose(temp_file); } } // Launch editor printf("Opening %s for text editing...\n", editor); char command[512]; snprintf(command, sizeof(command), "%s %s", editor, temp_filename); int result = system(command); if (result == 0) { // Read the edited content back FILE* temp_file = fopen(temp_filename, "r"); if (temp_file) { size_t bytes_read = fread(result_buffer, 1, buffer_size - 1, temp_file); result_buffer[bytes_read] = '\0'; // Remove trailing newline if present if (bytes_read > 0 && result_buffer[bytes_read - 1] == '\n') { result_buffer[bytes_read - 1] = '\0'; } fclose(temp_file); } else { printf("Error: Cannot read edited content\n"); free(editor); unlink(temp_filename); return 1; } } else { printf("Editor exited with error or was cancelled\n"); free(editor); unlink(temp_filename); return 1; } // Clean up unlink(temp_filename); free(editor); return 0; } char* get_preferred_file_manager(void) { // Try file managers in order of preference const char* file_managers[] = {"ranger", "fzf", "nnn", "lf", NULL}; for (int i = 0; file_managers[i] != NULL; i++) { char command[512]; snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", file_managers[i]); if (system(command) == 0) { return strdup(file_managers[i]); } } return NULL; // No file manager found } int launch_file_manager(const char* start_directory, char* selected_file, size_t buffer_size) { char* fm = get_preferred_file_manager(); if (!fm) { printf("No file manager found. Please install ranger, fzf, nnn, or lf.\n"); printf("Falling back to manual file path entry.\n"); return 1; // Fall back to manual entry } char temp_filename[64]; snprintf(temp_filename, sizeof(temp_filename), "/tmp/otp_file_%ld.tmp", time(NULL)); char command[512]; int result = 1; printf("Opening %s for file selection...\n", fm); if (strcmp(fm, "ranger") == 0) { snprintf(command, sizeof(command), "cd '%s' && ranger --choosefile=%s", start_directory ? start_directory : ".", temp_filename); } else if (strcmp(fm, "fzf") == 0) { snprintf(command, sizeof(command), "cd '%s' && find . -type f | fzf > %s", start_directory ? start_directory : ".", temp_filename); } else if (strcmp(fm, "nnn") == 0) { snprintf(command, sizeof(command), "cd '%s' && nnn -p %s", start_directory ? start_directory : ".", temp_filename); } else if (strcmp(fm, "lf") == 0) { snprintf(command, sizeof(command), "cd '%s' && lf -selection-path=%s", start_directory ? start_directory : ".", temp_filename); } result = system(command); if (result == 0 || result == 256) { // Some file managers return 256 on success // Read selected file from temp file FILE* temp_file = fopen(temp_filename, "r"); if (temp_file) { if (fgets(selected_file, buffer_size, temp_file)) { // Remove trailing newline selected_file[strcspn(selected_file, "\n\r")] = 0; // For relative paths from fzf, make absolute if needed if (selected_file[0] == '.' && selected_file[1] == '/') { char current_dir[512]; if (getcwd(current_dir, sizeof(current_dir))) { char abs_path[1024]; snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, selected_file + 2); strncpy(selected_file, abs_path, buffer_size - 1); selected_file[buffer_size - 1] = '\0'; } } fclose(temp_file); unlink(temp_filename); free(fm); return 0; // Success } fclose(temp_file); } } // Clean up and indicate failure unlink(temp_filename); free(fm); return 1; // Fall back to manual entry } // Stdin detection functions implementation int has_stdin_data(void) { // Check if stdin is a pipe/redirect (not a terminal) if (!isatty(STDIN_FILENO)) { return 1; } return 0; } char* read_stdin_text(void) { size_t capacity = 4096; size_t length = 0; char* buffer = malloc(capacity); if (!buffer) { return NULL; } char chunk[1024]; while (fgets(chunk, sizeof(chunk), stdin)) { size_t chunk_len = strlen(chunk); // Ensure we have enough capacity while (length + chunk_len >= capacity) { capacity *= 2; char* new_buffer = realloc(buffer, capacity); if (!new_buffer) { free(buffer); return NULL; } buffer = new_buffer; } strcpy(buffer + length, chunk); length += chunk_len; } // Remove trailing newline if present if (length > 0 && buffer[length - 1] == '\n') { buffer[length - 1] = '\0'; length--; } // If empty, free and return NULL if (length == 0) { free(buffer); return NULL; } return buffer; } int pipe_mode(int argc, char* argv[], const char* piped_text) { (void)argc; // Suppress unused parameter warning (void)argv; // Suppress unused parameter warning // Check if we have a default pad configured char* default_pad = get_default_pad_path(); if (default_pad) { // Verify the default pad exists and extract checksum if (access(default_pad, R_OK) == 0) { // Extract checksum from pad filename char* filename = strrchr(default_pad, '/'); if (!filename) filename = default_pad; else filename++; // Skip the '/' // Extract checksum (remove .pad extension) if (strlen(filename) >= 68 && strstr(filename, ".pad")) { char pad_checksum[65]; strncpy(pad_checksum, filename, 64); pad_checksum[64] = '\0'; free(default_pad); // Encrypt using the default pad (silent mode) return encrypt_text(pad_checksum, piped_text); } } fprintf(stderr, "Error: Default pad not found or invalid: %s\n", default_pad); free(default_pad); return 1; } fprintf(stderr, "Error: No default pad configured for pipe mode\n"); fprintf(stderr, "Configure a default pad in ~/.otp/otp.conf\n"); return 1; } // Preferences management functions implementation int load_preferences(void) { char* home_dir = getenv("HOME"); if (!home_dir) { return 1; // No home directory } char preferences_dir[1024]; char preferences_file[2048]; // Increased buffer size to accommodate longer paths snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir); snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir); FILE* file = fopen(preferences_file, "r"); if (!file) { // No preferences file exists - create it and set first pad as default // Create .otp directory if it doesn't exist struct stat st = {0}; if (stat(preferences_dir, &st) == -1) { if (mkdir(preferences_dir, 0755) != 0) { return 1; } } // Find the first available pad to set as default DIR* dir = opendir(get_current_pads_dir()); if (dir) { struct dirent* entry; char first_pad_path[1024]; int found_pad = 0; while ((entry = readdir(dir)) != NULL && !found_pad) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { // Found a pad file - construct full absolute path const char* pads_dir = get_current_pads_dir(); if (pads_dir[0] == '/') { // Already absolute path int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", pads_dir, entry->d_name); if (ret >= (int)sizeof(first_pad_path)) { // Path was truncated, skip this entry continue; } } else { // Relative path - make it absolute char current_dir[512]; if (getcwd(current_dir, sizeof(current_dir))) { int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s/%s", current_dir, pads_dir, entry->d_name); if (ret >= (int)sizeof(first_pad_path)) { // Path was truncated, skip this entry continue; } } else { // Fallback to relative path int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", pads_dir, entry->d_name); if (ret >= (int)sizeof(first_pad_path)) { // Path was truncated, skip this entry continue; } } } strncpy(default_pad_path, first_pad_path, sizeof(default_pad_path) - 1); default_pad_path[sizeof(default_pad_path) - 1] = '\0'; found_pad = 1; } } closedir(dir); // Create the preferences file with the default pad if (found_pad) { save_preferences(); } } return 0; // Successfully initialized } char line[1024]; while (fgets(line, sizeof(line), file)) { // Remove newline line[strcspn(line, "\n")] = 0; // Skip empty lines and comments if (strlen(line) == 0 || line[0] == '#') { continue; } // Parse key=value pairs char* equals = strchr(line, '='); if (equals) { *equals = '\0'; char* key = line; char* value = equals + 1; // Trim whitespace while (*key == ' ' || *key == '\t') key++; while (*value == ' ' || *value == '\t') value++; if (strcmp(key, "default_pad") == 0) { strncpy(default_pad_path, value, sizeof(default_pad_path) - 1); default_pad_path[sizeof(default_pad_path) - 1] = '\0'; } } } fclose(file); return 0; } char* find_pad_by_prefix(const char* prefix) { DIR* dir = opendir(get_current_pads_dir()); if (!dir) { printf("Error: Cannot open pads directory %s\n", get_current_pads_dir()); return NULL; } struct dirent* entry; char* matches[100]; // Store up to 100 matches int match_count = 0; // Always try hex prefix matching first size_t prefix_len = strlen(prefix); while ((entry = readdir(dir)) != NULL && match_count < 100) { // Skip . and .. entries, and only process .pad files if (entry->d_name[0] == '.') continue; if (!strstr(entry->d_name, ".pad")) continue; if (strlen(entry->d_name) != 68) continue; // 64 char chksum + ".pad" // Compare prefix with the filename (checksum part) if (strncmp(entry->d_name, prefix, prefix_len) == 0) { matches[match_count] = malloc(65); strncpy(matches[match_count], entry->d_name, 64); matches[match_count][64] = '\0'; match_count++; } } // Only prefix matching is supported closedir(dir); if (match_count == 0) { printf("No pads found matching '%s'\n", prefix); printf("Available pads:\n"); char* selected = select_pad_interactive("Available pads:", "Available pads (press Enter to continue)", PAD_FILTER_ALL, 0); if (selected) { free(selected); } return NULL; } else if (match_count == 1) { char* result = matches[0]; return result; } else { printf("Multiple matches found for '%s':\n", prefix); for (int i = 0; i < match_count; i++) { printf(" %.16s...\n", matches[i]); } printf("Please be more specific with your prefix.\n"); // Free all matches and return NULL for (int i = 0; i < match_count; i++) { free(matches[i]); } return NULL; } } uint64_t parse_size_string(const char* size_str) { if (!size_str) return 0; char* endptr; double value = strtod(size_str, &endptr); if (value <= 0) return 0; // Skip whitespace while (*endptr && isspace(*endptr)) endptr++; uint64_t multiplier = 1; if (*endptr) { char unit[4]; strncpy(unit, endptr, 3); unit[3] = '\0'; // Convert to uppercase for (int i = 0; unit[i]; i++) { unit[i] = toupper(unit[i]); } if (strcmp(unit, "K") == 0 || strcmp(unit, "KB") == 0) { multiplier = 1024ULL; } else if (strcmp(unit, "M") == 0 || strcmp(unit, "MB") == 0) { multiplier = 1024ULL * 1024ULL; } else if (strcmp(unit, "G") == 0 || strcmp(unit, "GB") == 0) { multiplier = 1024ULL * 1024ULL * 1024ULL; } else if (strcmp(unit, "T") == 0 || strcmp(unit, "TB") == 0) { multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL; } else { return 0; // Invalid unit } } else { // No unit specified, treat as bytes multiplier = 1; } return (uint64_t)(value * multiplier); } void get_directory_display(const char* file_path, char* result, size_t result_size) { // Extract directory path from full file path char dir_path[512]; char* last_slash = strrchr(file_path, '/'); if (last_slash) { size_t dir_len = last_slash - file_path; if (dir_len >= sizeof(dir_path)) { dir_len = sizeof(dir_path) - 1; } strncpy(dir_path, file_path, dir_len); dir_path[dir_len] = '\0'; } else { // No directory separator, assume current directory strcpy(dir_path, "."); } // USB Drive Detection and Smart Shortening char* home_dir = getenv("HOME"); // Check for USB/removable media mount patterns if (strstr(dir_path, "/media/") || strstr(dir_path, "/run/media/") || strstr(dir_path, "/mnt/")) { // Extract USB label/name char* media_start = NULL; if (strstr(dir_path, "/media/")) { media_start = strstr(dir_path, "/media/"); } else if (strstr(dir_path, "/run/media/")) { media_start = strstr(dir_path, "/run/media/"); } else if (strstr(dir_path, "/mnt/")) { media_start = strstr(dir_path, "/mnt/"); } if (media_start) { // Find the USB label part char* path_after_media = strchr(media_start + 1, '/'); if (path_after_media) { path_after_media++; // Skip the slash // For /media/user/LABEL pattern, skip the username to get to the drive label if (strstr(media_start, "/media/")) { char* next_slash = strchr(path_after_media, '/'); if (next_slash) { path_after_media = next_slash + 1; } } // For /run/media/user/LABEL pattern, skip the username else if (strstr(media_start, "/run/media/")) { char* next_slash = strchr(path_after_media, '/'); if (next_slash) { path_after_media = next_slash + 1; } } // Extract just the USB label (up to next slash or end) char* label_end = strchr(path_after_media, '/'); char usb_label[32]; if (label_end) { size_t label_len = label_end - path_after_media; if (label_len > sizeof(usb_label) - 1) label_len = sizeof(usb_label) - 1; strncpy(usb_label, path_after_media, label_len); usb_label[label_len] = '\0'; } else { // USB label is the last part strncpy(usb_label, path_after_media, sizeof(usb_label) - 1); usb_label[sizeof(usb_label) - 1] = '\0'; } // Format with USB: prefix, limiting total length to fit in result snprintf(result, result_size, "USB:%s", usb_label); // Truncate if too long if (strlen(result) > 11) { result[11] = '\0'; } return; } } } // Home directory shortening if (home_dir && strncmp(dir_path, home_dir, strlen(home_dir)) == 0) { if (dir_path[strlen(home_dir)] == '/' || dir_path[strlen(home_dir)] == '\0') { // Replace home directory with ~ char temp[512]; snprintf(temp, sizeof(temp), "~%s", dir_path + strlen(home_dir)); // If result is too long, truncate intelligently if (strlen(temp) > 11) { // Show ~/...end_part char* last_part = strrchr(temp, '/'); if (last_part && strlen(last_part) < 8) { snprintf(result, result_size, "~...%s", last_part); } else { strncpy(result, temp, 11); result[11] = '\0'; } } else { strncpy(result, temp, result_size - 1); result[result_size - 1] = '\0'; } return; } } // Current working directory if (strcmp(dir_path, ".") == 0 || strcmp(dir_path, get_current_pads_dir()) == 0) { strncpy(result, "pads", result_size - 1); result[result_size - 1] = '\0'; return; } // System/other paths - smart truncation with ellipsis if (strlen(dir_path) > 11) { // Try to show the most meaningful part char* last_part = strrchr(dir_path, '/'); if (last_part && strlen(last_part) < 9) { // Show .../last_part snprintf(result, result_size, "...%s", last_part); } else { // Show first part with ellipsis strncpy(result, dir_path, 8); strncpy(result + 8, "...", result_size - 8 - 1); result[result_size - 1] = '\0'; } } else { // Short enough, use as-is strncpy(result, dir_path, result_size - 1); result[result_size - 1] = '\0'; } } int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size) { // Find the last directory separator char* last_slash = strrchr(default_path, '/'); char directory[1024] = ""; char filename[512] = ""; if (last_slash) { // Extract directory path size_t dir_len = last_slash - default_path + 1; // Include the trailing slash if (dir_len < sizeof(directory)) { strncpy(directory, default_path, dir_len); directory[dir_len] = '\0'; } // Extract filename strncpy(filename, last_slash + 1, sizeof(filename) - 1); filename[sizeof(filename) - 1] = '\0'; } else { // No directory separator, treat as filename only strncpy(filename, default_path, sizeof(filename) - 1); filename[sizeof(filename) - 1] = '\0'; } // Setup terminal for raw input struct termios orig_termios; if (tcgetattr(STDIN_FILENO, &orig_termios) != 0) { // Fallback to simple input if terminal control fails printf("\n%s\n%s: ", prompt, directory); fflush(stdout); char input_buffer[512]; if (!fgets(input_buffer, sizeof(input_buffer), stdin)) { return 1; } input_buffer[strcspn(input_buffer, "\n")] = 0; if (strlen(input_buffer) == 0) { strncpy(result, default_path, result_size - 1); } else { if (strlen(directory) > 0) { snprintf(result, result_size, "%s%s", directory, input_buffer); } else { strncpy(result, input_buffer, result_size - 1); } } result[result_size - 1] = '\0'; return 0; } // Set up raw terminal mode struct termios raw_termios = orig_termios; raw_termios.c_lflag &= ~(ECHO | ICANON); raw_termios.c_cc[VMIN] = 1; raw_termios.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios) != 0) { // Fallback if terminal setup fails printf("\n%s\n%s: ", prompt, directory); fflush(stdout); char input_buffer[512]; if (!fgets(input_buffer, sizeof(input_buffer), stdin)) { return 1; } input_buffer[strcspn(input_buffer, "\n")] = 0; if (strlen(input_buffer) == 0) { strncpy(result, default_path, result_size - 1); } else { if (strlen(directory) > 0) { snprintf(result, result_size, "%s%s", directory, input_buffer); } else { strncpy(result, input_buffer, result_size - 1); } } result[result_size - 1] = '\0'; return 0; } // Display prompt and directory printf("\n%s\n%s", prompt, directory); fflush(stdout); // Initialize editing buffer with default filename char edit_buffer[512]; strncpy(edit_buffer, filename, sizeof(edit_buffer) - 1); edit_buffer[sizeof(edit_buffer) - 1] = '\0'; int cursor_pos = strlen(edit_buffer); int buffer_len = cursor_pos; // Display initial filename printf("%s", edit_buffer); fflush(stdout); // Main editing loop int c; while ((c = getchar()) != EOF) { if (c == '\n' || c == '\r') { // Enter key - accept input break; } else if (c == 127 || c == 8) { // Backspace/Delete if (cursor_pos > 0) { // Move everything after cursor one position left memmove(&edit_buffer[cursor_pos - 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1); cursor_pos--; buffer_len--; // Redraw line: move cursor to start of filename area, clear to end, redraw buffer printf("\r%s\033[K%s", directory, edit_buffer); // Position cursor correctly if (cursor_pos < buffer_len) { printf("\033[%dD", buffer_len - cursor_pos); } fflush(stdout); } } else if (c == 27) { // Escape sequence (arrow keys, etc.) int c1 = getchar(); int c2 = getchar(); if (c1 == '[') { if (c2 == 'C' && cursor_pos < buffer_len) { // Right arrow cursor_pos++; printf("\033[1C"); fflush(stdout); } else if (c2 == 'D' && cursor_pos > 0) { // Left arrow cursor_pos--; printf("\033[1D"); fflush(stdout); } else if (c2 == 'H') { // Home key printf("\033[%dD", cursor_pos); cursor_pos = 0; fflush(stdout); } else if (c2 == 'F') { // End key printf("\033[%dC", buffer_len - cursor_pos); cursor_pos = buffer_len; fflush(stdout); } } } else if (c >= 32 && c <= 126) { // Printable character if (buffer_len < (int)sizeof(edit_buffer) - 1) { // Move everything after cursor one position right memmove(&edit_buffer[cursor_pos + 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1); edit_buffer[cursor_pos] = c; cursor_pos++; buffer_len++; // Redraw from cursor position printf("%c", c); if (cursor_pos < buffer_len) { // Print remaining characters and move cursor back printf("%s\033[%dD", &edit_buffer[cursor_pos], buffer_len - cursor_pos); } fflush(stdout); } } // Ignore other control characters } // Restore terminal tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); printf("\n"); // Construct final result if (buffer_len == 0) { // Empty input, use default strncpy(result, default_path, result_size - 1); } else { // Combine directory with edited filename edit_buffer[buffer_len] = '\0'; if (strlen(directory) > 0) { snprintf(result, result_size, "%s%s", directory, edit_buffer); } else { strncpy(result, edit_buffer, result_size - 1); } } result[result_size - 1] = '\0'; return 0; } char* get_preference(const char* key) { if (strcmp(key, "default_pad") == 0) { if (strlen(default_pad_path) > 0) { return strdup(default_pad_path); } } return NULL; } int set_preference(const char* key, const char* value) { if (strcmp(key, "default_pad") == 0) { if (value) { strncpy(default_pad_path, value, sizeof(default_pad_path) - 1); default_pad_path[sizeof(default_pad_path) - 1] = '\0'; } else { default_pad_path[0] = '\0'; } return save_preferences(); } return 1; } char* get_default_pad_path(void) { if (strlen(default_pad_path) > 0) { return strdup(default_pad_path); } return NULL; } int set_default_pad_path(const char* pad_path) { if (!pad_path) { return set_preference("default_pad", NULL); } // Ensure we store the full absolute path char absolute_path[1024]; if (pad_path[0] == '/') { // Already absolute path strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1); absolute_path[sizeof(absolute_path) - 1] = '\0'; } else { // Relative path - make it absolute char current_dir[512]; if (getcwd(current_dir, sizeof(current_dir))) { snprintf(absolute_path, sizeof(absolute_path), "%s/%s", current_dir, pad_path); } else { // Fallback to using the path as-is if getcwd fails strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1); absolute_path[sizeof(absolute_path) - 1] = '\0'; } } return set_preference("default_pad", absolute_path); } // OTP thumb drive detection function implementation int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) { const char* mount_dirs[] = {"/media", "/run/media", "/mnt", NULL}; for (int mount_idx = 0; mount_dirs[mount_idx] != NULL; mount_idx++) { DIR* mount_dir = opendir(mount_dirs[mount_idx]); if (!mount_dir) continue; struct dirent* mount_entry; while ((mount_entry = readdir(mount_dir)) != NULL) { if (mount_entry->d_name[0] == '.') continue; char mount_path[1024]; // Increased buffer size snprintf(mount_path, sizeof(mount_path), "%s/%s", mount_dirs[mount_idx], mount_entry->d_name); // For /media, we need to go one level deeper (user directories) if (strcmp(mount_dirs[mount_idx], "/media") == 0) { // This is /media/[username] - look inside for drives DIR* user_dir = opendir(mount_path); if (!user_dir) continue; struct dirent* user_entry; while ((user_entry = readdir(user_dir)) != NULL) { if (user_entry->d_name[0] == '.') continue; // Check if drive name starts with "OTP" if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; char user_mount_path[2048]; // Increased buffer size // Verify buffer has enough space before concatenation size_t mount_len = strlen(mount_path); size_t entry_len = strlen(user_entry->d_name); if (mount_len + entry_len + 2 < sizeof(user_mount_path)) { snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name); // Check if this is a readable directory DIR* drive_dir = opendir(user_mount_path); if (drive_dir) { closedir(drive_dir); strncpy(otp_drive_path, user_mount_path, path_size - 1); otp_drive_path[path_size - 1] = '\0'; closedir(user_dir); closedir(mount_dir); return 1; // Found OTP drive } } } closedir(user_dir); } else if (strcmp(mount_dirs[mount_idx], "/run/media") == 0) { // For /run/media, we need to go one level deeper (skip username) DIR* user_dir = opendir(mount_path); if (!user_dir) continue; struct dirent* user_entry; while ((user_entry = readdir(user_dir)) != NULL) { if (user_entry->d_name[0] == '.') continue; // Check if drive name starts with "OTP" if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; char user_mount_path[2048]; // Increased buffer size snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name); // Check if this is a readable directory DIR* drive_dir = opendir(user_mount_path); if (drive_dir) { closedir(drive_dir); strncpy(otp_drive_path, user_mount_path, path_size - 1); otp_drive_path[path_size - 1] = '\0'; closedir(user_dir); closedir(mount_dir); return 1; // Found OTP drive } } closedir(user_dir); } else { // Direct mount point (like /mnt/OTP_DRIVE) // Check if drive name starts with "OTP" if (strncmp(mount_entry->d_name, "OTP", 3) == 0) { DIR* drive_dir = opendir(mount_path); if (drive_dir) { closedir(drive_dir); strncpy(otp_drive_path, mount_path, path_size - 1); otp_drive_path[path_size - 1] = '\0'; closedir(mount_dir); return 1; // Found OTP drive } } } } closedir(mount_dir); } return 0; // No OTP drive found } int save_preferences(void) { char* home_dir = getenv("HOME"); if (!home_dir) { return 1; } char preferences_dir[1024]; char preferences_file[2048]; // Increased buffer size to accommodate longer paths snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir); snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir); // Create .otp directory if it doesn't exist struct stat st = {0}; if (stat(preferences_dir, &st) == -1) { if (mkdir(preferences_dir, 0755) != 0) { return 1; } } FILE* file = fopen(preferences_file, "w"); if (!file) { return 1; } fprintf(file, "# OTP Preferences File\n"); fprintf(file, "# This file is automatically generated and updated by the OTP program\n\n"); if (strlen(default_pad_path) > 0) { fprintf(file, "default_pad=%s\n", default_pad_path); } fclose(file); return 0; }