{- Copyright 2016 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} module CmdLine where import Types import Types.Storage import Tunables import qualified Gpg import Options.Applicative import qualified Data.ByteString.UTF8 as BU8 import System.Directory import Network.Wai.Handler.Warp (Port) data CmdLine = CmdLine { mode :: Maybe Mode , secretkeysource :: Maybe SecretKeySource , localstorage :: Bool , localstoragedirectory :: Maybe LocalStorageDirectory , gui :: Bool , testMode :: Bool , customShareParams :: Maybe ShareParams , serverConfig :: ServerConfig } data Mode = Backup | Restore | UploadQueued | Server | Benchmark | Test deriving (Show) data ServerConfig = ServerConfig { serverPort :: Port , serverAddress :: String } parse :: Parser CmdLine parse = CmdLine <$> optional (backup <|> restore <|> uploadqueued <|> server <|> benchmark <|> test) <*> optional (gpgswitch <|> fileswitch) <*> localstorageswitch <*> localstoragedirectory <*> guiswitch <*> testmodeswitch <*> optional (ShareParams <$> totalobjects <*> neededobjects) <*> serverconfig where backup = flag' Backup ( long "backup" <> help "Store a secret key in keysafe." ) restore = flag' Restore ( long "restore" <> help "Retrieve a secret key from keysafe." ) uploadqueued = flag' UploadQueued ( long "uploadqueued" <> help "Upload any data to servers that was queued by a previous --backup run." ) server = flag' Server ( long "server" <> help "Run as a keysafe server, accepting objects and storing them to ~/.keysafe/objects/local/" ) benchmark = flag' Benchmark ( long "benchmark" <> help "Benchmark speed of keysafe's cryptographic primitives." ) test = flag' Test ( long "test" <> help "Run test suite." ) gpgswitch = GpgKey . KeyId . BU8.fromString <$> strOption ( long "gpgkeyid" <> metavar "KEYID" <> help "Specify keyid of gpg key to back up or restore. (When this option is used to back up a key, it must also be used at restore time.)" ) fileswitch = KeyFile <$> strOption ( long "keyfile" <> metavar "FILE" <> help "Specify secret key file to back up or restore. (The same filename must be used to restore a key as was used to back it up.)" ) localstorageswitch = switch ( long "store-local" <> help "Store data locally. (The default is to store data in the cloud.)" ) localstoragedirectory = optional $ LocalStorageDirectory <$> option str ( long "store-directory" <> metavar "DIR" <> help "Where to store data locally. (default: ~/.keysafe/objects/)" ) testmodeswitch = switch ( long "testmode" <> help "Avoid using expensive cryptographic operations to secure data. Use for testing only, not with real secret keys." ) guiswitch = switch ( long "gui" <> help "Use GUI interface for interaction. Default is to use readline interface when run in a terminal, and GUI otherwise." ) totalobjects = option auto ( long "totalshares" <> metavar "M" <> help ("Configure the number of shares to split encrypted secret key into. (default: " ++ show (totalObjects (shareParams defaultTunables)) ++ ") (When this option is used to back up a key, it must also be provided at restore time.)") ) neededobjects = option auto ( long "neededshares" <> metavar "N" <> help ("Configure the number of shares needed to restore. (default: " ++ show (neededObjects (shareParams defaultTunables)) ++ ") (When this option is used to back up a key, it must also be provided at restore time.)") ) serverconfig = ServerConfig <$> option auto ( long "port" <> metavar "P" <> value 80 <> showDefault <> help "Port for server to listen on." ) <*> option str ( long "address" <> metavar "A" <> value "127.0.0.1" <> showDefault <> help "Address for server to bind to. (Use \"*\" to bind to all addresses.)" ) get :: IO CmdLine get = execParser opts where opts = info (helper <*> parse) ( fullDesc <> header "keysafe - securely back up secret keys" ) -- | When a mode is not specified on the command line, -- default to backing up if a secret key exists, and otherwise restoring. selectMode :: CmdLine -> IO Mode selectMode cmdline = case mode cmdline of Just m -> return m Nothing -> case secretkeysource cmdline of Just (KeyFile f) -> present <$> doesFileExist f _ -> present . not . null <$> Gpg.listSecretKeys where present True = Backup present False = Restore customizeShareParams :: CmdLine -> Tunables -> Tunables customizeShareParams cmdline t = case customShareParams cmdline of Nothing -> t Just ps -> t { shareParams = ps }