Browse Source

Improve RFC 6121 (XMPP-IM) code.

Move the functionality from Network.Xmpp.IM.Message and *.Presence to Network.Xmpp.Stanza since it is not specific to RFC 6121.
Implement presence functionality of RFC 6121
Fix hslint errors and warning
master
Philipp Balzarek 13 years ago
parent
commit
570cdafd43
  1. 12
      pontarius-xmpp.cabal
  2. 10
      source/Network/Xmpp.hs
  3. 23
      source/Network/Xmpp/IM.hs
  4. 183
      source/Network/Xmpp/IM/Message.hs
  5. 113
      source/Network/Xmpp/IM/Presence.hs
  6. 52
      source/Network/Xmpp/IM/Roster.hs
  7. 76
      source/Network/Xmpp/Stanza.hs
  8. 39
      source/Network/Xmpp/Utilities.hs

12
pontarius-xmpp.cabal

@ -61,15 +61,20 @@ Library @@ -61,15 +61,20 @@ Library
, xml-conduit >=1.0
, xml-picklers >=0.3.2
Exposed-modules: Network.Xmpp
, Network.Xmpp.IM
, Network.Xmpp.Internal
Other-modules: Network.Xmpp.Concurrent
, Network.Xmpp.Concurrent.Types
, Network.Xmpp.Concurrent.Basic
, Network.Xmpp.Concurrent.IQ
, Network.Xmpp.Concurrent.Message
, Network.Xmpp.Concurrent.Monad
, Network.Xmpp.Concurrent.Presence
, Network.Xmpp.Concurrent.Threads
, Network.Xmpp.Concurrent.Monad
, Network.Xmpp.Concurrent.Types
, Network.Xmpp.IM.Message
, Network.Xmpp.IM.Presence
, Network.Xmpp.IM.Roster
, Network.Xmpp.IM.Roster.Types
, Network.Xmpp.Marshal
, Network.Xmpp.Sasl
, Network.Xmpp.Sasl.Common
@ -79,12 +84,11 @@ Library @@ -79,12 +84,11 @@ Library
, Network.Xmpp.Sasl.Mechanisms.Scram
, Network.Xmpp.Sasl.StringPrep
, Network.Xmpp.Sasl.Types
, Network.Xmpp.Stanza
, Network.Xmpp.Stream
, Network.Xmpp.Tls
, Network.Xmpp.Types
, Network.Xmpp.Utilities
, Network.Xmpp.IM.Roster
, Network.Xmpp.IM.Roster.Types
GHC-Options: -Wall
Source-Repository head

10
source/Network/Xmpp.hs

@ -82,6 +82,7 @@ module Network.Xmpp @@ -82,6 +82,7 @@ module Network.Xmpp
-- occur in a system such as email. It is not to be confused with
-- /instant messaging/ which is handled in the 'Network.Xmpp.IM' module
, Message(..)
, message
, MessageError(..)
, MessageType(..)
-- *** Creating
@ -103,6 +104,12 @@ module Network.Xmpp @@ -103,6 +104,12 @@ module Network.Xmpp
, PresenceType(..)
, PresenceError(..)
-- *** Creating
, presence
, presenceOffline
, presenceOnline
, presenceSubscribe
, presenceSubscribed
, presenceUnsubscribe
, presTo
-- *** Sending
-- | Sends a presence stanza. In general, the presence stanza should have no
@ -157,7 +164,8 @@ module Network.Xmpp @@ -157,7 +164,8 @@ module Network.Xmpp
) where
import Network.Xmpp.Concurrent
import Network.Xmpp.Utilities
import Network.Xmpp.Sasl
import Network.Xmpp.Sasl.Types
import Network.Xmpp.Stanza
import Network.Xmpp.Types
import Network.Xmpp.Utilities

23
source/Network/Xmpp/IM.hs

