Files
otp/src/ui.c

503 lines
17 KiB
C

#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <time.h>
#include <ctype.h>
#include <termios.h>
#include <fcntl.h>
#include <math.h>
#include "otp.h"
// Initialize terminal dimensions
void init_terminal_dimensions(void) {
struct winsize ws;
// Try to get actual terminal size
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0 && ws.ws_row > 0) {
set_terminal_dimensions(ws.ws_col, ws.ws_row);
}
// If ioctl fails, keep the default values (80x24)
}
// Print centered header with = padding, screen clearing, and optional pause
void print_centered_header(const char* text, int pause_before_clear) {
if (!text) return;
// Phase 1: Pause if requested
if (pause_before_clear) {
printf("\nPress Enter to continue...");
fflush(stdout);
// Wait for Enter key
int c;
while ((c = getchar()) != '\n' && c != EOF) {
// Consume any extra characters until newline
}
}
// Phase 2: Clear screen using terminal height
for (int i = 0; i < get_terminal_height(); i++) {
printf("\n");
}
// Phase 3: Display centered header (existing logic)
int text_len = strlen(text);
int available_width = get_terminal_width();
// Ensure minimum spacing: at least 1 space on each side
int min_required = text_len + 4; // text + " " + text + " " (spaces around text)
if (available_width < min_required) {
// Terminal too narrow - just print the text with minimal formatting
printf("=== %s ===\n", text);
return;
}
// Calculate padding
int total_padding = available_width - text_len - 2; // -2 for spaces around text
int left_padding = total_padding / 2;
int right_padding = total_padding - left_padding;
// Print the header
for (int i = 0; i < left_padding; i++) {
printf("=");
}
printf(" %s ", text);
for (int i = 0; i < right_padding; i++) {
printf("=");
}
printf("\n");
}
// Interactive mode main loop
int interactive_mode(void) {
char input[10];
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
while (1) {
show_main_menu();
if (!fgets(input, sizeof(input), stdin)) {
printf("Goodbye!\n");
break;
}
char choice = toupper(input[0]);
switch (choice) {
case 'T':
handle_text_encrypt();
break;
case 'F':
handle_file_encrypt();
break;
case 'D':
handle_decrypt_menu();
break;
case 'P':
handle_pads_menu();
break;
case 'X':
case 'Q':
printf("Goodbye!\n");
return 0;
default:
printf("Invalid choice. Please try again.\n");
break;
}
}
return 0;
}
void show_main_menu(void) {
printf("\n");
print_centered_header("Main Menu - OTP v0.3.16", 0);
printf("\n");
printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT
printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT
printf(" \033[4mD\033[0mecrypt\n"); //DECRYPT
printf(" \033[4mP\033[0mads\n"); //PADS
printf(" E\033[4mx\033[0mit\n"); //EXIT
printf("\nSelect option: ");
}
int handle_generate_menu(void) {
printf("\n");
print_centered_header("Generate New Pad", 0);
printf("Enter pad size (examples: 1GB, 5TB, 512MB, 2048): ");
char size_input[64];
if (!fgets(size_input, sizeof(size_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
size_input[strcspn(size_input, "\n")] = 0;
uint64_t size = parse_size_string(size_input);
if (size == 0) {
printf("Error: Invalid size format\n");
return 1;
}
double size_gb = (double)size / (1024.0 * 1024.0 * 1024.0);
printf("Generating %.2f GB pad...\n", size_gb);
printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n");
return generate_pad(size, 1);
}
int handle_encrypt_menu(void) {
printf("\n");
print_centered_header("Encrypt Data", 0);
printf("Available pads:\n");
char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to continue)", PAD_FILTER_ALL, 0);
int pad_count = 1; // Assume at least 1 pad if function returned
if (selected) {
free(selected);
} else {
pad_count = 0;
}
if (pad_count == 0) {
printf("No pads available. Generate a pad first.\n");
return 1;
}
// Ask user to choose between text and file encryption
printf("\nSelect encryption type:\n");
printf(" 1. Text message\n");
printf(" 2. File\n");
printf("Enter choice (1-2): ");
char choice_input[10];
if (!fgets(choice_input, sizeof(choice_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
int choice = atoi(choice_input);
if (choice == 1) {
// Text encryption - use unified pad selection
char* selected_pad = select_pad_interactive("Select Pad for Text Encryption",
"Select pad (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("Text encryption cancelled.\n");
return 1;
}
int result = encrypt_text(selected_pad, NULL); // NULL for interactive mode
free(selected_pad);
return result;
}
else if (choice == 2) {
// File encryption
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(file_choice, sizeof(file_choice), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
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) {
printf("Error: File '%s' not found or cannot be read\n", input_file);
return 1;
}
// Use unified pad selection
char* selected_pad = select_pad_interactive("Select Pad for File Encryption",
"Select pad (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("File encryption cancelled.\n");
return 1;
}
// Ask for output format
printf("\nSelect output format:\n");
printf(" 1. Binary (.otp) - preserves file permissions\n");
printf(" 2. ASCII (.otp.asc) - text-safe format\n");
printf("Enter choice (1-2): ");
char format_input[10];
if (!fgets(format_input, sizeof(format_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
int ascii_armor = (atoi(format_input) == 2) ? 1 : 0;
// Generate default output filename with files directory and use enhanced input function
char default_output[1024]; // Increased size to prevent truncation warnings
char temp_default[1024];
// Generate base filename with appropriate extension
if (ascii_armor) {
snprintf(temp_default, sizeof(temp_default), "%s.otp.asc", input_file);
} else {
snprintf(temp_default, sizeof(temp_default), "%s.otp", input_file);
}
// Apply files directory default path
get_default_file_path(temp_default, default_output, sizeof(default_output));
char output_file[512];
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
printf("Error: Failed to read input\n");
return 1;
}
const char* output_filename = output_file;
int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor);
free(selected_pad);
return result;
}
else {
printf("Invalid choice. Please enter 1 or 2.\n");
return 1;
}
}
int handle_decrypt_menu(void) {
printf("\n");
print_centered_header("Smart Decrypt", 0);
printf("Enter encrypted data (paste ASCII armor), file path, or press Enter to browse files:\n");
char input_line[MAX_LINE_LENGTH];
if (!fgets(input_line, sizeof(input_line), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
// Remove newline
input_line[strcspn(input_line, "\n")] = 0;
if (strlen(input_line) == 0) {
// Empty input - launch file manager to browse for files
char selected_file[512];
if (launch_file_manager(get_files_directory(), selected_file, sizeof(selected_file)) != 0) {
printf("Error: Could not launch file manager\n");
return 1;
}
// Generate smart default output filename with files directory and use enhanced input function
char temp_default[512];
char default_output[512];
strncpy(temp_default, selected_file, sizeof(temp_default) - 1);
temp_default[sizeof(temp_default) - 1] = '\0';
// Remove common encrypted extensions to get a better default
if (strstr(temp_default, ".otp.asc")) {
// Replace .otp.asc with original extension or no extension
char* ext_pos = strstr(temp_default, ".otp.asc");
*ext_pos = '\0';
} else if (strstr(temp_default, ".otp")) {
// Replace .otp with original extension or no extension
char* ext_pos = strstr(temp_default, ".otp");
*ext_pos = '\0';
} else {
// No recognized encrypted extension, add .decrypted suffix
strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1);
}
// Apply files directory default path
get_default_file_path(temp_default, default_output, sizeof(default_output));
char output_file[512];
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
printf("Error: Failed to read input\n");
return 1;
}
return decrypt_file(selected_file, output_file);
}
else if (strncmp(input_line, "-----BEGIN OTP MESSAGE-----", 27) == 0) {
// Looks like ASCII armor - collect the full message
char full_message[MAX_INPUT_SIZE * 4] = {0};
strcat(full_message, input_line);
strcat(full_message, "\n");
printf("Continue pasting the message (end with -----END OTP MESSAGE-----):\n");
char line[MAX_LINE_LENGTH];
while (fgets(line, sizeof(line), stdin)) {
strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1);
if (strstr(line, "-----END OTP MESSAGE-----")) {
break;
}
}
return decrypt_text(NULL, full_message);
}
else {
// Check if it looks like a file path
if (access(input_line, R_OK) == 0) {
// It's a valid file - decrypt it with enhanced input for output filename
char temp_default[512];
char default_output[512];
strncpy(temp_default, input_line, sizeof(temp_default) - 1);
temp_default[sizeof(temp_default) - 1] = '\0';
// Remove common encrypted extensions to get a better default
if (strstr(temp_default, ".otp.asc")) {
// Replace .otp.asc with original extension or no extension
char* ext_pos = strstr(temp_default, ".otp.asc");
*ext_pos = '\0';
} else if (strstr(temp_default, ".otp")) {
// Replace .otp with original extension or no extension
char* ext_pos = strstr(temp_default, ".otp");
*ext_pos = '\0';
} else {
// No recognized encrypted extension, add .decrypted suffix
strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1);
}
// Apply files directory default path
get_default_file_path(temp_default, default_output, sizeof(default_output));
char output_file[512];
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
printf("Error: Failed to read input\n");
return 1;
}
return decrypt_file(input_line, output_file);
} else {
printf("Input not recognized as ASCII armor or valid file path.\n");
return 1;
}
}
}
int handle_text_encrypt(void) {
printf("\n");
print_centered_header("Text Encrypt", 0);
// Launch text editor directly
char text_buffer[MAX_INPUT_SIZE];
if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) {
printf("Error: Could not launch text editor\n");
return 1;
}
if (strlen(text_buffer) == 0) {
printf("No text entered - canceling encryption\n");
return 1;
}
// Use unified pad selection
char* selected_pad = select_pad_interactive("Select Pad for Text Encryption",
"Select pad (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("Text encryption cancelled.\n");
return 1;
}
int result = encrypt_text(selected_pad, text_buffer);
free(selected_pad);
return result;
}
int handle_file_encrypt(void) {
printf("\n");
print_centered_header("File Encrypt", 0);
// Launch file manager directly
char input_file[512];
if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) {
printf("Error: Could not launch file manager\n");
return 1;
}
// Check if file exists
if (access(input_file, R_OK) != 0) {
printf("Error: File '%s' not found or cannot be read\n", input_file);
return 1;
}
// Use unified pad selection
char* selected_pad = select_pad_interactive("Select Pad for File Encryption",
"Select pad (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("File encryption cancelled.\n");
return 1;
}
// Ask for output format
printf("\nSelect output format:\n");
printf(" 1. Binary (.otp) - preserves file permissions\n");
printf(" 2. ASCII (.otp.asc) - text-safe format\n");
printf("Enter choice (1-2): ");
char format_input[10];
if (!fgets(format_input, sizeof(format_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
int ascii_armor = (atoi(format_input) == 2) ? 1 : 0;
// Generate default output filename
char default_output[1024]; // Increased buffer size to prevent truncation warnings
if (ascii_armor) {
snprintf(default_output, sizeof(default_output), "%s.otp.asc", input_file);
} else {
snprintf(default_output, sizeof(default_output), "%s.otp", input_file);
}
// Use enhanced input function for output filename
char output_file[512];
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
printf("Error: Failed to read input\n");
return 1;
}
const char* output_filename = output_file;
int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor);
free(selected_pad);
return result;
}