Files
simplex-chat/src/Simplex/Chat/Options.hs
Evgeny Poberezkin 491fe4a9bf core, ios: advanced server config (#1371)
* ios: advanced server config

* simplify UI

* core: ServerCfg

* commit migration, update schema

* add preset servers to response

* return default servers if none saved

* fix test
2022-11-16 15:37:20 +00:00

184 lines
5.3 KiB
Haskell

{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Simplex.Chat.Options
( ChatOpts (..),
getChatOpts,
smpServersP,
fullNetworkConfig,
)
where
import qualified Data.Attoparsec.ByteString.Char8 as A
import qualified Data.ByteString.Char8 as B
import Options.Applicative
import Simplex.Chat.Controller (updateStr, versionStr)
import Simplex.Chat.Types (ServerCfg (..))
import Simplex.Messaging.Client (NetworkConfig (..), defaultNetworkConfig)
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (parseAll)
import Simplex.Messaging.Transport.Client (SocksProxy, defaultSocksProxy)
import System.FilePath (combine)
data ChatOpts = ChatOpts
{ dbFilePrefix :: String,
dbKey :: String,
smpServers :: [ServerCfg],
networkConfig :: NetworkConfig,
logConnections :: Bool,
logServerHosts :: Bool,
logAgent :: Bool,
chatCmd :: String,
chatCmdDelay :: Int,
chatServerPort :: Maybe String,
maintenance :: Bool
}
chatOpts :: FilePath -> FilePath -> Parser ChatOpts
chatOpts appDir defaultDbFileName = do
dbFilePrefix <-
strOption
( long "database"
<> short 'd'
<> metavar "DB_FILE"
<> help "Path prefix to chat and agent database files"
<> value defaultDbFilePath
<> showDefault
)
dbKey <-
strOption
( long "key"
<> short 'k'
<> metavar "KEY"
<> help "Database encryption key/pass-phrase"
<> value ""
)
smpServers <-
option
parseSMPServers
( long "server"
<> short 's'
<> metavar "SERVER"
<> help "Semicolon-separated list of SMP server(s) to use (each server can have more than one hostname)"
<> value []
)
socksProxy <-
flag' (Just defaultSocksProxy) (short 'x' <> help "Use local SOCKS5 proxy at :9050")
<|> option
parseSocksProxy
( long "socks-proxy"
<> metavar "SOCKS5"
<> help "Use SOCKS5 proxy at `ipv4:port` or `:port`"
<> value Nothing
)
t <-
option
auto
( long "tcp-timeout"
<> metavar "TIMEOUT"
<> help "TCP timeout, seconds (default: 5/10 without/with SOCKS5 proxy)"
<> value 0
)
logConnections <-
switch
( long "connections"
<> short 'c'
<> help "Log every contact and group connection on start"
)
logServerHosts <-
switch
( long "log-hosts"
<> short 'l'
<> help "Log connections to servers"
)
logAgent <-
switch
( long "log-agent"
<> help "Enable logs from SMP agent"
)
chatCmd <-
strOption
( long "execute"
<> short 'e'
<> metavar "COMMAND"
<> help "Execute chat command (received messages won't be logged) and exit"
<> value ""
)
chatCmdDelay <-
option
auto
( long "time"
<> short 't'
<> metavar "TIME"
<> help "Time to wait after sending chat command before exiting, seconds"
<> value 3
<> showDefault
)
chatServerPort <-
option
parseServerPort
( long "chat-server-port"
<> short 'p'
<> metavar "PORT"
<> help "Run chat server on specified port"
<> value Nothing
)
maintenance <-
switch
( long "maintenance"
<> short 'm'
<> help "Run in maintenance mode (/_start to start chat)"
)
pure
ChatOpts
{ dbFilePrefix,
dbKey,
smpServers,
networkConfig = fullNetworkConfig socksProxy $ useTcpTimeout socksProxy t,
logConnections,
logServerHosts,
logAgent,
chatCmd,
chatCmdDelay,
chatServerPort,
maintenance
}
where
useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 5 (const 10) p
defaultDbFilePath = combine appDir defaultDbFileName
fullNetworkConfig :: Maybe SocksProxy -> Int -> NetworkConfig
fullNetworkConfig socksProxy tcpTimeout =
let tcpConnectTimeout = (tcpTimeout * 3) `div` 2
in defaultNetworkConfig {socksProxy, tcpTimeout, tcpConnectTimeout}
parseSMPServers :: ReadM [ServerCfg]
parseSMPServers = eitherReader $ parseAll smpServersP . B.pack
parseSocksProxy :: ReadM (Maybe SocksProxy)
parseSocksProxy = eitherReader $ parseAll strP . B.pack
parseServerPort :: ReadM (Maybe String)
parseServerPort = eitherReader $ parseAll serverPortP . B.pack
serverPortP :: A.Parser (Maybe String)
serverPortP = Just . B.unpack <$> A.takeWhile A.isDigit
smpServersP :: A.Parser [ServerCfg]
smpServersP = (toServerCfg <$> strP) `A.sepBy1` A.char ';'
where
toServerCfg server = ServerCfg {server, preset = False, tested = Nothing, enabled = True}
getChatOpts :: FilePath -> FilePath -> IO ChatOpts
getChatOpts appDir defaultDbFileName =
execParser $
info
(helper <*> versionOption <*> chatOpts appDir defaultDbFileName)
(header versionStr <> fullDesc <> progDesc "Start chat with DB_FILE file and use SERVER as SMP server")
where
versionOption = infoOption versionAndUpdate (long "version" <> short 'v' <> help "Show version")
versionAndUpdate = versionStr <> "\n" <> updateStr