@ -1,14 +1,19 @@ @@ -1,14 +1,19 @@
-- | RFC 6121: Instant Messaging and Presence
--
module Network.Xmpp.IM
( -- * Instant Messages
subject
, thread
, body
, bodies
, newIM
, simpleIM
, answerIM
-- * Presence
, module Network.Xmpp.IM.Presence
MessageBody(..)
, MessageThread(..)
, MessageSubject(..)
, instantMessage
, getIM
, withIM
-- * Presence
, ShowStatus(..)
, IMPresence(..)
, imPresence
, getIMPresence
, withIMPresence
-- * Roster
, Roster(..)
, Item(..)

183
source/Network/Xmpp/IM/Message.hs

@ -1,119 +1,64 @@ @@ -1,119 +1,64 @@
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK hide #-}
module Network.Xmpp.IM.Message
where
module Network.Xmpp.IM.Message where
import Control.Applicative ((<$>))
import Data.Maybe (maybeToList, listToMaybe)
import Data.Text (Text)
import Data.XML.Pickle
import Data.XML.Types
import Network.Xmpp.Marshal
import Network.Xmpp.Types
import Network.Xmpp.Stanza
import Data.List
import Data.Function
data MessageBody = MessageBody { bodyLang :: (Maybe LangTag)
data MessageBody = MessageBody { bodyLang :: Maybe LangTag
, bodyContent :: Text
}
data MessageThread = MessageThread { theadID :: Text
, threadParent :: (Maybe Text)
data MessageThread = MessageThread { theadID :: Text
, threadParent :: Maybe Text
}
data MessageSubject = MessageSubject { subjectLang :: (Maybe LangTag)
data MessageSubject = MessageSubject { subjectLang :: Maybe LangTag
, subjectContent :: Text
}
xpMessageSubject :: PU [Element] MessageSubject
xpMessageSubject = xpUnliftElems .
xpWrap (\(l, s) -> MessageSubject l s)
(\(MessageSubject l s) -> (l,s))
$ xpElem "{jabber:client}subject" xpLangTag $ xpContent xpId
-- | The instant message (IM) specific part of a message.
data InstantMessage = InstantMessage { imThread :: Maybe MessageThread
, imSubject :: [MessageSubject]
, imBody :: [MessageBody]
}
xpMessageBody :: PU [Element] MessageBody
xpMessageBody = xpUnliftElems .
xpWrap (\(l, s) -> MessageBody l s)
(\(MessageBody l s) -> (l,s))
$ xpElem "{jabber:client}body" xpLangTag $ xpContent xpId
instantMessage :: InstantMessage
instantMessage = InstantMessage { imThread = Nothing
, imSubject = []
, imBody = []
}
xpMessageThread :: PU [Element] MessageThread
xpMessageThread = xpUnliftElems
. xpWrap (\(t, p) -> MessageThread p t)
(\(MessageThread p t) -> (t,p))
$ xpElem "{jabber:client}thread"
(xpAttrImplied "parent" xpId)
(xpContent xpId)
-- | Get the IM specific parts of a message. Returns 'Nothing' when the received
-- payload is not valid IM data.
getIM :: Message -> Maybe InstantMessage
getIM im = either (const Nothing) Just . unpickle xpIM $ messagePayload im
-- | Get the subject elements of a message (if any). Messages may
-- contain multiple subjects if each of them has a distinct xml:lang
-- attribute
subject :: Message -> [MessageSubject]
subject m = ms
where
-- xpFindMatches will _always_ return Right
Right ms = unpickle (xpFindMatches xpMessageSubject) $ messagePayload m
-- | Get the thread elements of a message (if any). The thread of a
-- message is considered opaque, that is, no meaning, other than it's
-- literal identity, may be derived from it and it is not human
-- readable
thread :: Message -> Maybe MessageThread
thread m = ms
where
-- xpFindMatches will _always_ return Right
Right ms = unpickle (xpOption xpMessageThread) $ messagePayload m
-- | Get the body elements of a message (if any). Messages may contain
-- multiple bodies if each of them has a distinct xml:lang attribute
bodies :: Message -> [MessageBody]
bodies m = ms
where
-- xpFindMatches will _always_ return Right
Right ms = unpickle (xpFindMatches xpMessageBody) $ messagePayload m
-- | Return the first body element, regardless of it's language.
body :: Message -> Maybe Text
body m = bodyContent <$> listToMaybe (bodies m)
-- | Generate a new instant message
newIM
:: Jid
-> Maybe StanzaID
-> Maybe LangTag
-> MessageType
-> Maybe MessageSubject
-> Maybe MessageThread
-> Maybe MessageBody
-> [Element]
-> Message
newIM t i lang tp sbj thrd bdy payload = Message
{ messageID = i
, messageFrom = Nothing
, messageTo = Just t
, messageLangTag = lang
, messageType = tp
, messagePayload = concat $
maybeToList (pickle xpMessageSubject <$> sbj)
++ maybeToList (pickle xpMessageThread <$> thrd)
++ maybeToList (pickle xpMessageBody <$> bdy)
++ [payload]
}
sanitizeIM :: InstantMessage -> InstantMessage
sanitizeIM im = im{imBody = nubBy ((==) `on` bodyLang) $ imBody im}
-- | Append IM data to a message
withIM :: Message -> InstantMessage -> Message
withIM m im = m{ messagePayload = messagePayload m
++ pickleTree xpIM (sanitizeIM im) }
imToElements :: InstantMessage -> [Element]
imToElements im = pickle xpIM (sanitizeIM im)
-- | Generate a simple message
simpleIM :: Jid -- ^ recipient
-> Text -- ^ body
-> Message
simpleIM t bd = newIM
t
Nothing
Nothing
Normal
Nothing
Nothing
(Just $ MessageBody Nothing bd)
[]
simpleIM to bd = withIM message{messageTo = Just to}
instantMessage{imBody = [MessageBody Nothing bd]}
-- | Generate an answer from a received message. The recepient is
-- taken from the original sender, the sender is set to Nothing,
@ -121,17 +66,47 @@ simpleIM t bd = newIM @@ -121,17 +66,47 @@ simpleIM t bd = newIM
-- thread are inherited, the remaining payload is replaced by the
-- given one.
--
-- If multiple message bodies are given they must have different language tags
answerIM :: [MessageBody] -> [Element] -> Message -> Message
answerIM bd payload msg = Message
{ messageID = messageID msg
, messageFrom = Nothing
, messageTo = messageFrom msg
, messageLangTag = messageLangTag msg
, messageType = messageType msg
, messagePayload = concat $
(pickle xpMessageSubject <$> subject msg)
++ maybeToList (pickle xpMessageThread <$> thread msg)
++ (pickle xpMessageBody <$> bd)
++ [payload]
}
-- If multiple message bodies are given they MUST have different language tags
answerIM :: [MessageBody] -> Message -> Maybe Message
answerIM bd msg = case getIM msg of
Nothing -> Nothing
Just im -> Just $ flip withIM (im{imBody = bd}) $
message { messageID = messageID msg
, messageFrom = Nothing
, messageTo = messageFrom msg
, messageLangTag = messageLangTag msg
, messageType = messageType msg
}
--------------------------
-- Picklers --------------
--------------------------
xpIM :: PU [Element] InstantMessage
xpIM = xpWrap (\(t, s, b) -> InstantMessage t s b)
(\(InstantMessage t s b) -> (t, s, b)) $
xp3Tuple
xpMessageThread
xpMessageSubject
xpMessageBody
xpMessageSubject :: PU [Element] [MessageSubject]
xpMessageSubject = xpUnliftElems .
xpWrap (map $ \(l, s) -> MessageSubject l s)
(map $ \(MessageSubject l s) -> (l,s))
$ xpElems "{jabber:client}subject" xpLangTag $ xpContent xpId
xpMessageBody :: PU [Element] [MessageBody]
xpMessageBody = xpUnliftElems .
xpWrap (map $ \(l, s) -> MessageBody l s)
(map $ \(MessageBody l s) -> (l,s))
$ xpElems "{jabber:client}body" xpLangTag $ xpContent xpId
xpMessageThread :: PU [Element] (Maybe MessageThread)
xpMessageThread = xpUnliftElems
. xpOption
. xpWrap (\(t, p) -> MessageThread p t)
(\(MessageThread p t) -> (t,p))
$ xpElem "{jabber:client}thread"
(xpAttrImplied "parent" xpId)
(xpContent xpId)

113
source/Network/Xmpp/IM/Presence.hs

@ -1,75 +1,66 @@ @@ -1,75 +1,66 @@
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
module Network.Xmpp.IM.Presence where
import Network.Xmpp.Types
import Data.Text (Text)
import Data.XML.Pickle
import Data.XML.Types
import Network.Xmpp.Types
-- | An empty presence.
presence :: Presence
presence = Presence { presenceID = Nothing
, presenceFrom = Nothing
, presenceTo = Nothing
, presenceLangTag = Nothing
, presenceType = Nothing
, presencePayload = []
}
data ShowStatus = StatusAway
| StatusChat
| StatusDnd
| StatusXa
-- | Request subscription with an entity.
presenceSubscribe :: Jid -> Presence
presenceSubscribe to = presence { presenceTo = Just to
, presenceType = Just Subscribe
}
instance Show ShowStatus where
show StatusAway = "away"
show StatusChat = "chat"
show StatusDnd = "dnd"
show StatusXa = "xa"
-- | Is presence a subscription request?
isPresenceSubscribe :: Presence -> Bool
isPresenceSubscribe pres = presenceType pres == (Just Subscribe)
instance Read ShowStatus where
readsPrec _ "away" = [(StatusAway, "")]
readsPrec _ "chat" = [(StatusChat, "")]
readsPrec _ "dnd" = [(StatusDnd , "")]
readsPrec _ "xa" = [(StatusXa , "")]
readsPrec _ _ = []
-- | Approve a subscripton of an entity.
presenceSubscribed :: Jid -> Presence
presenceSubscribed to = presence { presenceTo = Just to
, presenceType = Just Subscribed
}
data IMPresence = IMP { showStatus :: Maybe ShowStatus
, status :: Maybe Text
, priority :: Maybe Int
}
-- | Is presence a subscription approval?
isPresenceSubscribed :: Presence -> Bool
isPresenceSubscribed pres = presenceType pres == (Just Subscribed)
imPresence :: IMPresence
imPresence = IMP { showStatus = Nothing
, status = Nothing
, priority = Nothing
}
-- | End a subscription with an entity.
presenceUnsubscribe :: Jid -> Presence
presenceUnsubscribe to = presence { presenceTo = Just to
, presenceType = Just Unsubscribed
}
-- | Is presence an unsubscription request?
isPresenceUnsubscribe :: Presence -> Bool
isPresenceUnsubscribe pres = presenceType pres == (Just Unsubscribe)
getIMPresence :: Presence -> Maybe IMPresence
getIMPresence pres = case unpickle xpIMPresence (presencePayload pres) of
Left _ -> Nothing
Right r -> Just r
-- | Signal to the server that the client is available for communication.
presenceOnline :: Presence
presenceOnline = presence
withIMPresence :: IMPresence -> Presence -> Presence
withIMPresence imPres pres = pres{presencePayload = presencePayload pres
++ pickleTree xpIMPresence
imPres}
-- | Signal to the server that the client is no longer available for
-- communication.
presenceOffline :: Presence
presenceOffline = presence {presenceType = Just Unavailable}
--
-- Picklers
--
---- Change your status
--status
-- :: Maybe Text -- ^ Status message
-- -> Maybe ShowType -- ^ Status Type
-- -> Maybe Int -- ^ Priority
-- -> Presence
--status txt showType prio = presence { presenceShowType = showType
-- , presencePriority = prio
-- , presenceStatus = txt
-- }
-- | Set the current availability status. This implicitly sets the client's
-- status online.
--presenceAvail :: ShowType -> Presence
--presenceAvail showType = status Nothing (Just showType) Nothing
-- | Set the current status message. This implicitly sets the client's status
-- online.
--presenceMessage :: Text -> Presence
--presenceMessage txt = status (Just txt) Nothing Nothing
xpIMPresence :: PU [Element] IMPresence
xpIMPresence = xpUnliftElems $
xpWrap (\(s, st, p) -> IMP s st p)
(\(IMP s st p) -> (s, st, p)) $
xp3Tuple
(xpOption $ xpElemNodes "{jabber:client}show"
(xpContent xpPrim))
(xpOption $ xpElemNodes "{jabber:client}status"
(xpContent xpText))
(xpOption $ xpElemNodes "{jabber:client}priority"
(xpContent xpPrim))

52
source/Network/Xmpp/IM/Roster.hs

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings #-}
@ -9,7 +8,7 @@ import Control.Concurrent.STM @@ -9,7 +8,7 @@ import Control.Concurrent.STM
import Control.Monad
import Data.List (nub)
import qualified Data.Map.Strict as Map
import Data.Maybe (isJust)
import Data.Maybe (isJust, fromMaybe)
import Data.Text (Text)
import Data.XML.Pickle
import Data.XML.Types
@ -60,9 +59,11 @@ rosterRemove j sess = do @@ -60,9 +59,11 @@ rosterRemove j sess = do
IQResponseResult IQResult{} -> return True
_ -> return False
-- | Retrieve the current Roster state
getRoster :: Session -> IO Roster
getRoster session = atomically $ readTVar (rosterRef session)
-- | Get the initial roster / refresh the roster. You don't need to call this on your own
initRoster :: Session -> IO ()
initRoster session = do
oldRoster <- getRoster session
@ -74,26 +75,25 @@ initRoster session = do @@ -74,26 +75,25 @@ initRoster session = do
Just roster -> atomically $ writeTVar (rosterRef session) roster
handleRoster :: TVar Roster -> TChan Stanza -> Stanza -> IO Bool
handleRoster ref outC sta = do
case sta of
IQRequestS (iqr@IQRequest{iqRequestPayload =
iqb@Element{elementName = en}})
| nameNamespace en == Just "jabber:iq:roster" -> do
case iqRequestFrom iqr of
Just _from -> return True -- Don't handle roster pushes from
-- unauthorized sources
Nothing -> case unpickleElem xpQuery iqb of
Right Query{ queryVer = v
, queryItems = [update]
} -> do
handleUpdate v update
atomically . writeTChan outC $ result iqr
return False
_ -> do
errorM "Pontarius.Xmpp" "Invalid roster query"
atomically . writeTChan outC $ badRequest iqr
return False
_ -> return True
handleRoster ref outC sta = case sta of
IQRequestS (iqr@IQRequest{iqRequestPayload =
iqb@Element{elementName = en}})
| nameNamespace en == Just "jabber:iq:roster" -> do
case iqRequestFrom iqr of
Just _from -> return True -- Don't handle roster pushes from
-- unauthorized sources
Nothing -> case unpickleElem xpQuery iqb of
Right Query{ queryVer = v
, queryItems = [update]
} -> do
handleUpdate v update
atomically . writeTChan outC $ result iqr
return False
_ -> do
errorM "Pontarius.Xmpp" "Invalid roster query"
atomically . writeTChan outC $ badRequest iqr
return False
_ -> return True
where
handleUpdate v' update = atomically $ modifyTVar ref $ \(Roster v is) ->
Roster (v' `mplus` v) $ case qiSubscription update of
@ -119,7 +119,7 @@ retrieveRoster oldRoster sess = do @@ -119,7 +119,7 @@ retrieveRoster oldRoster sess = do
return Nothing
Right ros' -> return . Just $ toRoster ros'
IQResponseResult (IQResult{iqResultPayload = Nothing}) -> do
return $ oldRoster
return oldRoster
-- sever indicated that no roster updates are necessary
IQResponseTimeout -> do
errorM "Pontarius.Xmpp.Roster" "getRoster: request timed out"
@ -134,11 +134,11 @@ retrieveRoster oldRoster sess = do @@ -134,11 +134,11 @@ retrieveRoster oldRoster sess = do
is)
toItem :: QueryItem -> Item
toItem qi = Item { approved = maybe False id (qiApproved qi)
toItem qi = Item { approved = fromMaybe False (qiApproved qi)
, ask = qiAsk qi
, jid = qiJid qi
, name = qiName qi
, subscription = maybe None id (qiSubscription qi)
, subscription = fromMaybe None (qiSubscription qi)
, groups = nub $ qiGroups qi
}
@ -161,7 +161,7 @@ xpItems = xpWrap (map (\((app_, ask_, jid_, name_, sub_), groups_) -> @@ -161,7 +161,7 @@ xpItems = xpWrap (map (\((app_, ask_, jid_, name_, sub_), groups_) ->
xpElems "{jabber:iq:roster}item"
(xp5Tuple
(xpAttribute' "approved" xpBool)
(xpWrap (maybe False (const True))
(xpWrap isJust
(\p -> if p then Just () else Nothing) $
xpOption $ xpAttribute_ "ask" "subscribe")
(xpAttribute "jid" xpPrim)

76
source/Network/Xmpp/Stanza.hs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_HADDOCK hide #-}
-- | Stanza related functions and constants
--
module Network.Xmpp.Stanza where
import Data.XML.Types
import Network.Xmpp.Types
-- | An empty message
message :: Message
message = Message { messageID = Nothing
, messageFrom = Nothing
, messageTo = Nothing
, messageLangTag = Nothing
, messageType = Normal
, messagePayload = []
}
-- | An empty presence.
presence :: Presence
presence = Presence { presenceID = Nothing
, presenceFrom = Nothing
, presenceTo = Nothing
, presenceLangTag = Nothing
, presenceType = Nothing
, presencePayload = []
}
-- | Request subscription with an entity.
presenceSubscribe :: Jid -> Presence
presenceSubscribe to = presence { presenceTo = Just to
, presenceType = Just Subscribe
}
-- | Approve a subscripton of an entity.
presenceSubscribed :: Jid -> Presence
presenceSubscribed to = presence { presenceTo = Just to
, presenceType = Just Subscribed
}
-- | End a subscription with an entity.
presenceUnsubscribe :: Jid -> Presence
presenceUnsubscribe to = presence { presenceTo = Just to
, presenceType = Just Unsubscribed
}
-- | Signal to the server that the client is available for communication.
presenceOnline :: Presence
presenceOnline = presence
-- | Signal to the server that the client is no longer available for
-- communication.
presenceOffline :: Presence
presenceOffline = presence {presenceType = Just Unavailable}
-- | Produce an answer message with the given payload, switching the "from" and
-- "to" attributes in the original message. Produces a 'Nothing' value of the
-- provided message message has no from attribute.
answerMessage :: Message -> [Element] -> Maybe Message
answerMessage Message{messageFrom = Just frm, ..} payload =
Just Message{ messageFrom = messageTo
, messageID = Nothing
, messageTo = Just frm
, messagePayload = payload
, ..
}
answerMessage _ _ = Nothing
-- | Add a recipient to a presence notification.
presTo :: Presence -> Jid -> Presence
presTo pres to = pres{presenceTo = Just to}

39
source/Network/Xmpp/Utilities.hs

@ -1,13 +1,10 @@ @@ -1,13 +1,10 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_HADDOCK hide #-}
module Network.Xmpp.Utilities
( presTo
, message
, answerMessage
, openElementToEvents
( openElementToEvents
, renderOpenElement
, renderElement
, checkHostName
@ -23,39 +20,11 @@ import qualified Data.Text as Text @@ -23,39 +20,11 @@ import qualified Data.Text as Text
import Data.Text(Text)
import qualified Data.Text.Encoding as Text
import Data.XML.Types
import Network.Xmpp.Types
import Prelude
import System.IO.Unsafe(unsafePerformIO)
import qualified Text.XML.Stream.Render as TXSR
import Text.XML.Unresolved as TXU
-- | Add a recipient to a presence notification.
presTo :: Presence -> Jid -> Presence
presTo pres to = pres{presenceTo = Just to}
-- | An empty message.
message :: Message
message = Message { messageID = Nothing
, messageFrom = Nothing
, messageTo = Nothing
, messageLangTag = Nothing
, messageType = Normal
, messagePayload = []
}
-- | Produce an answer message with the given payload, switching the "from" and
-- "to" attributes in the original message. Produces a 'Nothing' value of the
-- provided message message has no from attribute.
answerMessage :: Message -> [Element] -> Maybe Message
answerMessage Message{messageFrom = Just frm, ..} payload =
Just Message{ messageFrom = messageTo
, messageID = Nothing
, messageTo = Just frm
, messagePayload = payload
, ..
}
answerMessage _ _ = Nothing
openElementToEvents :: Element -> [Event]
openElementToEvents (Element name as ns) = EventBeginElement name as : goN ns []
where
@ -85,7 +54,7 @@ renderElement e = Text.encodeUtf8 . Text.concat . unsafePerformIO @@ -85,7 +54,7 @@ renderElement e = Text.encodeUtf8 . Text.concat . unsafePerformIO
-- | Validates the hostname string in accordance with RFC 1123.
checkHostName :: Text -> Maybe Text
checkHostName t = do
checkHostName t =
eitherToMaybeHostName $ AP.parseOnly hostnameP t
where
eitherToMaybeHostName = either (const Nothing) Just
@ -105,6 +74,6 @@ hostnameP = do @@ -105,6 +74,6 @@ hostnameP = do
<|> do
_ <- AP.satisfy (== '.')
r <- hostnameP
if (Text.length label) + 1 + (Text.length r) > 255
if Text.length label + 1 + Text.length r > 255
then fail "Hostname too long."
else return $ Text.concat [label, Text.pack ".", r]

Loading…
Cancel
Save