packages feed

domain-auth-0.2.4: Network/DomainAuth/DK/Parser.hs

{-# LANGUAGE OverloadedStrings #-}

module Network.DomainAuth.DK.Parser (
    parseDK,
) where

import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.List
import qualified Data.Map as M
import Data.Maybe
import Network.DomainAuth.DK.Types
import Network.DomainAuth.Mail

-- $setup
-- >>> import Text.Pretty.Simple
-- >>> import Data.ByteString.Char8 as BS8

-- | Parsing DomainKey-Signature:.
--
-- >>> :{
-- let dk = BS8.concat [
--                 "a=rsa-sha1; s=brisbane; d=football.example.com;\n"
--               , "  c=simple; q=dns;\n"
--               , "  b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ\n"
--               , "    VoG4ZHRNiYzR;"
--               ]
-- in pPrintNoColor $ parseDK dk
-- :}
-- Just
--     ( DK
--         { dkAlgorithm = DK_RSA_SHA1
--         , dkSignature = "dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR"
--         , dkCanonAlgo = DK_SIMPLE
--         , dkDomain0 = "football.example.com"
--         , dkFields = Nothing
--         , dkSelector0 = "brisbane"
--         }
--     )
parseDK :: RawFieldValue -> Maybe DK
parseDK val = toDK domkey
  where
    (ts, vs) = unzip $ parseTaggedValue val
    fs = map tagToSetter ts
    tagToSetter tag = fromMaybe (\_ mdk -> mdk) $ lookup (BS.head tag) dkTagDB
    pfs = zipWith ($) fs vs
    domkey = foldr ($) initialMDK pfs
    toDK mdk = do
        alg <- mdkAlgorithm mdk
        sig <- mdkSignature mdk
        cal <- mdkCanonAlgo mdk
        dom <- mdkDomain mdk
        sel <- mdkSelector mdk
        return
            DK
                { dkAlgorithm = alg
                , dkSignature = sig
                , dkCanonAlgo = cal
                , dkDomain0 = dom
                , dkFields = mdkFields mdk
                , dkSelector0 = sel
                }

data MDK = MDK
    { mdkAlgorithm :: Maybe DkAlgorithm
    , mdkSignature :: Maybe ByteString
    , mdkCanonAlgo :: Maybe DkCanonAlgo
    , mdkDomain :: Maybe ByteString
    , mdkFields :: Maybe DkFields
    , mdkSelector :: Maybe ByteString
    }
    deriving (Eq, Show)

initialMDK :: MDK
initialMDK =
    MDK
        { mdkAlgorithm = Just DK_RSA_SHA1
        , mdkSignature = Nothing
        , mdkCanonAlgo = Nothing
        , mdkDomain = Nothing
        , mdkFields = Nothing
        , mdkSelector = Nothing
        }

type DKSetter = ByteString -> MDK -> MDK

dkTagDB :: [(Char, DKSetter)]
dkTagDB =
    [ ('a', setDkAlgorithm)
    , ('b', setDkSignature)
    , ('c', setDkCanonAlgo)
    , ('d', setDkDomain)
    , ('h', setDkFields)
    , --  , ('q',setDkQuery)
      ('s', setDkSelector)
    ]

setDkAlgorithm :: DKSetter
setDkAlgorithm "rsa-sha1" dk = dk{mdkAlgorithm = Just DK_RSA_SHA1}
setDkAlgorithm _ _ = error "setDkAlgorithm"

setDkSignature :: DKSetter
setDkSignature sig dk = dk{mdkSignature = Just sig}

setDkCanonAlgo :: DKSetter
setDkCanonAlgo "simple" dk = dk{mdkCanonAlgo = Just DK_SIMPLE}
setDkCanonAlgo "nofws" dk = dk{mdkCanonAlgo = Just DK_NOFWS}
setDkCanonAlgo _ _ = error "setDkCanonAlgo"

setDkDomain :: DKSetter
setDkDomain dom dk = dk{mdkDomain = Just dom}

setDkFields :: DKSetter
setDkFields keys dk = dk{mdkFields = Just mx}
  where
    flds = BS.split ':' keys
    mx = foldl' func M.empty flds
    func m fld = M.insert fld True m

{-
setDkQuery :: DKSetter
setDkQuery "dns" dk = dk { mdkQuery = Just DK_DNS }
setDkQuery _ _      = error "setDkQuery"
-}

setDkSelector :: DKSetter
setDkSelector sel dk = dk{mdkSelector = Just sel}