From a9de7f44819d974e690613e4c3b5a13783116531 Mon Sep 17 00:00:00 2001
From: Storm Dragon <stormdragon2976@gmail.com>
Date: Thu, 10 Apr 2025 07:45:09 -0400
Subject: [PATCH] Moving mtoward using openssl to encrypt email address files.

---
 fleacollar.sh | 359 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 213 insertions(+), 146 deletions(-)

diff --git a/fleacollar.sh b/fleacollar.sh
index d7882d2..f67149d 100755
--- a/fleacollar.sh
+++ b/fleacollar.sh
@@ -4,14 +4,11 @@
 # Written by Storm Dragon: https://social.stormdragon.tk/storm
 # Written by Michael Taboada: https://2mb.social/mwtab
 # Contributions by Kyle: https://kyle.tk
-# Released under the terms of  the WTFPL: http://wtfpl.net
+# Released under the terms of the WTFPL: http://wtfpl.net
 
 # Settings to improve accessibility of dialog.
 export DIALOGOPTS='--insecure --no-lines --visit-items'
 
-# Use the cli entry system for gpg keys.
-export PINENTRY_USER_DATA="USE_CURSES=1"
-
 # Array of command line arguments
 declare -A command=(
     [h]="Show this help information."
@@ -20,7 +17,6 @@ declare -A command=(
 
 # Variables
 muttHome=~/.mutt
-fileDir="$(dirname "$(realpath "$0")")/files"
 testMode=false
 
 # Process command line arguments
@@ -37,6 +33,7 @@ while getopts "${args}" i ; do
     esac
 done
 
+# Functions
 help() {
     echo "fleacollar.sh"
     echo "Released under the terms of the WTFPL: http://wtfpl.net"
@@ -49,39 +46,14 @@ help() {
     exit 0
 }
 
-initialize_pass() {
-    # Check if password store is initialized
-    if [ ! -f ~/.password-store/.gpg-id ]; then
-        # Get the GPG key ID that we already have in the script
-        keyName="$(gpg2 --list-secret-keys --keyid-format short | grep -B1 ^uid | head -n1 | rev | cut -c -8 | rev)"
-        
-        # If we have a key, initialize the password store
-        if [ -n "$keyName" ]; then
-            echo "Initializing password store with GPG key: $keyName"
-            pass init "$keyName"
-        else
-            msgbox "No GPG key found. Please run 'pass init your-gpg-id' manually after creating a GPG key."
-            exit 1
-        fi
-    fi
-}
-
-check_dependancies()
-{
+check_dependancies() {
     local dep
-    for dep in dialog gpg2 mutt pass w3m ; do
+    for dep in dialog mutt w3m openssl ; do
        if ! command -v $dep &> /dev/null ; then
             echo "$dep is not installed. Please install $dep and run this script again."
             exit 1
          fi
     done
-    if ! [ -d ~/.gnupg ]; then
-        read -p "No configuration for GPG was found. To have Fleacollar configure this for you press enter. If you would like to configure GPG manually, press control+c. " continue
-        configure_gpg
-    fi
-    
-    # Initialize the password store if needed
-    initialize_pass
 }
 
 inputbox() {
@@ -105,9 +77,9 @@ menulist() {
 }
 
 msgbox() {
-# Returns: None
-# Shows the provided message on the screen with an ok button.
-dialog --msgbox "$*" 0 0
+    # Returns: None
+    # Shows the provided message on the screen with an ok button.
+    dialog --msgbox "$*" 0 0
 } 
 
 passwordbox() {
@@ -131,52 +103,100 @@ yesno() {
         echo "No"
     fi
 }
+
+# Function to encrypt a file with OpenSSL
+encrypt_file() {
+    # $1 = input file to encrypt
+    # $2 = output file
+    local passphrase
+    
+    # If master password file doesn't exist, create it
+    if [[ ! -f "$muttHome/.master_password" ]]; then
+        passphrase=""
+        passphraseCompare="-"
+        while [[ "${passphrase}" != "passphraseCompare" ]] ; do
+            passphrase="$(passwordbox "Please create a master password for encrypting your email credentials:")"
+            passphrase="$(passwordbox "Please create a master password for encrypting your email credentials:")"
+            if [[ "${passphrase}" != "passphraseCompare" ]]; then
+                msgbox "Passphrase does not match, please retry."
+            fi
+        done
+        # Store a hash of the master password for verification later
+        echo "$(echo "$passphrase" | openssl dgst -sha256 -hex | cut -d ' ' -f 2)" > "$muttHome/.master_password"
+    else
+        # Ask for existing master password
+        passphrase="$(passwordbox "Enter your master password to encrypt this email configuration:")"
+        # Verify the password
+        local stored_hash="$(cat "$muttHome/.master_password")"
+        local input_hash="$(echo "$passphrase" | openssl dgst -sha256 -hex | cut -d ' ' -f 2)"
+        
+        if [[ "$stored_hash" != "$input_hash" ]]; then
+            msgbox "Incorrect master password. Please try again."
+            encrypt_file "$1" "$2"
+            return
+        fi
+    fi
+    
+    # Encrypt the file with AES-256-CBC
+    openssl enc -aes-256-cbc -salt -a -pbkdf2 -pass pass:"$passphrase" -in "$1" -out "$2"
+}
+
+# Function to decrypt a file
+decrypt_file() {
+    # $1 = encrypted file
+    # $2 = output file (temporary)
+    local passphrase
+    
+    # If the master password file doesn't exist, we can't decrypt
+    if [[ ! -f "$muttHome/.master_password" ]]; then
+        echo "ERROR: No master password set"
+        return 1
+    fi
+    
+    # Ask for the master password
+    passphrase="$(passwordbox "Enter your master password to decrypt email configuration:")"
+    
+    # Verify the password
+    local stored_hash="$(cat "$muttHome/.master_password")"
+    local input_hash="$(echo "$passphrase" | openssl dgst -sha256 -hex | cut -d ' ' -f 2)"
+    
+    if [[ "$stored_hash" != "$input_hash" ]]; then
+        msgbox "Incorrect master password. Please try again."
+        decrypt_file "$1" "$2"
+        return
+    fi
+    
+    # Decrypt the file
+    openssl enc -aes-256-cbc -d -a -pbkdf2 -pass pass:"$passphrase" -in "$1" -out "$2" 2>/dev/null
+}
  
-initialize_directory()
-{
+initialize_directory() {
     mkdir -p "${muttHome}/cache/bodies"
     mkdir -p "${muttHome}/scripts"
+    
     if ! [ -f "$muttHome/aliases" ]; then
         touch "$muttHome/aliases"
     fi
     
     # Copy the add_address.sh script
     if ! [ -f "$muttHome/scripts/add_address.sh" ]; then
-        cp "$fileDir/add_address.sh" "$muttHome/scripts/add_address.sh"
+        cp "files/add_address.sh" "$muttHome/scripts/add_address.sh"
         chmod 700 "$muttHome/scripts/add_address.sh"
     fi
     
-    # Create mailcap file (now always using w3m)
     if ! [ -f "$muttHome/mailcap" ]; then
-        cp "$fileDir/mailcap" "$muttHome/mailcap"
+        cp "scripts/mailcap" "$muttHome/mailcap"
     fi
     
-    # Create gpg.rc file
-    if ! [ -f "$muttHome/gpg.rc" ]; then
-        find /usr -name gpg.rc -exec cp "{}" "$muttHome/" \; 2> /dev/null
-        echo "set pgp_autosign=yes" >> "$muttHome/gpg.rc"
-        echo "set crypt_autosign=yes" >> "$muttHome/gpg.rc"
-        echo "set pgp_replyencrypt=yes" >> "$muttHome/gpg.rc"
-        echo "set pgp_timeout=1800" >> "$muttHome/gpg.rc"
-        if [[ $(gpg --list-secret-keys | wc -l) -eq 0 ]]; then
-            read -p "No gpg key was found. Type your name and press enter to generate a PGP key. Control+c if you would like to create it manually. " continue
-            # Try to use quick key generation, and fall back to the more verbose version for legacy systems.
-            if ! gpg2 --quick-gen-key "${continue:-${USER}}"; then
-                gpg2 --gen-key
-            fi
-        fi
-        PS3="Select the key you want to use for encryption/signing:"
-        select key in $(gpg --list-secret-keys | grep '.*@.*' | cut -d '<' -f2 | cut -d '>' -f1) ; do
-            if [[ -n "$key" ]]; then
-                break
-            fi
-        done
-        keyName="$(gpg2 --list-secret-keys --keyid-format short | grep -B1 ^uid | head -n1 | rev | cut -c -8 | rev)"
-        echo "set pgp_sign_as=$keyName" >> "$muttHome/gpg.rc"
+    # Copy the gpg.rc file if it exists in files directory
+    if [[ -f "files/gpg.rc" ]] && ! [[ -f "$muttHome/gpg.rc" ]]; then
+        cp "files/gpg.rc" "$muttHome/gpg.rc"
+        echo "# Note: GPG signing/encryption is available but not configured." >> "$muttHome/gpg.rc"
+        echo "# You can manually set up GPG for email if needed." >> "$muttHome/gpg.rc"
     fi
     
     # Create or update macro file
-    cp "$fileDir/macros" "$muttHome/macros"
+    cp "files/macros" "$muttHome/macros"
     
     # Create basic muttrc
     if ! [ -f "$muttHome/muttrc" ]; then
@@ -185,19 +205,19 @@ initialize_directory()
         for i in nano vim ; do
             unset editorPath
             editorPath="$(command -v $i)"
-            if [ -n "$editorPath" ]; then
+            if [[ -n "$editorPath" ]]; then
                 editors[$x]="$editorPath"
                 ((x++))
             fi
         done
         echo "Select editor for email composition:"
         i="$(menulist ${EDITOR} ${EDITOR} $(for i in "${editors[@]##*/}" ; do echo "$i";echo "$i";done))"
-        if [ -z "$i" ]; then
+        if [[ -z "$i" ]]; then
             exit 0
         fi
         
         # Copy base muttrc
-        cp "$fileDir/muttrc" "$muttHome/muttrc"
+        cp "files/muttrc" "$muttHome/muttrc"
         
         # Set editor in muttrc
         case "$i" in
@@ -208,61 +228,117 @@ initialize_directory()
         # Replace muttHome in muttrc
         sed -i "s|\$muttHome|${muttHome/#$HOME/\~}|g" "$muttHome/muttrc"
     fi
+    
+    # Create the decrypt helper script
+    create_decrypt_helper
 }
 
-configure_gpg()
-{
-    # GPG stuff in .bashrc:
-    if ! grep 'GPG_TTY=$(tty)'  ~/.bashrc ; then
-        echo -e 'GPG_TTY=$(tty)\nexport GPG_TTY' >> ~/.bashrc
-    fi
+create_decrypt_helper() {
+    # Create a helper script to decrypt files
+    cat > "$muttHome/scripts/decrypt.sh" << 'EOF'
+#!/bin/bash
+# Script to decrypt an encrypted email configuration file
+# Usage: decrypt.sh encrypted_file temp_output_file
+
+if [[ $# -ne 2 ]]; then
+    echo "Usage: $0 encrypted_file temp_output_file"
+    exit 1
+fi
+
+ENCRYPTED_FILE="$1"
+OUTPUT_FILE="$2"
+MASTER_FILE="$(dirname "$ENCRYPTED_FILE")/.master_password"
+
+if [[ ! -f "$ENCRYPTED_FILE" ]]; then
+    echo "Error: Encrypted file not found"
+    exit 1
+fi
+
+if [[ ! -f "$MASTER_FILE" ]]; then
+    echo "Error: Master password file not found"
+    exit 1
+fi
+
+# Function to decrypt
+decrypt() {
+    local stored_hash="$(cat "$MASTER_FILE")"
+    local passphrase
+    local attempt=0
     
-    # Make sure the configuration directory exists
-    if ! [ -d ~/.gnupg/ ]; then
-        gpg2 --list-secret-keys &> /dev/null
-    fi
+    while [ $attempt -lt 3 ]; do
+        passphrase="$(dialog --passwordbox "Enter master password to decrypt:" 0 0 --stdout)"
+        
+        # Check password hash
+        local input_hash="$(echo "$passphrase" | openssl dgst -sha256 -hex | cut -d ' ' -f 2)"
+        
+        if [[ "$stored_hash" = "$input_hash" ]]; then
+            # Decrypt the file
+            openssl enc -aes-256-cbc -d -a -pbkdf2 -pass pass:"$passphrase" -in "$ENCRYPTED_FILE" -out "$OUTPUT_FILE" 2>/dev/null
+            return 0
+        else
+            dialog --msgbox "Incorrect password. Please try again." 0 0
+            ((attempt++))
+        fi
+    done
     
-    if [ -f ~/.gnupg/gpg.conf ]; then
-        read -p "This will overwrite your existing ~/.gnupg/gpg.conf file. Press enter to continue or control+c to abort. " continue
-    fi
-    
-    # Copy GPG configuration files
-    cp "$fileDir/gpg.conf" ~/.gnupg/gpg.conf
-    
-    if [ -f ~/.gnupg/gpg-agent.conf ]; then
-        read -p "This will overwrite your existing ~/.gnupg/gpg-agent.conf file. Press enter to continue or control+c to abort. " continue
-    fi
-    
-    cp "$fileDir/gpg-agent.conf" ~/.gnupg/gpg-agent.conf
+    echo "Too many failed attempts."
+    return 1
 }
 
-add_email_address()
-{
+# Create temp directory if it doesn't exist
+mkdir -p "$(dirname "$OUTPUT_FILE")"
+
+# Decrypt the file
+decrypt
+
+# Make sure the output file is readable
+if [[ -f "$OUTPUT_FILE" ]]; then
+    chmod 600 "$OUTPUT_FILE"
+else
+    echo "Error: Failed to decrypt file"
+    exit 1
+fi
+
+exit 0
+EOF
+    
+    # Make script executable
+    chmod 700 "$muttHome/scripts/decrypt.sh"
+}
+
+add_email_address() {
     read -p "Please enter your email address: " emailAddress
     if ! [[ "$emailAddress" =~ .*@.*\..* ]]; then
         read -p "This appears to be an invalid email address. Continue anyway? (y/n) " continue
-        if [ "${continue^}" != "Y" ]; then
+        if [[ "${continue^}" != "Y" ]]; then
             exit 0
         fi
     fi
-    if [ -f "$muttHome/$emailAddress" ]; then
+    if [[ -f "$muttHome/$emailAddress" ]] || [[ -f "$muttHome/$emailAddress.ssl" ]]; then
         read -p "This email address already exists. Overwrite the existing settings? (y/n) " continue
-        if [ "${continue^}" != "Y" ]; then
+        if [[ "${continue^}" != "Y" ]]; then
             exit 0
         else
             sed -i "/$emailAddress/d" "$muttHome/muttrc"
+            rm -f "$muttHome/$emailAddress" "$muttHome/$emailAddress.ssl"
         fi
     fi
-    read -p "Enter your name as you want it to appear in emails. From: " realName
-    echo "set realname=\"$realName\"" > "$muttHome/$emailAddress"
-    echo "set from=\"$emailAddress\"" >> "$muttHome/$emailAddress"
-    echo "set use_from = \"yes\"" >> "$muttHome/$emailAddress"
-    echo "set hostname=${emailAddress##*@}" >> "$muttHome/$emailAddress"
+    
+    # Create a temporary file for configuration
+    configFile="$muttHome/$emailAddress"
+    
+    # Write basic config
+    echo "set realname=\"$(inputbox "Enter your name as you want it to appear in emails:")\"" > "$configFile"
+    echo "set from=\"$emailAddress\"" >> "$configFile"
+    echo "set use_from = \"yes\"" >> "$configFile"
+    echo "set hostname=${emailAddress##*@}" >> "$configFile"
+    
+    # Configure account based on email domain
     case "$emailAddress" in
         *gmail.com)
             configure_gmail "$emailAddress"
         ;;
-        *hotmail.com)
+        *hotmail.com | *outlook.com | *live.com)
             configure_hotmail "$emailAddress"
         ;;
         *)
@@ -274,36 +350,41 @@ add_email_address()
             fi
     esac
     
-    # Initialize pass before using it
-    initialize_pass
+    # Get password for email
+    emailPassword="$(passwordbox "Please enter the password for $emailAddress:")"
     
-    # Password storage with pass
-    passOne=a
-    passTwo=b
-    until [ "$passOne" = "$passTwo" ]; do
-        passOne="$(passwordbox "Please enter the password for $emailAddress:")"
-        passTwo="$(passwordbox "Please enter the password again: ")"
-        if [ "$passOne" != "$passTwo" ]; then
-            echo "The passwords do not match."
-        fi
-    done
+    # Add password to the config file
+    echo "set imap_pass=\"$emailPassword\"" >> "$configFile"
+    echo "set smtp_pass=\"$emailPassword\"" >> "$configFile"
     
-    # Store password in pass
-    pass insert -f "email/$emailAddress" <<< "$passOne"
+    # Add source for aliases
+    echo "source ~/${muttHome#/home/*/}/aliases" >> "$configFile"
     
-    # Add configuration to retrieve password from pass
-    echo "set imap_pass=\"`pass show email/$emailAddress`\"" >> "$muttHome/$emailAddress"
-    echo "set smtp_pass=\"`pass show email/$emailAddress`\"" >> "$muttHome/$emailAddress"
+    # Add folder-hook
+    echo "folder-hook .*$emailAddress/ 'source ${muttHome/#$HOME/\~}/$emailAddress.ssl'" >> "$configFile"
     
+    # Encrypt the configuration file
+    encrypt_file "$configFile" "$configFile.ssl"
+    
+    # Create a wrapper configuration file that sources the encrypted file
+    echo "# Encrypted email configuration for $emailAddress" > "$configFile"
+    echo "# The real configuration is in $emailAddress.ssl (encrypted)" >> "$configFile"
+    echo "" >> "$configFile"
+    echo "# This command will decrypt the configuration when needed" >> "$configFile"
+    echo "source \"| $muttHome/scripts/decrypt.sh $muttHome/$emailAddress.ssl /tmp/mutt-$emailAddress-$USER\"" >> "$configFile"
+    
+    # Add keybinding in muttrc
     add_keybinding
-    echo "folder-hook .*$emailAddress/ 'source ${muttHome/#$HOME/\~}/$emailAddress'" >> "$muttHome/$emailAddress"
+    
+    # Clean up
+    rm -f "/tmp/mutt-$emailAddress-$USER" 2>/dev/null
+    
     msgbox "Email address added, press enter to continue."
 }
 
-configure_gmail()
-{
+configure_gmail() {
     # Copy the Gmail template and replace placeholders
-    cp "$fileDir/gmail.template" "$muttHome/$1.tmp"
+    cp "files/gmail.template" "$muttHome/$1.tmp"
     
     # Replace email placeholder in the template
     sed -i "s|EMAIL_ADDRESS|$1|g" "$muttHome/$1.tmp"
@@ -317,21 +398,16 @@ configure_gmail()
     unset continue
     if command -v goobook &> /dev/null ; then
         read -p "Goobook is installed, would you like to use it as your addressbook for the account $1? " continue
-        if [ "${continue^}" = "Y" ]; then
+        if [[ "${continue^}" = "Y" ]]; then
             echo "set query_command=\"goobook query %s\"" >> "$muttHome/$1"
             echo "macro index,pager a \"<pipe-message>goobook add<return>\" \"add sender to google contacts\"" >> "$muttHome/$1"
-        else
-            echo "source ~/${muttHome#/home/*/}/aliases" >> "$muttHome/$1"
         fi
-    else
-        echo "source ~/${muttHome#/home/*/}/aliases" >> "$muttHome/$1"
     fi
 }
 
-configure_hotmail()
-{
+configure_hotmail() {
     # Copy the Hotmail template and replace placeholders
-    cp "$fileDir/hotmail.template" "$muttHome/$1.tmp"
+    cp "files/hotmail.template" "$muttHome/$1.tmp"
     
     # Replace email placeholder in the template
     sed -i "s|EMAIL_ADDRESS|$1|g" "$muttHome/$1.tmp"
@@ -339,13 +415,9 @@ configure_hotmail()
     # Append the template to the existing config
     cat "$muttHome/$1.tmp" >> "$muttHome/$1"
     rm "$muttHome/$1.tmp"
-    
-    # Add source for aliases
-    echo "source ~/${muttHome#/home/*/}/aliases" >> "$muttHome/$1"
 }
 
-configure_generic()
-{
+configure_generic() {
     # Break the email address into its components:
     local userName="${1%%@*}"
     local hostName="${1##*@}"
@@ -365,7 +437,7 @@ configure_generic()
     read -p "Enter smtp port: " -e -i 587 smtpPort
     
     # Copy the generic template
-    cp "$fileDir/generic.template" "$muttHome/$1.tmp"
+    cp "files/generic.template" "$muttHome/$1.tmp"
     
     # Replace placeholders
     sed -i "s|IMAP_HOST|$imapHost|g" "$muttHome/$1.tmp"
@@ -385,13 +457,9 @@ configure_generic()
         echo "$extraSettings" >> "$muttHome/$1"
         read extraSettings
     done
-    
-    # Add source for aliases
-    echo "source ~/${muttHome#/home/*/}/aliases" >> "$muttHome/$1"
 }
 
-add_keybinding()
-{
+add_keybinding() {
     # Here we search for previous keybinding
     local fNumber=1
     while : ; do
@@ -403,20 +471,19 @@ add_keybinding()
     echo "mail account $emailAddress bound to F$fNumber."
     if ! grep "^source.*@.*\..*" "$muttHome/muttrc" &> /dev/null ; then
         continue="$(yesno "Make $emailAddress the default account?")"
-        if [ "$continue" = "Yes" ]; then
+        if [[ "$continue" = "Yes" ]]; then
             echo "source ${muttHome/#$HOME/\~}/$emailAddress" >> "$muttHome/muttrc"
         fi
     fi
 }
 
-new_contact()
-{
+new_contact() {
     contactName="$(inputbox "Enter the contact's first and last name..")"
-    if [ -z "$contactName" ]; then
+    if [[ -z "$contactName" ]]; then
         exit 0
     fi
     contactEmail="$(inputbox "Enter the email address for $contactName")"
-    if [ -z "$contactEmail" ]; then
+    if [[ -z "$contactEmail" ]]; then
         exit 0
     fi
     contactAlias="${contactName,,}"
@@ -435,12 +502,12 @@ check_dependancies
 initialize_directory
 
 # If in test mode, display a notification
-if [ "$testMode" = true ]; then
+if [[ "$testMode" = true ]]; then
     echo "Running in test mode. Using $muttHome for configuration."
 fi
 
 # Let's make a mainmenu variable to hold all the options for the select loop.
-mainmenu=("Add Email Address" "Configure GPG" "New Contact" "Exit")
+mainmenu=("Add Email Address" "New Contact" "Exit")
 while : ; do
     i="$(IFS=$'\n';menulist $(for i in "${mainmenu[@]}" ; do echo "$i";echo "$i";done))"
     [[ -z "$i" ]] && continue