content: -- |Various options for running a Tor node
  module Tor.Options(
           -- * Options for running Tor
           TorOptions(..),         defaultTorOptions
         , TorEntranceOptions(..), defaultTorEntranceOptions
         , TorRelayOptions(..),    defaultTorRelayOptions
         , TorExitOptions(..),     defaultTorExitOptions
         , ExitRule(..), AddrSpec(..), PortSpec(..)
           -- * Handy utilities
         , makeLogger
         )
   where
  
  import Data.Hourglass
  import Data.Word
  import System.Hourglass
  import Tor.RouterDesc
  
  -- |How the node should be set up during initialization. For each of these
  -- items, 'Nothing' means that the node will not operate in that capacity, while
  -- Just of the option type will initialize that system with those options.
  --
  -- Note that while we will do our best to make it work, it doesn't make a whole
  -- lot of sense to be an Exit node and not be a Relay node.
  data TorOptions = TorOptions {
         torLog             :: String -> IO ()
       , torEntranceOptions :: Maybe TorEntranceOptions
       , torRelayOptions    :: Maybe TorRelayOptions
       , torExitOptions     :: Maybe TorExitOptions
       }
  
  -- |A reasonable default set of options for a Tor node. Sets the node up as an
  -- entrance and relay node with their standard options, and logging output
  -- printed to stdout.
  defaultTorOptions :: TorOptions
  defaultTorOptions = TorOptions {
      torLog             = makeLogger putStrLn
    , torEntranceOptions = Just defaultTorEntranceOptions
    , torRelayOptions    = Just defaultTorRelayOptions
    , torExitOptions     = Nothing
    }
  
  -- |Options for allowing circuits that originated at this node.
  data TorEntranceOptions = TorEntranceOptions {
         -- |The number of intermediate hops to use between this node and
         -- the exit node. To be clear, created circuits will have an entrance
         -- node, this number of nodes, and then the exit node.
         torInternalCircuitLength :: Int
         -- |The target number of external connections to keep alive for
         -- outgoing connections. Note that this is a target, rather than a hard
         -- minimum or limit.
       , torTargetLinks :: Int
       }
  
  -- |A reasonable set of entrance options. The internal circuit length is set to
  -- 4, and a target number of links of 5.
  defaultTorEntranceOptions :: TorEntranceOptions
  defaultTorEntranceOptions  = TorEntranceOptions {
      torInternalCircuitLength = 4
    , torTargetLinks           = 5
    }
  
  -- |Options for allowing circuits that pass through this node.
  data TorRelayOptions = TorRelayOptions {
        -- |The port to listen on. By default, this is 9374, but there are
        -- compelling reasons to have it be some other wel-known port, like
        -- 80.
        torOnionPort :: Word16
        -- |The nickname for this node. This is completely optional, but can
        -- be helpful in finding yourself in node lists.
      , torNickname  :: String
        -- |A contact email address. If not provided, we will either provide
        -- no email address or just include a junk address.
      , torContact   :: Maybe String
        -- |If you're setting up a number of nodes within the same operating
        -- environment, you might want to provide a "family" identifier. That way,
        -- those building circuits can limit what percentage of their hops might
        -- go through this group. A node can be a member of zero, one, or more
        -- families, thus the list.
      , torFamilies  :: [NodeFamily]
        -- |The maximum number of links from this node. Note that this should be
        -- greater than or equal to torTargetLinks if this node is also to be used
        -- as an entrance node.
      , torMaximumLinks :: Int
      }
  
  -- |A reasonable set of relay options. The onion port is set to 9374, the
  -- nickname is set to "", and no contact information is provided. These options
  -- set the maximum number of links to 50.
  defaultTorRelayOptions :: TorRelayOptions
  defaultTorRelayOptions  = TorRelayOptions {
      torOnionPort    = 9374
    , torNickname     = ""
    , torContact      = Nothing
    , torFamilies     = []
    , torMaximumLinks = 50
    }
  
  -- |Options for allowing circuits that end at this node.
  data TorExitOptions = TorExitOptions {
        -- |The rules for allowing or rejecting traffic leaving this node.
        torExitRules :: [ExitRule]
        -- |The ports to disallow (Left) or allow (Right) when forwarding
        -- IPv6 traffic.
      , torIPv6Policy :: Either [PortSpec] [PortSpec]
        -- |Set this flag if you want to allow single-hop exits. These are
        -- usually not advisable, but according to the spec they may be
        -- usefule for "specialized controllers desgined to support perspective
        -- access and such."
      , torAllowSingleHopExits :: Bool
      }
  
  -- |A reasonable default exit node options. This allows all outgoing
  -- traffic to ports 22 (SSH), 80 (HTTP), 443 (HTTPS), 465 (SMTPS), and
  -- 993 (IMAPS), and disallows single hop exits.
  defaultTorExitOptions :: TorExitOptions
  defaultTorExitOptions  = TorExitOptions {
      torExitRules = map (\ p -> ExitRuleAccept AddrSpecAll (PortSpecSingle p))
                         allowPorts
    , torIPv6Policy = Right (map PortSpecSingle allowPorts)
    , torAllowSingleHopExits = False
    }
   where allowPorts = [22, 80, 443, 465, 993]
  
  -- -----------------------------------------------------------------------------
  
  -- |If you like the output format of the default log function, but want to
  -- send it to your own output stream, this is the function for you! This
  -- function takes an outgoing logger and a string to log, and adds a nicely-
  -- formatted and easily-sortable timestamp to the front of it.
  --
  -- NOTE: The default value for the logger is (makeLogger putStrLn).
  makeLogger :: (String -> IO ()) -> String -> IO ()
  makeLogger out msg =
    do now <- dateCurrent
       out (timePrint timeFormat now ++ msg)
   where
     timeFormat = [Format_Text '[', Format_Year4, Format_Text '-', Format_Month2,
                   Format_Text '-', Format_Day2, Format_Text ' ', Format_Hour,
                   Format_Text ':', Format_Minute, Format_Text ']',
                   Format_Text ' ']
  
  
  