\section{User Interface} \ignore{ \begin{code} module SimpleCiphersMain (main) where import IO import System (getArgs,getProgName,exitFailure) import SimpleCiphers import Brianweb.IO import Brianweb.Misc \end{code} } \begin{code} commands :: [(String,CipherFunctions IO -> [String] -> IO())] commands = [ ("encrypt",\(f,_,_,_,_) -> runCrypt f), ("decrypt",\(_,f,_,_,_) -> runCrypt f), ("break", \(_,_,f,_,_) -> runBreak f), ("pbreak", \(_,_,_,f,_) -> runPBreak f), ("help", \(_,_,_,_,h) -> const $ putStrLn $ "The key is in the format : " ++ h) ] \end{code} {\tt commands} maps command names to their actions. Encrypt and decrypt run runCipher on the given cipher function. Break runs runBreak if a break function is available. \begin{code} main :: IO() main = do argv <- getArgs case argv of (cipher:command:args) -> do fs <- partialLookup cipher ciphers "cipher" action <- partialLookup command commands "command" action fs args `catch` \e -> if isUserError e then do hPutStrLn stderr $ ioeGetErrorString e exitFailure else ioError e _ -> usage where usage = do me <- getProgName hPutStrLn stderr $ unlines $ [ "Usage: " ++ me ++ " cipher cmd arg [file,...]", " cipher is " ++ (englishJoin "or" $ map fst ciphers'), " cmd is " ++ (englishJoin "or" $ map fst commands), " arg is the key or language depending on the command", " key formats:"] ++ map (\(c,(_,_,_,_,h)) -> " " ++ c ++ ": " ++ h) ciphers' exitFailure where ciphers' :: [(String,CipherFunctions IO)] ciphers' = ciphers \end{code} The main function. This processes the command line arguments and does whatever action is specified for the command, \begin{code} runCrypt :: (String -> IO([Int] -> [Int])) -> [String] -> IO() runCrypt _ [] = fail "The crypt commands requires a key" runCrypt cf (key:files) = do s <- cat files f <- cf key putStrLn $ fromNumbers $ f $ toNumbers s \end{code} runCrypt runs the specified cipher on the input (files or stdin) and prints the result to stdout. The cipher can be either an encryption or decryption function. They both work the same. \begin{code} runBreakOutput :: Int -> [(String,[Int])] -> IO() runBreakOutput _ [] = putStrLn "Sorry, no more possible decryptions available" runBreakOutput n ((k,m):xs) = do putStrLn $ "Possible decryption #" ++ (show n) ++ ", key: " ++ k putStrLn $ fromNumbers m c <- keyPrompt stdin stdout "Does this look right? [ynq]" "ynq" case c of 'y' -> putStrLn "Done!" 'q' -> do hPutStrLn stderr "Aborting!" exitFailure 'n' -> runBreakOutput (n+1) xs _ -> undefined \end{code} \begin{code} runBreak :: IO(Break String) -> [String] -> IO() runBreak _ [] = fail "The break command requires a language" runBreak _ (_:[]) = fail "The break command cannot be run on stdin" runBreak mf (language:files) = do f <- mf lv <- partialLookup language languages "language" s <- cat files runBreakOutput 1 $ f (toNumbers s) lv \end{code} {\tt runBreak} runs the break function. {\tt runBreakOutput} provides an easy to use interface to the results. Each potential plain-text result is presented to the user one at a time and the user can chose to continue or not. This avoids computing all possible results in the common case that the most probable result is the correct result. \begin{code} runPBreak :: IO(PBreak String) -> [String] -> IO() runPBreak mf [language,ptf,ctf] = do f <- mf lv <- partialLookup language languages "language" pt <- readFile ptf ct <- readFile ctf runBreakOutput 1 $ f (toNumbers pt) (toNumbers ct) lv runPBreak _ _ = fail $ "The pbreak command requires a language, " ++ "a plaintext file, and a ciphertext file" \end{code} {\tt runPBreak} works just like {\tt runBreak} except for doing a known plaintext attack instead of a cipertext only attack.