Account.hs 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. {-# LANGUAGE OverloadedStrings #-}
  2. -- Parser of email account
  3. module Data.SMTP.Parser.Account where
  4. import Data.SMTP.Types.Account
  5. import Data.Attoparsec.ByteString.Char8.Extras
  6. import Data.SMTP.Parser.Host
  7. import Control.Applicative ((<|>))
  8. import Data.Attoparsec.ByteString.Char8
  9. import qualified Data.ByteString as BS
  10. import qualified Data.ByteString.Lazy as LBS
  11. import qualified Data.ByteString.Search as Search
  12. import Data.ByteString (ByteString)
  13. import qualified Codec.Binary.UTF8.String as UTF8
  14. import qualified Data.Char as C
  15. import qualified Data.Word8 as W
  16. parseAccount :: Parser Account
  17. parseAccount = quotedAccount <|> escapedAccount <|> plainAccount
  18. quotedAccount :: Parser Account
  19. quotedAccount = do
  20. char '\"'
  21. a <- parseAccount
  22. char '\"'
  23. return a
  24. plainAccount :: Parser Account
  25. plainAccount = do
  26. (fa, AccountName a, h, ts) <- plainAddress
  27. if BS.null a
  28. then fail "empty account name"
  29. else return $ Account fa (PersonalName "") (AccountName a) h ts
  30. escapedAccount :: Parser Account
  31. escapedAccount = do
  32. n' <- takeTill (`elem` ("\"'<\r\n" :: String))
  33. let n = fst . BS.spanEnd W.isSpace $ n'
  34. char '<'
  35. (fa, a, h, ts) <- plainAddress
  36. char '>'
  37. return $ Account (BS.concat [n', "<", fa, ">"]) (PersonalName n) a h ts
  38. plainAddress :: Parser (ByteString, AccountName, HostName, [AccountTag])
  39. plainAddress = do
  40. (fa, a, ts) <- scanAccountName
  41. (h, hasAt) <-
  42. (do
  43. char '@'
  44. h <- scanHostName
  45. return (h, True)
  46. ) <|> return ("", False)
  47. return (if hasAt then BS.concat [fa, "@", h] else fa, a, HostName h, ts)
  48. scanAccountName :: Parser (ByteString, AccountName, [AccountTag])
  49. scanAccountName = do
  50. f <- quotedString '\\' " ][,:\\;<>\"\r\n@" "\r\n"
  51. let ff = Search.split "+" f
  52. case ff of
  53. [] -> return (f, normalizeA f, [])
  54. [_] -> return (f, normalizeA f, [])
  55. (a:aa) -> return (f, normalizeA a, map AccountTag aa)
  56. normalizeA :: ByteString -> AccountName
  57. normalizeA a = AccountName na
  58. where
  59. a' = LBS.unpack $ Search.replace ("([^)]*)"::ByteString) (""::ByteString) a
  60. a'' = UTF8.encode . map C.toLower . UTF8.decode $ a'
  61. na = BS.pack a''