diff --git a/.clinerules/workspace_rules.md b/.clinerules/workspace_rules.md index d84c98c..c9de372 100644 --- a/.clinerules/workspace_rules.md +++ b/.clinerules/workspace_rules.md @@ -1,4 +1,7 @@ When building, use build.sh, not make. -Use it as follows: build.sh -m "useful comment on changes being made" \ No newline at end of file +Use it as follows: build.sh -m "useful comment on changes being made" + +When making TUI menus, try to use the first leter of the command and the key to press to execute that command. For example, if the command is "Open file" try to use a keypress of "o" upper or lower case to signal to open the file. Use this instead of number keyed menus when possible. In the command, the letter should be underlined that signifies the command. + diff --git a/otp.c b/otp.c index 7cfffc5..d082165 100644 --- a/otp.c +++ b/otp.c @@ -47,6 +47,12 @@ int main(int argc, char* argv[]); int interactive_mode(void); int command_line_mode(int argc, char* argv[]); +// Editor and file manager functions +char* get_preferred_editor(void); +char* get_preferred_file_manager(void); +int launch_text_editor(const char* initial_content, char* result_buffer, size_t buffer_size); +int launch_file_manager(const char* start_directory, char* selected_file, size_t buffer_size); + // Core functions int generate_pad(uint64_t size_bytes, int show_progress); int generate_pad_with_entropy(uint64_t size_bytes, int show_progress, int use_keyboard_entropy); @@ -329,25 +335,92 @@ int handle_encrypt_menu(void) { if (choice == 1) { // Text encryption - printf("\nEnter pad selection (number, chksum, or prefix): "); - char input[MAX_HASH_LENGTH]; - if (!fgets(input, sizeof(input), stdin)) { + printf("\nPad selection options:\n"); + printf("1. Select from numbered list\n"); + printf("2. Enter checksum/prefix manually\n"); + printf("3. Browse pad files\n"); + printf("Enter choice (1-3): "); + + char pad_choice[10]; + if (!fgets(pad_choice, sizeof(pad_choice), stdin)) { printf("Error: Failed to read input\n"); return 1; } - input[strcspn(input, "\n")] = 0; + char input[MAX_HASH_LENGTH]; + if (atoi(pad_choice) == 3) { + // Use file manager to browse pads directory + char selected_pad[512]; + if (launch_file_manager("pads", selected_pad, sizeof(selected_pad)) == 0) { + // Extract checksum from selected .pad file + char* filename = strrchr(selected_pad, '/'); + if (!filename) filename = selected_pad; + else filename++; // Skip the '/' + + // Remove .pad extension to get checksum + if (strlen(filename) == 68 && strstr(filename, ".pad")) { + strncpy(input, filename, 64); + input[64] = '\0'; + } else { + printf("Invalid pad file selected.\n"); + return 1; + } + } else { + printf("Falling back to manual pad selection.\n"); + printf("Enter pad selection (number, chksum, or prefix): "); + if (!fgets(input, sizeof(input), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + input[strcspn(input, "\n")] = 0; + } + } else { + // Manual pad selection + printf("Enter pad selection (number, chksum, or prefix): "); + if (!fgets(input, sizeof(input), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + input[strcspn(input, "\n")] = 0; + } + return encrypt_text(input, NULL); // NULL for interactive mode } else if (choice == 2) { // File encryption - printf("\nEnter input file path: "); + printf("\nFile selection options:\n"); + printf("1. Type file path directly\n"); + printf("2. Use file manager\n"); + printf("Enter choice (1-2): "); + + char file_choice[10]; char input_file[512]; - if (!fgets(input_file, sizeof(input_file), stdin)) { + + if (!fgets(file_choice, sizeof(file_choice), stdin)) { printf("Error: Failed to read input\n"); return 1; } - input_file[strcspn(input_file, "\n")] = 0; + + if (atoi(file_choice) == 2) { + // Use file manager + if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) { + printf("Falling back to manual file path entry.\n"); + printf("Enter input file path: "); + if (!fgets(input_file, sizeof(input_file), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + input_file[strcspn(input_file, "\n")] = 0; + } + } else { + // Direct file path input + printf("Enter input file path: "); + if (!fgets(input_file, sizeof(input_file), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + input_file[strcspn(input_file, "\n")] = 0; + } // Check if file exists if (access(input_file, R_OK) != 0) { @@ -1076,19 +1149,53 @@ int encrypt_text(const char* pad_identifier, const char* input_text) { text_buffer[sizeof(text_buffer) - 1] = '\0'; } else { // Get input text from user (interactive mode) - printf("Enter text to encrypt: "); - fflush(stdout); + printf("\nText input options:\n"); + printf("1. Type text directly\n"); + printf("2. Use text editor\n"); + printf("Enter choice (1-2): "); - if (fgets(text_buffer, sizeof(text_buffer), stdin) == NULL) { + char input_choice[10]; + if (!fgets(input_choice, sizeof(input_choice), stdin)) { printf("Error: Failed to read input\n"); free(pad_chksum); return 1; } - // Remove newline if present - size_t len = strlen(text_buffer); - if (len > 0 && text_buffer[len - 1] == '\n') { - text_buffer[len - 1] = '\0'; + if (atoi(input_choice) == 2) { + // Use text editor + if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) { + printf("Falling back to direct text input.\n"); + printf("Enter text to encrypt: "); + fflush(stdout); + + if (fgets(text_buffer, sizeof(text_buffer), stdin) == NULL) { + printf("Error: Failed to read input\n"); + free(pad_chksum); + return 1; + } + + // Remove newline if present + size_t len = strlen(text_buffer); + if (len > 0 && text_buffer[len - 1] == '\n') { + text_buffer[len - 1] = '\0'; + } + } + } else { + // Direct text input + printf("Enter text to encrypt: "); + fflush(stdout); + + if (fgets(text_buffer, sizeof(text_buffer), stdin) == NULL) { + printf("Error: Failed to read input\n"); + free(pad_chksum); + return 1; + } + + // Remove newline if present + size_t len = strlen(text_buffer); + if (len > 0 && text_buffer[len - 1] == '\n') { + text_buffer[len - 1] = '\0'; + } } } @@ -2182,6 +2289,190 @@ void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size, } } +// Editor and file manager implementations + +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 +} + +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_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; +} + +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 +} + void print_usage(const char* program_name) { printf("OTP Cipher - One Time Pad Implementation %s\n", get_version()); printf("%s\n", get_build_info()); diff --git a/otp.code-workspace b/otp.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/otp.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/test.txt.otp b/test.txt.otp deleted file mode 100644 index 0f8f732..0000000 Binary files a/test.txt.otp and /dev/null differ