packages feed

shadowsocks-1.20140617: Shadowsocks/Util.hs

{-# LANGUAGE DeriveGeneric #-}
module Shadowsocks.Util
  ( Config (..)
  , parseConfigOptions
  ) where

import           Control.Monad (liftM)
import           Data.Aeson (decode', FromJSON)
import qualified Data.ByteString.Lazy as L
import           Data.Maybe (fromMaybe)
import           GHC.Generics (Generic)
import           Options.Applicative
import           Options.Applicative.Types (ReadM(ReadM))

data Config = Config
    { server        :: String
    , server_port   :: Int
    , local_port    :: Int
    , password      :: String
    , timeout       :: Int
    , method        :: String
    } deriving (Show, Generic)

instance FromJSON Config

data Options = Options
    { _server        :: Maybe String
    , _server_port   :: Maybe Int
    , _local_port    :: Maybe Int
    , _password      :: Maybe String
    , _method        :: Maybe String
    , _config        :: Maybe String
    } deriving (Show, Generic)

nullConfig :: Config
nullConfig = Config "" 0 0 "" 0 ""

readConfig :: FilePath -> IO (Maybe Config)
readConfig fp = liftM decode' $ L.readFile fp

optStr :: Mod OptionFields (Maybe String) -> Parser (Maybe String)
optStr m =
    nullOption $ value Nothing <> reader (success . str) <> m
  where
    success = ReadM . Right

maybeOpt :: Read a => Mod OptionFields (Maybe a) -> Parser (Maybe a)
maybeOpt m =
    nullOption $ value Nothing <> reader (success . auto) <> m
  where
    success = ReadM . Right

configOptions :: Parser Options
configOptions = Options
    <$> optStr (long "server" <> short 's' <> metavar "ADDR"
               <> help "server address")
    <*> maybeOpt (long "server-port" <> short 'p' <> metavar "PORT"
                 <> help "server port")
    <*> maybeOpt (long "local-port" <> short 'l' <> metavar "PORT"
                 <> help "local port")
    <*> optStr (long "password" <> short 'k' <> metavar "PASSWORD"
               <> help "password")
    <*> optStr (long "method" <> short 'm' <> metavar "METHOD"
               <> help "encryption method, for example, aes-256-cfb")
    <*> optStr (long "config" <> short 'c' <> metavar "CONFIG"
               <> help "path to config file")

parseConfigOptions :: IO Config
parseConfigOptions = do
    o <- execParser $ info (helper <*> configOptions)
                      (fullDesc <> header "shadowsocks - a fast tunnel proxy")
    let configFile = fromMaybe "config.json" (_config o)
    mconfig <- readConfig configFile
    let c = fromMaybe nullConfig mconfig
    return $ c { server = fromMaybe (server c) (_server o)
               , server_port = fromMaybe (server_port c) (_server_port o)
               , local_port = fromMaybe (local_port c) (_local_port o)
               , password = fromMaybe (password c) (_password o)
               , method = fromMaybe (method c) (_method o)
               }