packages feed

keysafe-0.20160831: CmdLine.hs

{- Copyright 2016 Joey Hess <id@joeyh.name>
 -
 - 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 }