-- | This module contains the 'DnsblSite' data type representing one
--- blacklist with its associated return codes and multiplier. For example,
+-- blacklist with its associated return codes and weight. For example,
-- in Postfix's main.cf you might have,
--
-- postscreen_dnsbl_sites = bl.mailspike.net=127.0.0.[2;10;11]*2, ...
--
-- Here, the 'Domain' is \"bl.mailspike.net\", the return code
--- pattern is \"127.0.0.[2;10;11]\", and the multiplier is \"2".
+-- pattern is \"127.0.0.[2;10;11]\", and the weight is \"2".
--
module DnsblSite
where
+import Text.Parsec (
+ ParseError,
+ (<|>),
+ char,
+ digit,
+ eof,
+ many1,
+ option,
+ parse,
+ string,
+ try,
+ unexpected )
+import Text.Parsec.String ( Parser )
+import Text.Read ( readMaybe )
+
+
import IPv4Pattern ( IPv4Pattern )
+newtype Weight = Weight Int deriving (Eq, Show)
+
+-- | Parse the weight multiplier at the end of a dnsbl_site.
+--
+-- ==== _Examples_
+--
+-- >>> import Text.Parsec ( parseTest )
+--
+-- Negative, zero, and positive integers are all supported:
+--
+-- >>> parseTest weight "*-5"
+-- Weight (-5)
+--
+-- >>> parseTest weight "*0"
+-- Weight 0
+--
+-- >>> parseTest weight "*17"
+-- Weight 17
+--
+-- A bare asterisk doesn't work:
+--
+-- >>> parseTest weight "*"
+-- parse error at (line 1, column 2):
+-- unexpected end of input
+-- expecting "-", "+" or digit
+--
+-- If the weight is empty, it defaults to @1@:
+--
+-- >>> parseTest weight ""
+-- Weight 1
+--
+-- But the default is only used if the weight is really empty,
+-- not if parsing simply fails:
+--
+-- >>> parseTest weight "*hello"
+-- parse error at (line 1, column 2):
+-- unexpected "h"
+-- expecting "-", "+" or digit
+--
+weight :: Parser Weight
+weight = try parse_weight <|> (eof >> return (Weight 1))
+ where
+ parse_weight = do
+ _ <- char '*'
+ sign <- (char '-') <|> (option '+' (char '+'))
+ w <- many1 digit
+ case ( readMaybe w :: Maybe Int ) of
+ -- If "many1 digit" gives us a list of digits, we should be able
+ -- to convert that to an Int! It will overflow rather than fail
+ -- if the input is too big/small, so it should really always
+ -- succeed.
+ Nothing -> unexpected "weight: readMaybe failed on a sequence of digits!"
+ Just k -> return $ Weight (if sign == '-' then negate k else k)
+
newtype Domain = Domain String
-newtype Multiplier = Multiplier Int
-data DnsblSite = DnsblSite Domain IPv4Pattern Multiplier
+data DnsblSite = DnsblSite Domain IPv4Pattern Weight
+