Browse Source

Merge branch 'master' of github.com:pontarius/pontarius-xmpp into upstream

master
Philipp Balzarek 13 years ago
parent
commit
932e8e0314
  1. 49
      pontarius-xmpp.cabal
  2. 9
      source/Network/Xmpp.hs
  3. 4
      source/Network/Xmpp/Concurrent/IQ.hs
  4. 34
      source/Network/Xmpp/IM/Presence.hs
  5. 21
      source/Network/Xmpp/IM/Roster.hs
  6. 17
      source/Network/Xmpp/IM/Roster/Types.hs
  7. 233
      source/Network/Xmpp/Marshal.hs
  8. 32
      source/Network/Xmpp/Sasl/Common.hs
  9. 277
      source/Network/Xmpp/Types.hs
  10. 11
      source/Network/Xmpp/Utilities.hs

49
pontarius-xmpp.cabal

@ -28,8 +28,10 @@ Extra-Source-Files: README.md
Library Library
hs-source-dirs: source hs-source-dirs: source
Exposed: True Exposed: True
-- The only different between the below two blocks is that the first one caps -- The only different between the below two blocks is that the first one caps
-- the range for the `bytestring' package. -- the range for the `bytestring' package, and that the second one includes
-- `template-haskell' for GHC 7.6.1 and above.
If impl(ghc ==7.0.1) If impl(ghc ==7.0.1)
{ {
Build-Depends: attoparsec >=0.10.0.3 Build-Depends: attoparsec >=0.10.0.3
@ -37,7 +39,7 @@ Library
, base64-bytestring >=0.1.0.0 , base64-bytestring >=0.1.0.0
, binary >=0.4.1 , binary >=0.4.1
, bytestring >=0.9.1.9 && <=0.9.2.1 , bytestring >=0.9.1.9 && <=0.9.2.1
, conduit >=0.5 , conduit >=1.0.1
, containers >=0.5.0.0 , containers >=0.5.0.0
, crypto-api >=0.9 , crypto-api >=0.9
, crypto-random-api >=0.2 , crypto-random-api >=0.2
@ -56,7 +58,6 @@ Library
, split >=0.1.2.3 , split >=0.1.2.3
, stm >=2.1.2.1 , stm >=2.1.2.1
, stringprep >=0.1.3 , stringprep >=0.1.3
, template-haskell >=2.5
, text >=0.11.1.5 , text >=0.11.1.5
, tls >=1.1.0 , tls >=1.1.0
, tls-extra >=0.5.0 , tls-extra >=0.5.0
@ -66,14 +67,16 @@ Library
, xml-conduit >=1.0 , xml-conduit >=1.0
, xml-picklers >=0.3.3 , xml-picklers >=0.3.3
} }
else Else
{
If impl(ghc >=7.6.1)
{ {
Build-Depends: attoparsec >=0.10.0.3 Build-Depends: attoparsec >=0.10.0.3
, base >4 && <5 , base >4 && <5
, base64-bytestring >=0.1.0.0 , base64-bytestring >=0.1.0.0
, binary >=0.4.1 , binary >=0.4.1
, bytestring >=0.9.1.9 , bytestring >=0.9.1.9
, conduit >=0.5 , conduit >=1.0.1
, containers >=0.5.0.0 , containers >=0.5.0.0
, crypto-api >=0.9 , crypto-api >=0.9
, crypto-random-api >=0.2 , crypto-random-api >=0.2
@ -102,6 +105,42 @@ Library
, xml-conduit >=1.0 , xml-conduit >=1.0
, xml-picklers >=0.3.3 , xml-picklers >=0.3.3
} }
Else
{
Build-Depends: attoparsec >=0.10.0.3
, base >4 && <5
, base64-bytestring >=0.1.0.0
, binary >=0.4.1
, bytestring >=0.9.1.9
, conduit >=1.0.1
, containers >=0.5.0.0
, crypto-api >=0.9
, crypto-random-api >=0.2
, cryptohash >=0.6.1
, cryptohash-cryptoapi >=0.1
, data-default >=0.2
, dns >=0.3.0
, hslogger >=1.1.0
, iproute >=1.2.4
, lifted-base >=0.1.0.1
, mtl >=2.0.0.0
, network >=2.4.1.0
, pureMD5 >=2.1.2.1
, resourcet >=0.3.0
, random >=1.0.0.0
, split >=0.1.2.3
, stm >=2.1.2.1
, stringprep >=0.1.3
, text >=0.11.1.5
, tls >=1.1.0
, tls-extra >=0.5.0
, transformers >=0.2.2.0
, void >=0.5.5
, xml-types >=0.3.1
, xml-conduit >=1.0
, xml-picklers >=0.3.3
}
}
Exposed-modules: Network.Xmpp Exposed-modules: Network.Xmpp
, Network.Xmpp.IM , Network.Xmpp.IM
, Network.Xmpp.Internal , Network.Xmpp.Internal

9
source/Network/Xmpp.hs

@ -21,7 +21,7 @@
-- For low-level access to Pontarius XMPP, see the "Network.Xmpp.Internal" -- For low-level access to Pontarius XMPP, see the "Network.Xmpp.Internal"
-- module. -- module.
{-# LANGUAGE NoMonomorphismRestriction, OverloadedStrings #-} {-# LANGUAGE CPP, NoMonomorphismRestriction, OverloadedStrings #-}
module Network.Xmpp module Network.Xmpp
( -- * Session management ( -- * Session management
@ -46,7 +46,9 @@ module Network.Xmpp
-- for addressing entities in the network. It is somewhat similar to an e-mail -- for addressing entities in the network. It is somewhat similar to an e-mail
-- address, but contains three parts instead of two. -- address, but contains three parts instead of two.
, Jid , Jid
#if __GLASGOW_HASKELL__ >= 706
, jidQ , jidQ
#endif
, isBare , isBare
, isFull , isFull
, jidFromText , jidFromText
@ -169,7 +171,10 @@ module Network.Xmpp
-- * Threads -- * Threads
, dupSession , dupSession
-- * Miscellaneous -- * Miscellaneous
, LangTag(..) , LangTag
, langTagFromText
, langTagToText
, parseLangTag
, XmppFailure(..) , XmppFailure(..)
, StreamErrorInfo(..) , StreamErrorInfo(..)
, StreamErrorCondition(..) , StreamErrorCondition(..)

4
source/Network/Xmpp/Concurrent/IQ.hs

@ -54,7 +54,7 @@ sendIQ timeOut to tp lang body session = do -- TODO: Add timeout
return () return ()
-- | Like 'sendIQ', but waits for the answer IQ. Times out after 3 seconds -- | Like 'sendIQ', but waits for the answer IQ. Times out after 30 seconds
sendIQ' :: Maybe Jid sendIQ' :: Maybe Jid
-> IQRequestType -> IQRequestType
-> Maybe LangTag -> Maybe LangTag
@ -62,7 +62,7 @@ sendIQ' :: Maybe Jid
-> Session -> Session
-> IO (Maybe IQResponse) -> IO (Maybe IQResponse)
sendIQ' to tp lang body session = do sendIQ' to tp lang body session = do
ref <- sendIQ (Just 3000000) to tp lang body session ref <- sendIQ (Just 30000000) to tp lang body session
maybe (return Nothing) (fmap Just . atomically . takeTMVar) ref maybe (return Nothing) (fmap Just . atomically . takeTMVar) ref

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

@ -13,20 +13,7 @@ import Network.Xmpp.Types
data ShowStatus = StatusAway data ShowStatus = StatusAway
| StatusChat | StatusChat
| StatusDnd | StatusDnd
| StatusXa | StatusXa deriving (Read, Show)
instance Show ShowStatus where
show StatusAway = "away"
show StatusChat = "chat"
show StatusDnd = "dnd"
show StatusXa = "xa"
instance Read ShowStatus where
readsPrec _ "away" = [(StatusAway, "")]
readsPrec _ "chat" = [(StatusChat, "")]
readsPrec _ "dnd" = [(StatusDnd , "")]
readsPrec _ "xa" = [(StatusXa , "")]
readsPrec _ _ = []
data IMPresence = IMP { showStatus :: Maybe ShowStatus data IMPresence = IMP { showStatus :: Maybe ShowStatus
, status :: Maybe Text , status :: Maybe Text
@ -65,8 +52,25 @@ xpIMPresence = xpUnliftElems .
xpClean $ xpClean $
xp3Tuple xp3Tuple
(xpOption $ xpElemNodes "{jabber:client}show" (xpOption $ xpElemNodes "{jabber:client}show"
(xpContent xpPrim)) (xpContent xpShow))
(xpOption $ xpElemNodes "{jabber:client}status" (xpOption $ xpElemNodes "{jabber:client}status"
(xpContent xpText)) (xpContent xpText))
(xpOption $ xpElemNodes "{jabber:client}priority" (xpOption $ xpElemNodes "{jabber:client}priority"
(xpContent xpPrim)) (xpContent xpPrim))
xpShow :: PU Text ShowStatus
xpShow = ("xpShow", "") <?>
xpPartial ( \input -> case showStatusFromText input of
Nothing -> Left "Could not parse show status."
Just j -> Right j)
showStatusToText
where
showStatusFromText "away" = Just StatusAway
showStatusFromText "chat" = Just StatusChat
showStatusFromText "dnd" = Just StatusDnd
showStatusFromText "xa" = Just StatusXa
showStatusFromText _ = Nothing
showStatusToText StatusAway = "away"
showStatusToText StatusChat = "chat"
showStatusToText StatusDnd = "dnd"
showStatusToText StatusXa = "xa"

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

@ -177,7 +177,7 @@ xpItems = xpWrap (map (\((app_, ask_, jid_, name_, sub_), groups_) ->
xpOption $ xpAttribute_ "ask" "subscribe") xpOption $ xpAttribute_ "ask" "subscribe")
(xpAttribute "jid" xpJid) (xpAttribute "jid" xpJid)
(xpAttribute' "name" xpText) (xpAttribute' "name" xpText)
(xpAttribute' "subscription" xpPrim) (xpAttribute' "subscription" xpSubscription)
) )
(xpFindMatches $ xpElemText "{jabber:iq:roster}group") (xpFindMatches $ xpElemText "{jabber:iq:roster}group")
@ -187,3 +187,22 @@ xpQuery = xpWrap (\(ver_, items_) -> Query ver_ items_ )
xpElem "{jabber:iq:roster}query" xpElem "{jabber:iq:roster}query"
(xpAttribute' "ver" xpText) (xpAttribute' "ver" xpText)
xpItems xpItems
xpSubscription :: PU Text Subscription
xpSubscription = ("xpSubscription", "") <?>
xpPartial ( \input -> case subscriptionFromText input of
Nothing -> Left "Could not parse subscription."
Just j -> Right j)
subscriptionToText
where
subscriptionFromText "none" = Just None
subscriptionFromText "to" = Just To
subscriptionFromText "from" = Just From
subscriptionFromText "both" = Just Both
subscriptionFromText "remove" = Just Remove
subscriptionFromText _ = Nothing
subscriptionToText None = "none"
subscriptionToText To = "to"
subscriptionToText From = "from"
subscriptionToText Both = "both"
subscriptionToText Remove = "remove"

17
source/Network/Xmpp/IM/Roster/Types.hs

@ -4,22 +4,7 @@ import qualified Data.Map as Map
import Data.Text (Text) import Data.Text (Text)
import Network.Xmpp.Types import Network.Xmpp.Types
data Subscription = None | To | From | Both | Remove deriving Eq data Subscription = None | To | From | Both | Remove deriving (Eq, Read, Show)
instance Show Subscription where
show None = "none"
show To = "to"
show From = "from"
show Both = "both"
show Remove = "remove"
instance Read Subscription where
readsPrec _ "none" = [(None ,"")]
readsPrec _ "to" = [(To ,"")]
readsPrec _ "from" = [(From ,"")]
readsPrec _ "both" = [(Both ,"")]
readsPrec _ "remove" = [(Remove ,"")]
readsPrec _ _ = []
data Roster = Roster { ver :: Maybe Text data Roster = Roster { ver :: Maybe Text
, items :: Map.Map Jid Item } deriving Show , items :: Map.Map Jid Item } deriving Show

233
source/Network/Xmpp/Marshal.hs

@ -47,8 +47,8 @@ xpMessage = ("xpMessage" , "") <?+> xpWrap
(\(Message qid from to lang tp ext) -> ((tp, qid, from, to, lang), ext)) (\(Message qid from to lang tp ext) -> ((tp, qid, from, to, lang), ext))
(xpElem "{jabber:client}message" (xpElem "{jabber:client}message"
(xp5Tuple (xp5Tuple
(xpDefault Normal $ xpAttr "type" xpPrim) (xpDefault Normal $ xpAttr "type" xpMessageType)
(xpAttrImplied "id" xpPrim) (xpAttrImplied "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
xpLangTag xpLangTag
@ -63,11 +63,11 @@ xpPresence = ("xpPresence" , "") <?+> xpWrap
(\(Presence qid from to lang tp ext) -> ((qid, from, to, lang, tp), ext)) (\(Presence qid from to lang tp ext) -> ((qid, from, to, lang, tp), ext))
(xpElem "{jabber:client}presence" (xpElem "{jabber:client}presence"
(xp5Tuple (xp5Tuple
(xpAttrImplied "id" xpPrim) (xpAttrImplied "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
xpLangTag xpLangTag
(xpDefault Available $ xpAttr "type" xpPrim) (xpDefault Available $ xpAttr "type" xpPresenceType)
) )
(xpAll xpElemVerbatim) (xpAll xpElemVerbatim)
) )
@ -78,11 +78,11 @@ xpIQRequest = ("xpIQRequest" , "") <?+> xpWrap
(\(IQRequest qid from to lang tp body) -> ((qid, from, to, lang, tp), body)) (\(IQRequest qid from to lang tp body) -> ((qid, from, to, lang, tp), body))
(xpElem "{jabber:client}iq" (xpElem "{jabber:client}iq"
(xp5Tuple (xp5Tuple
(xpAttr "id" xpPrim) (xpAttr "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
xpLangTag xpLangTag
((xpAttr "type" xpPrim)) ((xpAttr "type" xpIQRequestType))
) )
xpElemVerbatim xpElemVerbatim
) )
@ -93,7 +93,7 @@ xpIQResult = ("xpIQResult" , "") <?+> xpWrap
(\(IQResult qid from to lang body) -> ((qid, from, to, lang, ()), body)) (\(IQResult qid from to lang body) -> ((qid, from, to, lang, ()), body))
(xpElem "{jabber:client}iq" (xpElem "{jabber:client}iq"
(xp5Tuple (xp5Tuple
(xpAttr "id" xpPrim) (xpAttr "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
xpLangTag xpLangTag
@ -112,7 +112,7 @@ xpErrorCondition = ("xpErrorCondition" , "") <?+> xpWrap
(\cond -> (cond, (), ())) (\cond -> (cond, (), ()))
(xpElemByNamespace (xpElemByNamespace
"urn:ietf:params:xml:ns:xmpp-stanzas" "urn:ietf:params:xml:ns:xmpp-stanzas"
xpPrim xpStanzaErrorCondition
xpUnit xpUnit
xpUnit xpUnit
) )
@ -122,11 +122,11 @@ xpStanzaError = ("xpStanzaError" , "") <?+> xpWrap
(\(tp, (cond, txt, ext)) -> StanzaError tp cond txt ext) (\(tp, (cond, txt, ext)) -> StanzaError tp cond txt ext)
(\(StanzaError tp cond txt ext) -> (tp, (cond, txt, ext))) (\(StanzaError tp cond txt ext) -> (tp, (cond, txt, ext)))
(xpElem "{jabber:client}error" (xpElem "{jabber:client}error"
(xpAttr "type" xpPrim) (xpAttr "type" xpStanzaErrorType)
(xp3Tuple (xp3Tuple
xpErrorCondition xpErrorCondition
(xpOption $ xpElem "{jabber:client}text" (xpOption $ xpElem "{jabber:client}text"
(xpAttrImplied xmlLang xpPrim) (xpAttrImplied xmlLang xpLang)
(xpContent xpId) (xpContent xpId)
) )
(xpOption xpElemVerbatim) (xpOption xpElemVerbatim)
@ -142,10 +142,10 @@ xpMessageError = ("xpMessageError" , "") <?+> xpWrap
(xpElem "{jabber:client}message" (xpElem "{jabber:client}message"
(xp5Tuple (xp5Tuple
(xpAttrFixed "type" "error") (xpAttrFixed "type" "error")
(xpAttrImplied "id" xpPrim) (xpAttrImplied "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
(xpAttrImplied xmlLang xpPrim) (xpAttrImplied xmlLang xpLang)
-- TODO: NS? -- TODO: NS?
) )
(xp2Tuple xpStanzaError (xpAll xpElemVerbatim)) (xp2Tuple xpStanzaError (xpAll xpElemVerbatim))
@ -159,7 +159,7 @@ xpPresenceError = ("xpPresenceError" , "") <?+> xpWrap
((qid, from, to, lang, ()), (err, ext))) ((qid, from, to, lang, ()), (err, ext)))
(xpElem "{jabber:client}presence" (xpElem "{jabber:client}presence"
(xp5Tuple (xp5Tuple
(xpAttrImplied "id" xpPrim) (xpAttrImplied "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
xpLangTag xpLangTag
@ -176,7 +176,7 @@ xpIQError = ("xpIQError" , "") <?+> xpWrap
((qid, from, to, lang, ()), (err, body))) ((qid, from, to, lang, ()), (err, body)))
(xpElem "{jabber:client}iq" (xpElem "{jabber:client}iq"
(xp5Tuple (xp5Tuple
(xpAttr "id" xpPrim) (xpAttr "id" xpStanzaID)
(xpAttrImplied "from" xpJid) (xpAttrImplied "from" xpJid)
(xpAttrImplied "to" xpJid) (xpAttrImplied "to" xpJid)
xpLangTag xpLangTag
@ -198,7 +198,7 @@ xpStreamError = ("xpStreamError" , "") <?+> xpWrap
(xp3Tuple (xp3Tuple
(xpElemByNamespace (xpElemByNamespace
"urn:ietf:params:xml:ns:xmpp-streams" "urn:ietf:params:xml:ns:xmpp-streams"
xpPrim xpStreamErrorCondition
xpUnit xpUnit
xpUnit xpUnit
) )
@ -212,7 +212,14 @@ xpStreamError = ("xpStreamError" , "") <?+> xpWrap
) )
xpLangTag :: PU [Attribute] (Maybe LangTag) xpLangTag :: PU [Attribute] (Maybe LangTag)
xpLangTag = xpAttrImplied xmlLang xpPrim xpLangTag = xpAttrImplied xmlLang xpLang
xpLang :: PU Text LangTag
xpLang = ("xpLang", "") <?>
xpPartial ( \input -> case langTagFromText input of
Nothing -> Left "Could not parse language tag."
Just j -> Right j)
langTagToText
xmlLang :: Name xmlLang :: Name
xmlLang = Name "lang" (Just "http://www.w3.org/XML/1998/namespace") (Just "xml") xmlLang = Name "lang" (Just "http://www.w3.org/XML/1998/namespace") (Just "xml")
@ -284,3 +291,197 @@ xpJid = ("xpJid", "") <?>
Nothing -> Left "Could not parse JID." Nothing -> Left "Could not parse JID."
Just j -> Right j) Just j -> Right j)
jidToText jidToText
xpStanzaID :: PU Text StanzaID
xpStanzaID = ("xpStanzaID", "") <?>
xpPartial ( \input -> case stanzaIDFromText input of
Nothing -> Left "Could not parse StanzaID."
Just j -> Right j)
stanzaIDToText
where
stanzaIDFromText t = Just $ StanzaID t
stanzaIDToText (StanzaID s) = s
xpIQRequestType :: PU Text IQRequestType
xpIQRequestType = ("xpIQRequestType", "") <?>
xpPartial ( \input -> case iqRequestTypeFromText input of
Nothing -> Left "Could not parse IQ request type."
Just j -> Right j)
iqRequestTypeToText
where
iqRequestTypeFromText "get" = Just Get
iqRequestTypeFromText "set" = Just Set
iqRequestTypeFromText _ = Nothing
iqRequestTypeToText Get = "get"
iqRequestTypeToText Set = "set"
xpMessageType :: PU Text MessageType
xpMessageType = ("xpMessageType", "") <?>
xpPartial ( \input -> case messageTypeFromText input of
Nothing -> Left "Could not parse message type."
Just j -> Right j)
messageTypeToText
where
messageTypeFromText "chat" = Just Chat
messageTypeFromText "groupchat" = Just GroupChat
messageTypeFromText "headline" = Just Headline
messageTypeFromText "normal" = Just Normal
messageTypeFromText _ = Just Normal
messageTypeToText Chat = "chat"
messageTypeToText GroupChat = "groupchat"
messageTypeToText Headline = "headline"
messageTypeToText Normal = "normal"
xpPresenceType :: PU Text PresenceType
xpPresenceType = ("xpPresenceType", "") <?>
xpPartial ( \input -> case presenceTypeFromText input of
Nothing -> Left "Could not parse presence type."
Just j -> Right j)
presenceTypeToText
where
presenceTypeFromText "" = Just Available
presenceTypeFromText "available" = Just Available
presenceTypeFromText "unavailable" = Just Unavailable
presenceTypeFromText "subscribe" = Just Subscribe
presenceTypeFromText "subscribed" = Just Subscribed
presenceTypeFromText "unsubscribe" = Just Unsubscribe
presenceTypeFromText "unsubscribed" = Just Unsubscribed
presenceTypeFromText "probe" = Just Probe
presenceTypeToText Available = "available"
presenceTypeToText Unavailable = "unavailable"
presenceTypeToText Subscribe = "subscribe"
presenceTypeToText Subscribed = "subscribed"
presenceTypeToText Unsubscribe = "unsubscribe"
presenceTypeToText Unsubscribed = "unsubscribed"
presenceTypeToText Probe = "probe"
xpStanzaErrorType :: PU Text StanzaErrorType
xpStanzaErrorType = ("xpStanzaErrorType", "") <?>
xpPartial ( \input -> case stanzaErrorTypeFromText input of
Nothing -> Left "Could not parse stanza error type."
Just j -> Right j)
stanzaErrorTypeToText
where
stanzaErrorTypeFromText "auth" = Just Auth
stanzaErrorTypeFromText "cancel" = Just Cancel
stanzaErrorTypeFromText "continue" = Just Continue
stanzaErrorTypeFromText "modify" = Just Modify
stanzaErrorTypeFromText "wait" = Just Wait
stanzaErrorTypeFromText _ = Nothing
stanzaErrorTypeToText Auth = "auth"
stanzaErrorTypeToText Cancel = "cancel"
stanzaErrorTypeToText Continue = "continue"
stanzaErrorTypeToText Modify = "modify"
stanzaErrorTypeToText Wait = "wait"
xpStanzaErrorCondition :: PU Text StanzaErrorCondition
xpStanzaErrorCondition = ("xpStanzaErrorCondition", "") <?>
xpPartial ( \input -> case stanzaErrorConditionFromText input of
Nothing -> Left "Could not parse stanza error condition."
Just j -> Right j)
stanzaErrorConditionToText
where
stanzaErrorConditionToText BadRequest = "bad-request"
stanzaErrorConditionToText Conflict = "conflict"
stanzaErrorConditionToText FeatureNotImplemented = "feature-not-implemented"
stanzaErrorConditionToText Forbidden = "forbidden"
stanzaErrorConditionToText Gone = "gone"
stanzaErrorConditionToText InternalServerError = "internal-server-error"
stanzaErrorConditionToText ItemNotFound = "item-not-found"
stanzaErrorConditionToText JidMalformed = "jid-malformed"
stanzaErrorConditionToText NotAcceptable = "not-acceptable"
stanzaErrorConditionToText NotAllowed = "not-allowed"
stanzaErrorConditionToText NotAuthorized = "not-authorized"
stanzaErrorConditionToText PaymentRequired = "payment-required"
stanzaErrorConditionToText RecipientUnavailable = "recipient-unavailable"
stanzaErrorConditionToText Redirect = "redirect"
stanzaErrorConditionToText RegistrationRequired = "registration-required"
stanzaErrorConditionToText RemoteServerNotFound = "remote-server-not-found"
stanzaErrorConditionToText RemoteServerTimeout = "remote-server-timeout"
stanzaErrorConditionToText ResourceConstraint = "resource-constraint"
stanzaErrorConditionToText ServiceUnavailable = "service-unavailable"
stanzaErrorConditionToText SubscriptionRequired = "subscription-required"
stanzaErrorConditionToText UndefinedCondition = "undefined-condition"
stanzaErrorConditionToText UnexpectedRequest = "unexpected-request"
stanzaErrorConditionFromText "bad-request" = Just BadRequest
stanzaErrorConditionFromText "conflict" = Just Conflict
stanzaErrorConditionFromText "feature-not-implemented" = Just FeatureNotImplemented
stanzaErrorConditionFromText "forbidden" = Just Forbidden
stanzaErrorConditionFromText "gone" = Just Gone
stanzaErrorConditionFromText "internal-server-error" = Just InternalServerError
stanzaErrorConditionFromText "item-not-found" = Just ItemNotFound
stanzaErrorConditionFromText "jid-malformed" = Just JidMalformed
stanzaErrorConditionFromText "not-acceptable" = Just NotAcceptable
stanzaErrorConditionFromText "not-allowed" = Just NotAllowed
stanzaErrorConditionFromText "not-authorized" = Just NotAuthorized
stanzaErrorConditionFromText "payment-required" = Just PaymentRequired
stanzaErrorConditionFromText "recipient-unavailable" = Just RecipientUnavailable
stanzaErrorConditionFromText "redirect" = Just Redirect
stanzaErrorConditionFromText "registration-required" = Just RegistrationRequired
stanzaErrorConditionFromText "remote-server-not-found" = Just RemoteServerNotFound
stanzaErrorConditionFromText "remote-server-timeout" = Just RemoteServerTimeout
stanzaErrorConditionFromText "resource-constraint" = Just ResourceConstraint
stanzaErrorConditionFromText "service-unavailable" = Just ServiceUnavailable
stanzaErrorConditionFromText "subscription-required" = Just SubscriptionRequired
stanzaErrorConditionFromText "undefined-condition" = Just UndefinedCondition
stanzaErrorConditionFromText "unexpected-request" = Just UnexpectedRequest
stanzaErrorConditionFromText _ = Nothing
xpStreamErrorCondition :: PU Text StreamErrorCondition
xpStreamErrorCondition = ("xpStreamErrorCondition", "") <?>
xpPartial ( \input -> case streamErrorConditionFromText input of
Nothing -> Left "Could not parse stream error condition."
Just j -> Right j)
streamErrorConditionToText
where
streamErrorConditionToText StreamBadFormat = "bad-format"
streamErrorConditionToText StreamBadNamespacePrefix = "bad-namespace-prefix"
streamErrorConditionToText StreamConflict = "conflict"
streamErrorConditionToText StreamConnectionTimeout = "connection-timeout"
streamErrorConditionToText StreamHostGone = "host-gone"
streamErrorConditionToText StreamHostUnknown = "host-unknown"
streamErrorConditionToText StreamImproperAddressing = "improper-addressing"
streamErrorConditionToText StreamInternalServerError = "internal-server-error"
streamErrorConditionToText StreamInvalidFrom = "invalid-from"
streamErrorConditionToText StreamInvalidNamespace = "invalid-namespace"
streamErrorConditionToText StreamInvalidXml = "invalid-xml"
streamErrorConditionToText StreamNotAuthorized = "not-authorized"
streamErrorConditionToText StreamNotWellFormed = "not-well-formed"
streamErrorConditionToText StreamPolicyViolation = "policy-violation"
streamErrorConditionToText StreamRemoteConnectionFailed = "remote-connection-failed"
streamErrorConditionToText StreamReset = "reset"
streamErrorConditionToText StreamResourceConstraint = "resource-constraint"
streamErrorConditionToText StreamRestrictedXml = "restricted-xml"
streamErrorConditionToText StreamSeeOtherHost = "see-other-host"
streamErrorConditionToText StreamSystemShutdown = "system-shutdown"
streamErrorConditionToText StreamUndefinedCondition = "undefined-condition"
streamErrorConditionToText StreamUnsupportedEncoding = "unsupported-encoding"
streamErrorConditionToText StreamUnsupportedFeature = "unsupported-feature"
streamErrorConditionToText StreamUnsupportedStanzaType = "unsupported-stanza-type"
streamErrorConditionToText StreamUnsupportedVersion = "unsupported-version"
streamErrorConditionFromText "bad-format" = Just StreamBadFormat
streamErrorConditionFromText "bad-namespace-prefix" = Just StreamBadNamespacePrefix
streamErrorConditionFromText "conflict" = Just StreamConflict
streamErrorConditionFromText "connection-timeout" = Just StreamConnectionTimeout
streamErrorConditionFromText "host-gone" = Just StreamHostGone
streamErrorConditionFromText "host-unknown" = Just StreamHostUnknown
streamErrorConditionFromText "improper-addressing" = Just StreamImproperAddressing
streamErrorConditionFromText "internal-server-error" = Just StreamInternalServerError
streamErrorConditionFromText "invalid-from" = Just StreamInvalidFrom
streamErrorConditionFromText "invalid-namespace" = Just StreamInvalidNamespace
streamErrorConditionFromText "invalid-xml" = Just StreamInvalidXml
streamErrorConditionFromText "not-authorized" = Just StreamNotAuthorized
streamErrorConditionFromText "not-well-formed" = Just StreamNotWellFormed
streamErrorConditionFromText "policy-violation" = Just StreamPolicyViolation
streamErrorConditionFromText "remote-connection-failed" = Just StreamRemoteConnectionFailed
streamErrorConditionFromText "reset" = Just StreamReset
streamErrorConditionFromText "resource-constraint" = Just StreamResourceConstraint
streamErrorConditionFromText "restricted-xml" = Just StreamRestrictedXml
streamErrorConditionFromText "see-other-host" = Just StreamSeeOtherHost
streamErrorConditionFromText "system-shutdown" = Just StreamSystemShutdown
streamErrorConditionFromText "undefined-condition" = Just StreamUndefinedCondition
streamErrorConditionFromText "unsupported-encoding" = Just StreamUnsupportedEncoding
streamErrorConditionFromText "unsupported-feature" = Just StreamUnsupportedFeature
streamErrorConditionFromText "unsupported-stanza-type" = Just StreamUnsupportedStanzaType
streamErrorConditionFromText "unsupported-version" = Just StreamUnsupportedVersion
streamErrorConditionFromText _ = Nothing

32
source/Network/Xmpp/Sasl/Common.hs

@ -80,10 +80,40 @@ xpFailure = xpWrap
(xpContent xpId)) (xpContent xpId))
(xpElemByNamespace (xpElemByNamespace
"urn:ietf:params:xml:ns:xmpp-sasl" "urn:ietf:params:xml:ns:xmpp-sasl"
xpPrim xpSaslError
(xpUnit) (xpUnit)
(xpUnit)))) (xpUnit))))
xpSaslError :: PU Text.Text SaslError
xpSaslError = ("xpSaslError", "") <?>
xpPartial ( \input -> case saslErrorFromText input of
Nothing -> Left "Could not parse SASL error."
Just j -> Right j)
saslErrorToText
where
saslErrorToText SaslAborted = "aborted"
saslErrorToText SaslAccountDisabled = "account-disabled"
saslErrorToText SaslCredentialsExpired = "credentials-expired"
saslErrorToText SaslEncryptionRequired = "encryption-required"
saslErrorToText SaslIncorrectEncoding = "incorrect-encoding"
saslErrorToText SaslInvalidAuthzid = "invalid-authzid"
saslErrorToText SaslInvalidMechanism = "invalid-mechanism"
saslErrorToText SaslMalformedRequest = "malformed-request"
saslErrorToText SaslMechanismTooWeak = "mechanism-too-weak"
saslErrorToText SaslNotAuthorized = "not-authorized"
saslErrorToText SaslTemporaryAuthFailure = "temporary-auth-failure"
saslErrorFromText "aborted" = Just SaslAborted
saslErrorFromText "account-disabled" = Just SaslAccountDisabled
saslErrorFromText "credentials-expired" = Just SaslCredentialsExpired
saslErrorFromText "encryption-required" = Just SaslEncryptionRequired
saslErrorFromText "incorrect-encoding" = Just SaslIncorrectEncoding
saslErrorFromText "invalid-authzid" = Just SaslInvalidAuthzid
saslErrorFromText "invalid-mechanism" = Just SaslInvalidMechanism
saslErrorFromText "malformed-request" = Just SaslMalformedRequest
saslErrorFromText "mechanism-too-weak" = Just SaslMechanismTooWeak
saslErrorFromText "not-authorized" = Just SaslNotAuthorized
saslErrorFromText "temporary-auth-failure" = Just SaslTemporaryAuthFailure
-- Challenge element pickler. -- Challenge element pickler.
xpChallenge :: PU [Node] (Maybe Text.Text) xpChallenge :: PU [Node] (Maybe Text.Text)
xpChallenge = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-sasl}challenge" xpChallenge = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-sasl}challenge"

277
source/Network/Xmpp/Types.hs

@ -1,6 +1,11 @@
{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 706
{-# LANGUAGE TemplateHaskell #-}
#endif
{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TupleSections #-} {-# LANGUAGE TupleSections #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StandaloneDeriving #-}
@ -15,6 +20,9 @@ module Network.Xmpp.Types
, IQResult(..) , IQResult(..)
, IdGenerator(..) , IdGenerator(..)
, LangTag (..) , LangTag (..)
, langTagFromText
, langTagToText
, parseLangTag
, Message(..) , Message(..)
, message , message
, MessageError(..) , MessageError(..)
@ -42,9 +50,10 @@ module Network.Xmpp.Types
, StanzaHandler , StanzaHandler
, ConnectionDetails(..) , ConnectionDetails(..)
, StreamConfiguration(..) , StreamConfiguration(..)
, langTag
, Jid(..) , Jid(..)
#if __GLASGOW_HASKELL__ >= 706
, jidQ , jidQ
#endif
, isBare , isBare
, isFull , isFull
, jidFromText , jidFromText
@ -78,8 +87,10 @@ import Data.Text (Text)
import qualified Data.Text as Text import qualified Data.Text as Text
import Data.Typeable(Typeable) import Data.Typeable(Typeable)
import Data.XML.Types import Data.XML.Types
#if __GLASGOW_HASKELL__ >= 706
import Language.Haskell.TH import Language.Haskell.TH
import Language.Haskell.TH.Quote import Language.Haskell.TH.Quote
#endif
import Network import Network
import Network.DNS import Network.DNS
import Network.TLS hiding (Version) import Network.TLS hiding (Version)
@ -91,13 +102,7 @@ import qualified Text.StringPrep as SP
-- Wraps a string of random characters that, when using an appropriate -- Wraps a string of random characters that, when using an appropriate
-- @IdGenerator@, is guaranteed to be unique for the Xmpp session. -- @IdGenerator@, is guaranteed to be unique for the Xmpp session.
data StanzaID = StanzaID !Text deriving (Eq, Ord) data StanzaID = StanzaID !Text deriving (Eq, Ord, Read, Show)
instance Show StanzaID where
show (StanzaID s) = Text.unpack s
instance Read StanzaID where
readsPrec _ x = [(StanzaID $ Text.pack x, "")]
instance IsString StanzaID where instance IsString StanzaID where
fromString = StanzaID . Text.pack fromString = StanzaID . Text.pack
@ -124,16 +129,7 @@ data IQRequest = IQRequest { iqRequestID :: !StanzaID
} deriving Show } deriving Show
-- | The type of IQ request that is made. -- | The type of IQ request that is made.
data IQRequestType = Get | Set deriving (Eq, Ord) data IQRequestType = Get | Set deriving (Eq, Ord, Read, Show)
instance Show IQRequestType where
show Get = "get"
show Set = "set"
instance Read IQRequestType where
readsPrec _ "get" = [(Get, "")]
readsPrec _ "set" = [(Set, "")]
readsPrec _ _ = []
-- | A "response" Info/Query (IQ) stanza is either an 'IQError', an IQ stanza -- | A "response" Info/Query (IQ) stanza is either an 'IQError', an IQ stanza
-- of type "result" ('IQResult') or a Timeout. -- of type "result" ('IQResult') or a Timeout.
@ -228,20 +224,7 @@ data MessageType = -- | The message is sent in the context of a one-to-one chat
-- --
-- This is the /default/ value. -- This is the /default/ value.
| Normal | Normal
deriving (Eq) deriving (Eq, Read, Show)
instance Show MessageType where
show Chat = "chat"
show GroupChat = "groupchat"
show Headline = "headline"
show Normal = "normal"
instance Read MessageType where
readsPrec _ "chat" = [(Chat, "")]
readsPrec _ "groupchat" = [(GroupChat, "")]
readsPrec _ "headline" = [(Headline, "")]
readsPrec _ "normal" = [(Normal, "")]
readsPrec _ _ = [(Normal, "")]
-- | The presence stanza. Used for communicating status updates. -- | The presence stanza. Used for communicating status updates.
data Presence = Presence { presenceID :: !(Maybe StanzaID) data Presence = Presence { presenceID :: !(Maybe StanzaID)
@ -285,27 +268,7 @@ data PresenceType = Subscribe | -- ^ Sender wants to subscribe to presence
-- should only be used by servers -- should only be used by servers
Available | -- ^ Sender wants to express availability Available | -- ^ Sender wants to express availability
-- (no type attribute is defined) -- (no type attribute is defined)
Unavailable deriving (Eq) Unavailable deriving (Eq, Read, Show)
instance Show PresenceType where
show Subscribe = "subscribe"
show Subscribed = "subscribed"
show Unsubscribe = "unsubscribe"
show Unsubscribed = "unsubscribed"
show Probe = "probe"
show Available = ""
show Unavailable = "unavailable"
instance Read PresenceType where
readsPrec _ "" = [(Available, "")]
readsPrec _ "available" = [(Available, "")]
readsPrec _ "unavailable" = [(Unavailable, "")]
readsPrec _ "subscribe" = [(Subscribe, "")]
readsPrec _ "subscribed" = [(Subscribed, "")]
readsPrec _ "unsubscribe" = [(Unsubscribe, "")]
readsPrec _ "unsubscribed" = [(Unsubscribed, "")]
readsPrec _ "probe" = [(Probe, "")]
readsPrec _ _ = []
-- | All stanzas (IQ, message, presence) can cause errors, which in the Xmpp -- | All stanzas (IQ, message, presence) can cause errors, which in the Xmpp
-- stream looks like <stanza-kind to='sender' type='error'>. These errors are -- stream looks like <stanza-kind to='sender' type='error'>. These errors are
@ -324,22 +287,7 @@ data StanzaErrorType = Cancel | -- ^ Error is unrecoverable - do not retry
Modify | -- ^ Change the data and retry Modify | -- ^ Change the data and retry
Auth | -- ^ Provide credentials and retry Auth | -- ^ Provide credentials and retry
Wait -- ^ Error is temporary - wait and retry Wait -- ^ Error is temporary - wait and retry
deriving (Eq) deriving (Eq, Read, Show)
instance Show StanzaErrorType where
show Cancel = "cancel"
show Continue = "continue"
show Modify = "modify"
show Auth = "auth"
show Wait = "wait"
instance Read StanzaErrorType where
readsPrec _ "auth" = [( Auth , "")]
readsPrec _ "cancel" = [( Cancel , "")]
readsPrec _ "continue" = [( Continue, "")]
readsPrec _ "modify" = [( Modify , "")]
readsPrec _ "wait" = [( Wait , "")]
readsPrec _ _ = []
-- | Stanza errors are accommodated with one of the error conditions listed -- | Stanza errors are accommodated with one of the error conditions listed
-- below. -- below.
@ -376,56 +324,7 @@ data StanzaErrorCondition = BadRequest -- ^ Malformed XML.
| UndefinedCondition -- ^ Application-specific | UndefinedCondition -- ^ Application-specific
-- condition. -- condition.
| UnexpectedRequest -- ^ Badly timed request. | UnexpectedRequest -- ^ Badly timed request.
deriving Eq deriving (Eq, Read, Show)
instance Show StanzaErrorCondition where
show BadRequest = "bad-request"
show Conflict = "conflict"
show FeatureNotImplemented = "feature-not-implemented"
show Forbidden = "forbidden"
show Gone = "gone"
show InternalServerError = "internal-server-error"
show ItemNotFound = "item-not-found"
show JidMalformed = "jid-malformed"
show NotAcceptable = "not-acceptable"
show NotAllowed = "not-allowed"
show NotAuthorized = "not-authorized"
show PaymentRequired = "payment-required"
show RecipientUnavailable = "recipient-unavailable"
show Redirect = "redirect"
show RegistrationRequired = "registration-required"
show RemoteServerNotFound = "remote-server-not-found"
show RemoteServerTimeout = "remote-server-timeout"
show ResourceConstraint = "resource-constraint"
show ServiceUnavailable = "service-unavailable"
show SubscriptionRequired = "subscription-required"
show UndefinedCondition = "undefined-condition"
show UnexpectedRequest = "unexpected-request"
instance Read StanzaErrorCondition where
readsPrec _ "bad-request" = [(BadRequest , "")]
readsPrec _ "conflict" = [(Conflict , "")]
readsPrec _ "feature-not-implemented" = [(FeatureNotImplemented, "")]
readsPrec _ "forbidden" = [(Forbidden , "")]
readsPrec _ "gone" = [(Gone , "")]
readsPrec _ "internal-server-error" = [(InternalServerError , "")]
readsPrec _ "item-not-found" = [(ItemNotFound , "")]
readsPrec _ "jid-malformed" = [(JidMalformed , "")]
readsPrec _ "not-acceptable" = [(NotAcceptable , "")]
readsPrec _ "not-allowed" = [(NotAllowed , "")]
readsPrec _ "not-authorized" = [(NotAuthorized , "")]
readsPrec _ "payment-required" = [(PaymentRequired , "")]
readsPrec _ "recipient-unavailable" = [(RecipientUnavailable , "")]
readsPrec _ "redirect" = [(Redirect , "")]
readsPrec _ "registration-required" = [(RegistrationRequired , "")]
readsPrec _ "remote-server-not-found" = [(RemoteServerNotFound , "")]
readsPrec _ "remote-server-timeout" = [(RemoteServerTimeout , "")]
readsPrec _ "resource-constraint" = [(ResourceConstraint , "")]
readsPrec _ "service-unavailable" = [(ServiceUnavailable , "")]
readsPrec _ "subscription-required" = [(SubscriptionRequired , "")]
readsPrec _ "unexpected-request" = [(UnexpectedRequest , "")]
readsPrec _ "undefined-condition" = [(UndefinedCondition , "")]
readsPrec _ _ = [(UndefinedCondition , "")]
-- ============================================================================= -- =============================================================================
-- OTHER STUFF -- OTHER STUFF
@ -464,34 +363,7 @@ data SaslError = SaslAborted -- ^ Client aborted.
-- temporary error condition; the -- temporary error condition; the
-- initiating entity is recommended -- initiating entity is recommended
-- to try again later. -- to try again later.
deriving Eq deriving (Eq, Read, Show)
instance Show SaslError where
show SaslAborted = "aborted"
show SaslAccountDisabled = "account-disabled"
show SaslCredentialsExpired = "credentials-expired"
show SaslEncryptionRequired = "encryption-required"
show SaslIncorrectEncoding = "incorrect-encoding"
show SaslInvalidAuthzid = "invalid-authzid"
show SaslInvalidMechanism = "invalid-mechanism"
show SaslMalformedRequest = "malformed-request"
show SaslMechanismTooWeak = "mechanism-too-weak"
show SaslNotAuthorized = "not-authorized"
show SaslTemporaryAuthFailure = "temporary-auth-failure"
instance Read SaslError where
readsPrec _ "aborted" = [(SaslAborted , "")]
readsPrec _ "account-disabled" = [(SaslAccountDisabled , "")]
readsPrec _ "credentials-expired" = [(SaslCredentialsExpired , "")]
readsPrec _ "encryption-required" = [(SaslEncryptionRequired , "")]
readsPrec _ "incorrect-encoding" = [(SaslIncorrectEncoding , "")]
readsPrec _ "invalid-authzid" = [(SaslInvalidAuthzid , "")]
readsPrec _ "invalid-mechanism" = [(SaslInvalidMechanism , "")]
readsPrec _ "malformed-request" = [(SaslMalformedRequest , "")]
readsPrec _ "mechanism-too-weak" = [(SaslMechanismTooWeak , "")]
readsPrec _ "not-authorized" = [(SaslNotAuthorized , "")]
readsPrec _ "temporary-auth-failure" = [(SaslTemporaryAuthFailure , "")]
readsPrec _ _ = []
-- The documentation of StreamErrorConditions is copied from -- The documentation of StreamErrorConditions is copied from
-- http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions -- http://xmpp.org/rfcs/rfc6120.html#streams-error-conditions
@ -608,63 +480,7 @@ data StreamErrorCondition
-- initiating entity in the stream header -- initiating entity in the stream header
-- specifies a version of XMPP that is not -- specifies a version of XMPP that is not
-- supported by the server. -- supported by the server.
deriving Eq deriving (Eq, Read, Show)
instance Show StreamErrorCondition where
show StreamBadFormat = "bad-format"
show StreamBadNamespacePrefix = "bad-namespace-prefix"
show StreamConflict = "conflict"
show StreamConnectionTimeout = "connection-timeout"
show StreamHostGone = "host-gone"
show StreamHostUnknown = "host-unknown"
show StreamImproperAddressing = "improper-addressing"
show StreamInternalServerError = "internal-server-error"
show StreamInvalidFrom = "invalid-from"
show StreamInvalidNamespace = "invalid-namespace"
show StreamInvalidXml = "invalid-xml"
show StreamNotAuthorized = "not-authorized"
show StreamNotWellFormed = "not-well-formed"
show StreamPolicyViolation = "policy-violation"
show StreamRemoteConnectionFailed = "remote-connection-failed"
show StreamReset = "reset"
show StreamResourceConstraint = "resource-constraint"
show StreamRestrictedXml = "restricted-xml"
show StreamSeeOtherHost = "see-other-host"
show StreamSystemShutdown = "system-shutdown"
show StreamUndefinedCondition = "undefined-condition"
show StreamUnsupportedEncoding = "unsupported-encoding"
show StreamUnsupportedFeature = "unsupported-feature"
show StreamUnsupportedStanzaType = "unsupported-stanza-type"
show StreamUnsupportedVersion = "unsupported-version"
instance Read StreamErrorCondition where
readsPrec _ "bad-format" = [(StreamBadFormat , "")]
readsPrec _ "bad-namespace-prefix" = [(StreamBadNamespacePrefix , "")]
readsPrec _ "conflict" = [(StreamConflict , "")]
readsPrec _ "connection-timeout" = [(StreamConnectionTimeout , "")]
readsPrec _ "host-gone" = [(StreamHostGone , "")]
readsPrec _ "host-unknown" = [(StreamHostUnknown , "")]
readsPrec _ "improper-addressing" = [(StreamImproperAddressing , "")]
readsPrec _ "internal-server-error" = [(StreamInternalServerError , "")]
readsPrec _ "invalid-from" = [(StreamInvalidFrom , "")]
readsPrec _ "invalid-namespace" = [(StreamInvalidNamespace , "")]
readsPrec _ "invalid-xml" = [(StreamInvalidXml , "")]
readsPrec _ "not-authorized" = [(StreamNotAuthorized , "")]
readsPrec _ "not-well-formed" = [(StreamNotWellFormed , "")]
readsPrec _ "policy-violation" = [(StreamPolicyViolation , "")]
readsPrec _ "remote-connection-failed" =
[(StreamRemoteConnectionFailed, "")]
readsPrec _ "reset" = [(StreamReset , "")]
readsPrec _ "resource-constraint" = [(StreamResourceConstraint , "")]
readsPrec _ "restricted-xml" = [(StreamRestrictedXml , "")]
readsPrec _ "see-other-host" = [(StreamSeeOtherHost , "")]
readsPrec _ "system-shutdown" = [(StreamSystemShutdown , "")]
readsPrec _ "undefined-condition" = [(StreamUndefinedCondition , "")]
readsPrec _ "unsupported-encoding" = [(StreamUnsupportedEncoding , "")]
readsPrec _ "unsupported-feature" = [(StreamUnsupportedFeature , "")]
readsPrec _ "unsupported-stanza-type" = [(StreamUnsupportedStanzaType, "")]
readsPrec _ "unsupported-version" = [(StreamUnsupportedVersion , "")]
readsPrec _ _ = [(StreamUndefinedCondition , "")]
-- | Encapsulates information about an XMPP stream error. -- | Encapsulates information about an XMPP stream error.
data StreamErrorInfo = StreamErrorInfo data StreamErrorInfo = StreamErrorInfo
@ -749,7 +565,7 @@ newtype IdGenerator = IdGenerator (IO Text)
-- 2.13, which in turn is lesser than 12.3. -- 2.13, which in turn is lesser than 12.3.
data Version = Version { majorVersion :: !Integer data Version = Version { majorVersion :: !Integer
, minorVersion :: !Integer } deriving (Eq) , minorVersion :: !Integer } deriving (Eq, Read, Show)
-- If the major version numbers are not equal, compare them. Otherwise, compare -- If the major version numbers are not equal, compare them. Otherwise, compare
-- the minor version numbers. -- the minor version numbers.
@ -758,11 +574,11 @@ instance Ord Version where
| amajor /= bmajor = compare amajor bmajor | amajor /= bmajor = compare amajor bmajor
| otherwise = compare aminor bminor | otherwise = compare aminor bminor
instance Read Version where -- instance Read Version where
readsPrec _ txt = (,"") <$> maybeToList (versionFromText $ Text.pack txt) -- readsPrec _ txt = (,"") <$> maybeToList (versionFromText $ Text.pack txt)
instance Show Version where -- instance Show Version where
show (Version major minor) = (show major) ++ "." ++ (show minor) -- show (Version major minor) = (show major) ++ "." ++ (show minor)
-- Converts a "<major>.<minor>" numeric version number to a @Version@ object. -- Converts a "<major>.<minor>" numeric version number to a @Version@ object.
versionFromText :: Text.Text -> Maybe Version versionFromText :: Text.Text -> Maybe Version
@ -785,24 +601,21 @@ versionParser = do
data LangTag = LangTag { primaryTag :: !Text data LangTag = LangTag { primaryTag :: !Text
, subtags :: ![Text] } , subtags :: ![Text] }
-- Equals for language tags is not case-sensitive.
instance Eq LangTag where instance Eq LangTag where
LangTag p s == LangTag q t = Text.toLower p == Text.toLower q && LangTag p s == LangTag q t = Text.toLower p == Text.toLower q &&
map Text.toLower s == map Text.toLower t map Text.toLower s == map Text.toLower t
instance Read LangTag where
readsPrec _ txt = (,"") <$> maybeToList (langTag $ Text.pack txt)
instance Show LangTag where
show (LangTag p []) = Text.unpack p
show (LangTag p s) = Text.unpack . Text.concat $
[p, "-", Text.intercalate "-" s]
-- | Parses, validates, and possibly constructs a "LangTag" object. -- | Parses, validates, and possibly constructs a "LangTag" object.
langTag :: Text.Text -> Maybe LangTag langTagFromText :: Text.Text -> Maybe LangTag
langTag s = case AP.parseOnly langTagParser s of langTagFromText s = case AP.parseOnly langTagParser s of
Right tag -> Just tag Right tag -> Just tag
Left _ -> Nothing Left _ -> Nothing
langTagToText :: LangTag -> Text.Text
langTagToText (LangTag p []) = p
langTagToText (LangTag p s) = Text.concat $ [p, "-", Text.intercalate "-" s]
-- Parses a language tag as defined by RFC 1766 and constructs a LangTag object. -- Parses a language tag as defined by RFC 1766 and constructs a LangTag object.
langTagParser :: AP.Parser LangTag langTagParser :: AP.Parser LangTag
langTagParser = do langTagParser = do
@ -962,6 +775,7 @@ instance Read Jid where
[(parseJid (read s' :: String), r)] -- May fail with "Prelude.read: no parse" [(parseJid (read s' :: String), r)] -- May fail with "Prelude.read: no parse"
-- or the `parseJid' error message (see below) -- or the `parseJid' error message (see below)
#if __GLASGOW_HASKELL__ >= 706
jidQ :: QuasiQuoter jidQ :: QuasiQuoter
jidQ = QuasiQuoter { quoteExp = \s -> do jidQ = QuasiQuoter { quoteExp = \s -> do
when (head s == ' ') . fail $ "Leading whitespaces in JID" ++ show s when (head s == ' ') . fail $ "Leading whitespaces in JID" ++ show s
@ -981,6 +795,29 @@ jidQ = QuasiQuoter { quoteExp = \s -> do
textE t = [| Text.pack $(stringE $ Text.unpack t) |] textE t = [| Text.pack $(stringE $ Text.unpack t) |]
mbTextE Nothing = [| Nothing |] mbTextE Nothing = [| Nothing |]
mbTextE (Just s) = [| Just $(textE s) |] mbTextE (Just s) = [| Just $(textE s) |]
#endif
-- Produces a LangTag value in the format "parseLangTag \"<jid>\"".
instance Show LangTag where
show l = "parseLangTag " ++ show (langTagToText l)
-- The string must be in the format "parseLangTag \"<LangTag>\"". This is based
-- on parseJid, and suffers the same problems.
instance Read LangTag where
readsPrec _ s = do
let (s', r) = case lex s of
[] -> error "Expected `parseLangTag \"<LangTag>\"'"
[("parseLangTag", r')] -> case lex r' of
[] -> error "Expected `parseLangTag \"<LangTag>\"'"
[(s'', r'')] -> (s'', r'')
_ -> error "Expected `parseLangTag \"<LangTag>\"'"
_ -> error "Expected `parseLangTag \"<LangTag>\"'"
[(parseLangTag (read s' :: String), r)]
parseLangTag :: String -> LangTag
parseLangTag s = case langTagFromText $ Text.pack s of
Just l -> l
Nothing -> error $ "Language tag value (" ++ s ++ ") did not validate"
-- | Parses a JID string. -- | Parses a JID string.
-- --

11
source/Network/Xmpp/Utilities.hs

@ -28,6 +28,7 @@ import Prelude
import System.IO.Unsafe(unsafePerformIO) import System.IO.Unsafe(unsafePerformIO)
import qualified Text.XML.Stream.Render as TXSR import qualified Text.XML.Stream.Render as TXSR
import Text.XML.Unresolved as TXU import Text.XML.Unresolved as TXU
import System.Random
-- | Apply f with the content of tv as state, restoring the original value when an -- | Apply f with the content of tv as state, restoring the original value when an
-- exception occurs -- exception occurs
@ -92,3 +93,13 @@ hostnameP = do
if Text.length label + 1 + Text.length r > 255 if Text.length label + 1 + Text.length r > 255
then fail "Hostname too long." then fail "Hostname too long."
else return $ Text.concat [label, Text.pack ".", r] else return $ Text.concat [label, Text.pack ".", r]
-- The number of seconds to wait before reconnection attempts in accordance with
-- the truncated binary exponential backoff algorithm.
waitingTimes :: IO [Int]
waitingTimes = do
wait <- randomRIO (1, 59)
waits <- Prelude.mapM (\n -> randomRIO (0, wait * n)) slotTimes
return (wait:waits)
where
slotTimes = [1, 3, 8, 15, 24, 35, 48, 63, 80, 99, 99, 99, 99, 99, 99]

Loading…
Cancel
Save