305 lines
9.0 KiB
Haskell
305 lines
9.0 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
module Main (main) where
|
|
|
|
import Data.ByteString.UTF8 (fromString, toString)
|
|
import Database.SQLite.Simple
|
|
import System.Hclip
|
|
import qualified Data.Text as T
|
|
import qualified Data.Map as Map
|
|
|
|
import qualified UserInterface as Ui
|
|
import qualified Database as Db
|
|
import qualified Crypto as Cr
|
|
import qualified Utils as Ut
|
|
|
|
main :: IO ()
|
|
main = do
|
|
conn <- Db.init_connection
|
|
|
|
Ut.clear_screen
|
|
putStrLn "Welcome to Hassword Manager!!!"
|
|
|
|
setup_application conn
|
|
mpass <- open_hassword_book conn
|
|
application_loop conn mpass
|
|
|
|
Db.close_connection conn
|
|
|
|
|
|
setup_application :: Connection -> IO ()
|
|
setup_application conn = do
|
|
first <- Db.is_it_first_app_usage conn
|
|
if first
|
|
then do
|
|
Ut.set_green
|
|
putStrLn "It seems like you are using this application for the first time."
|
|
Ut.reset_color
|
|
create_mpass conn
|
|
else
|
|
return ()
|
|
|
|
create_mpass :: Connection -> IO ()
|
|
create_mpass conn = do
|
|
res <- input_new_valid_mpass
|
|
case res of
|
|
Ut.Valid mpass -> do
|
|
Db.insert_new_mpass conn (Cr.hash' $ fromString mpass)
|
|
Ut.DoNotMatch -> do
|
|
Ut.set_red
|
|
putStrLn "Passwords do not match!!!\n"
|
|
Ut.reset_color
|
|
create_mpass conn
|
|
Ut.TooShort -> do
|
|
Ut.set_red
|
|
putStrLn "Password is too short (min 8 symbols)!!!\n"
|
|
Ut.reset_color
|
|
create_mpass conn
|
|
Ut.Empty -> do
|
|
Ut.set_red
|
|
putStrLn "Password cannot be empty!!!\n"
|
|
Ut.reset_color
|
|
create_mpass conn
|
|
|
|
input_new_valid_mpass :: IO (Ut.MasterPasswordValidationCases)
|
|
input_new_valid_mpass = do
|
|
(password1:password2:_) <- sequence [putStrLn "Please Enter new MASTER PASSWORD:" >> Ut.get_password, putStrLn "Enter the password again:" >> Ut.get_password]
|
|
return $ Ut.validate_password password1 password2
|
|
|
|
open_hassword_book :: Connection -> IO (String)
|
|
open_hassword_book conn = do
|
|
putStrLn "Please Enter your MASTER PASSWORD:"
|
|
mpass <- Ut.get_password
|
|
mpass_valid <- Db.check_if_mpass_valid conn (Cr.hash' $ fromString mpass)
|
|
if mpass_valid
|
|
then
|
|
return mpass
|
|
else do
|
|
Ut.set_red
|
|
putStrLn "Invalid MASTER PASSWORD!!!\n"
|
|
Ut.reset_color
|
|
open_hassword_book conn
|
|
|
|
get_action_choice :: IO (Ut.ActionChoice)
|
|
get_action_choice = do
|
|
putStrLn "\nWhat would you like to do?"
|
|
putStrLn "1. List all entries"
|
|
putStrLn "2. Copy an entry password"
|
|
putStrLn "3. Add a new entry"
|
|
putStrLn "4. Delete an entry"
|
|
putStrLn "5. Update an entry password"
|
|
putStrLn "6. Change Master Password"
|
|
putStrLn "7. Close the application"
|
|
choice <- getLine
|
|
case choice of
|
|
"1" -> return Ut.ListAllEntries
|
|
"2" -> return Ut.CopyEntryPassword
|
|
"3" -> return Ut.AddNewEntry
|
|
"4" -> return Ut.DeleteEntry
|
|
"5" -> return Ut.UpdateEntry
|
|
"6" -> return Ut.ChangeMasterPassword
|
|
"7" -> return Ut.Exit
|
|
_ -> return Ut.InvalidAction
|
|
|
|
application_loop :: Connection -> String -> IO ()
|
|
application_loop conn mpass = do
|
|
choice <- get_action_choice
|
|
case choice of
|
|
Ut.ListAllEntries -> do
|
|
entries <- Db.get_all_entries conn
|
|
if null entries
|
|
then do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "No entries to list!!!"
|
|
Ut.reset_color
|
|
application_loop conn mpass
|
|
else do
|
|
list_all_entries conn
|
|
application_loop conn mpass
|
|
Ut.CopyEntryPassword -> do
|
|
entries <- Db.get_all_entries conn
|
|
if null entries
|
|
then do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "No entries to copy a password from!!!"
|
|
Ut.reset_color
|
|
application_loop conn mpass
|
|
else do
|
|
copy_entry_pass conn mpass
|
|
application_loop conn mpass
|
|
Ut.AddNewEntry -> do
|
|
add_entry conn mpass
|
|
application_loop conn mpass
|
|
Ut.DeleteEntry -> do
|
|
entries <- Db.get_all_entries conn
|
|
if null entries
|
|
then do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "No entries to delete!!!"
|
|
Ut.reset_color
|
|
application_loop conn mpass
|
|
else do
|
|
delete_entry conn
|
|
application_loop conn mpass
|
|
Ut.UpdateEntry -> do
|
|
entries <- Db.get_all_entries conn
|
|
if null entries
|
|
then do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "No entries to update!!!"
|
|
Ut.reset_color
|
|
application_loop conn mpass
|
|
else do
|
|
update_entry conn mpass
|
|
application_loop conn mpass
|
|
Ut.ChangeMasterPassword -> do
|
|
mpass <- change_mpass conn mpass
|
|
application_loop conn mpass
|
|
Ut.InvalidAction -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Invalid choice!!!"
|
|
Ut.reset_color
|
|
application_loop conn mpass
|
|
Ut.Exit -> do
|
|
return ()
|
|
|
|
list_all_entries :: Connection -> IO (Map.Map Int (String, String))
|
|
list_all_entries conn = do
|
|
Ut.clear_screen
|
|
entries <- Db.get_all_entries conn
|
|
Ui.print_entries entries
|
|
let entries_map = Ut.map_entries entries
|
|
return entries_map
|
|
|
|
copy_entry_pass :: Connection -> String -> IO ()
|
|
copy_entry_pass conn mpass = do
|
|
entries <- list_all_entries conn
|
|
chosen_entry <- Ui.choose_entry "Please choose the entry ID to copy password:"
|
|
let result = Map.lookup chosen_entry entries
|
|
case result of
|
|
Just (service, login) -> do
|
|
entry <- Db.get_entry conn (T.pack service) (T.pack login)
|
|
setClipboard (Cr.decrypt' mpass (T.unpack $ Db.entryPassword entry))
|
|
Ut.clear_screen
|
|
Ut.set_green
|
|
putStrLn "Password copied to clipboard!!!"
|
|
Ut.reset_color
|
|
Nothing -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Invalid entry ID!!!"
|
|
Ut.reset_color
|
|
return ()
|
|
|
|
add_entry :: Connection -> String -> IO ()
|
|
add_entry conn mpass = do
|
|
Ut.clear_screen
|
|
putStrLn "Please Enter the service name for the new entry:"
|
|
service <- getLine
|
|
putStrLn "Please Enter the login for the new entry:"
|
|
login <- getLine
|
|
entry_exists <- Db.entry_already_exists conn (T.pack service) (T.pack login)
|
|
if entry_exists
|
|
then do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Entry already exists!!!"
|
|
Ut.reset_color
|
|
return ()
|
|
else do
|
|
putStrLn "Please Enter the password:"
|
|
password <- Ut.get_password
|
|
Db.add_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
|
|
Ut.clear_screen
|
|
Ut.set_green
|
|
putStrLn "Entry added successfully!!!"
|
|
Ut.reset_color
|
|
|
|
update_entry :: Connection -> String -> IO ()
|
|
update_entry conn mpass = do
|
|
Ut.clear_screen
|
|
entries <- list_all_entries conn
|
|
chosen_entry <- Ui.choose_entry "Please choose the entry ID to update:"
|
|
let result = Map.lookup chosen_entry entries
|
|
case result of
|
|
Just (service, login) -> do
|
|
putStrLn "Please Enter the new password:"
|
|
password <- Ut.get_password
|
|
Db.update_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
|
|
Ut.clear_screen
|
|
Ut.set_green
|
|
putStrLn "Entry updated successfully!!!"
|
|
Ut.reset_color
|
|
Nothing -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Invalid entry ID!!!"
|
|
Ut.reset_color
|
|
return ()
|
|
|
|
delete_entry :: Connection -> IO ()
|
|
delete_entry conn = do
|
|
entries <- list_all_entries conn
|
|
chosen_entry <- Ui.choose_entry "Please choose the entry ID to delete:"
|
|
let result = Map.lookup chosen_entry entries
|
|
case result of
|
|
Just (service, login) -> do
|
|
Db.delete_entry conn (T.pack service) (T.pack login)
|
|
Ut.clear_screen
|
|
Ut.set_green
|
|
putStrLn "Entry deleted successfully!!!"
|
|
Ut.reset_color
|
|
Nothing -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Invalid entry ID!!!"
|
|
Ut.reset_color
|
|
return ()
|
|
|
|
change_mpass :: Connection -> String -> IO (String)
|
|
change_mpass conn mpass = do
|
|
putStrLn "Please Enter your current MASTER PASSWORD:"
|
|
current_mpass <- Ut.get_password
|
|
mpass_valid <- Db.check_if_mpass_valid conn (Cr.hash' $ fromString current_mpass)
|
|
if mpass_valid
|
|
then do
|
|
res <- input_new_valid_mpass
|
|
case res of
|
|
Ut.Valid new_mpass -> do
|
|
Db.insert_new_mpass conn (Cr.hash' $ fromString new_mpass)
|
|
entries <- Db.get_all_entries conn
|
|
mapM_ (\entry -> Db.update_entry conn (Db.entryService entry) (Db.entryLogin entry) (T.pack (Cr.encrypt' new_mpass (Cr.decrypt' mpass (T.unpack (Db.entryPassword entry)))))) entries
|
|
Ut.clear_screen
|
|
Ut.set_green
|
|
putStrLn "Master Password changed successfully!!!\n"
|
|
Ut.reset_color
|
|
return new_mpass
|
|
Ut.DoNotMatch -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Passwords do not match!!!\n"
|
|
Ut.reset_color
|
|
return mpass
|
|
Ut.TooShort -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Password is too short (min 8 symbols)!!!\n"
|
|
Ut.reset_color
|
|
return mpass
|
|
Ut.Empty -> do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Password cannot be empty!!!\n"
|
|
Ut.reset_color
|
|
return mpass
|
|
else do
|
|
Ut.clear_screen
|
|
Ut.set_red
|
|
putStrLn "Invalid MASTER PASSWORD!!!\n"
|
|
Ut.reset_color
|
|
return mpass
|