Files
simplex-chat/src/Simplex/Chat/Options.hs
T
Evgeny Poberezkin 4fd13c637c core: access messaging servers via SOCKS5 proxy (#835)
* core: access messaging servers via SOCKS5 proxy

* update option info

* update simplexmq
2022-07-23 14:49:04 +01:00

163 lines
5.0 KiB
Haskell

{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Simplex.Chat.Options
( ChatOpts (..),
getChatOpts,
smpServersP,
)
where
import qualified Data.Attoparsec.ByteString.Char8 as A
import qualified Data.ByteString.Char8 as B
import Data.Maybe (fromMaybe)
import Network.Socket (HostAddress, SockAddr (..), tupleToHostAddress)
import Network.Socks5 (SocksConf, defaultSocksConf)
import Options.Applicative
import Simplex.Chat.Controller (updateStr, versionStr)
import Simplex.Messaging.Agent.Protocol (SMPServer)
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (parseAll)
import System.FilePath (combine)
data ChatOpts = ChatOpts
{ dbFilePrefix :: String,
smpServers :: [SMPServer],
socksProxy :: Maybe SocksConf,
tcpTimeout :: Int,
logConnections :: 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
)
smpServers <-
option
parseSMPServers
( long "server"
<> short 's'
<> metavar "SERVER"
<> help "Comma separated list of SMP server(s) to use"
<> value []
)
socksProxy <-
flag' (Just defaultSocksProxy) (short 'x' <> help "use local SOCKS5 proxy at :9050")
<|> option
parseSocksConf
( long "socks-proxy"
<> metavar "SOCKS5"
<> help "`ipv4:port` or `:port` of SOCKS5 proxy"
<> 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"
)
logAgent <-
switch
( long "log-agent"
<> short 'l'
<> 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, smpServers, socksProxy, tcpTimeout = useTcpTimeout socksProxy t, logConnections, 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
parseSMPServers :: ReadM [SMPServer]
parseSMPServers = eitherReader $ parseAll smpServersP . B.pack
defaultSocksHost :: HostAddress
defaultSocksHost = tupleToHostAddress (127, 0, 0, 1)
defaultSocksProxy :: SocksConf
defaultSocksProxy = defaultSocksConf $ SockAddrInet 9050 defaultSocksHost
parseSocksConf :: ReadM (Maybe SocksConf)
parseSocksConf = eitherReader $ parseAll socksConfP . B.pack
where
socksConfP = do
host <- maybe defaultSocksHost tupleToHostAddress <$> optional ipv4P
port <- fromMaybe 9050 <$> optional (A.char ':' *> (fromInteger <$> A.decimal))
pure . Just . defaultSocksConf $ SockAddrInet port host
ipv4P = (,,,) <$> ipNum <*> ipNum <*> ipNum <*> A.decimal
ipNum = A.decimal <* A.char '.'
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 [SMPServer]
smpServersP = strP `A.sepBy1` A.char ','
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