diff options
Diffstat (limited to 'Presence/XMPPServer.hs')
-rw-r--r-- | Presence/XMPPServer.hs | 433 |
1 files changed, 4 insertions, 429 deletions
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index 3327b523..11a27660 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -75,7 +75,6 @@ import Text.Printf | |||
75 | import Data.ByteString (ByteString) | 75 | import Data.ByteString (ByteString) |
76 | import qualified Data.ByteString.Char8 as Strict8 | 76 | import qualified Data.ByteString.Char8 as Strict8 |
77 | -- import qualified Data.ByteString.Lazy.Char8 as Lazy8 | 77 | -- import qualified Data.ByteString.Lazy.Char8 as Lazy8 |
78 | import Data.Int (Int8) | ||
79 | 78 | ||
80 | import Data.Conduit | 79 | import Data.Conduit |
81 | import qualified Data.Conduit.List as CL | 80 | import qualified Data.Conduit.List as CL |
@@ -85,8 +84,8 @@ import Data.Conduit.ByteString.Builder (builderToByteStringFlush) | |||
85 | #else | 84 | #else |
86 | import Data.Conduit.Blaze (builderToByteStringFlush) | 85 | import Data.Conduit.Blaze (builderToByteStringFlush) |
87 | #endif | 86 | #endif |
88 | import Control.Monad.Catch (MonadThrow) | ||
89 | 87 | ||
88 | import Control.Concurrent.STM.Util | ||
90 | import DNSCache (withPort) | 89 | import DNSCache (withPort) |
91 | import qualified Text.XML.Stream.Render as XML hiding (content) | 90 | import qualified Text.XML.Stream.Render as XML hiding (content) |
92 | import qualified Text.XML.Stream.Parse as XML | 91 | import qualified Text.XML.Stream.Parse as XML |
@@ -94,8 +93,7 @@ import Data.XML.Types as XML | |||
94 | import Data.Maybe | 93 | import Data.Maybe |
95 | import Data.Monoid ( (<>) ) | 94 | import Data.Monoid ( (<>) ) |
96 | import Data.Text (Text) | 95 | import Data.Text (Text) |
97 | import qualified Data.Text as Text (pack,unpack,words,intercalate,drop) | 96 | import qualified Data.Text as Text (pack,unpack,intercalate,drop) |
98 | import Data.Char (chr,ord) | ||
99 | import qualified Data.Map as Map | 97 | import qualified Data.Map as Map |
100 | import Data.Set (Set, (\\) ) | 98 | import Data.Set (Set, (\\) ) |
101 | import qualified Data.Set as Set | 99 | import qualified Data.Set as Set |
@@ -104,6 +102,8 @@ import qualified System.Random | |||
104 | import Data.Void (Void) | 102 | import Data.Void (Void) |
105 | import DPut | 103 | import DPut |
106 | import DebugTag | 104 | import DebugTag |
105 | import Stanza.Type | ||
106 | import Stanza.Parse | ||
107 | 107 | ||
108 | -- peerport :: PortNumber | 108 | -- peerport :: PortNumber |
109 | -- peerport = 5269 | 109 | -- peerport = 5269 |
@@ -113,87 +113,6 @@ import DebugTag | |||
113 | my_uuid :: Text | 113 | my_uuid :: Text |
114 | my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" | 114 | my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" |
115 | 115 | ||
116 | data JabberShow = Offline | ||
117 | | ExtendedAway | ||
118 | | Away | ||
119 | | DoNotDisturb | ||
120 | | Available | ||
121 | | Chatty | ||
122 | deriving (Show,Enum,Ord,Eq,Read) | ||
123 | |||
124 | data MessageThread = MessageThread { | ||
125 | msgThreadParent :: Maybe Text, | ||
126 | msgThreadContent :: Text | ||
127 | } | ||
128 | deriving (Show,Eq) | ||
129 | |||
130 | data LangSpecificMessage = | ||
131 | LangSpecificMessage { msgBody :: Maybe Text | ||
132 | , msgSubject :: Maybe Text | ||
133 | } | ||
134 | deriving (Show,Eq) | ||
135 | |||
136 | data RosterEventType | ||
137 | = RequestedSubscription | ||
138 | | NewBuddy -- preceded by PresenceInformSubscription True | ||
139 | | RemovedBuddy -- preceded by PresenceInformSubscription False | ||
140 | | PendingSubscriber -- same as PresenceRequestSubscription | ||
141 | | NewSubscriber | ||
142 | | RejectSubscriber | ||
143 | deriving (Show,Read,Ord,Eq,Enum) | ||
144 | |||
145 | data ClientHack = SimulatedChatErrors | ||
146 | deriving (Show,Read,Ord,Eq,Enum) | ||
147 | |||
148 | data StanzaType | ||
149 | = Unrecognized | ||
150 | | Ping | ||
151 | | Pong | ||
152 | | RequestResource (Maybe Text) (Maybe Text) -- ^ Client's name for this host followed by client's requested resource id. | ||
153 | | SetResource | ||
154 | | SessionRequest | ||
155 | | UnrecognizedQuery Name | ||
156 | | RequestRoster | ||
157 | | Roster | ||
158 | | RosterEvent { rosterEventType :: RosterEventType | ||
159 | , rosterUser :: Text | ||
160 | , rosterContact :: Text } | ||
161 | | Error StanzaError XML.Event | ||
162 | | PresenceStatus { presenceShow :: JabberShow | ||
163 | , presencePriority :: Maybe Int8 | ||
164 | , presenceStatus :: [(Lang,Text)] | ||
165 | , presenceWhiteList :: [Text] | ||
166 | } | ||
167 | | PresenceInformError | ||
168 | | PresenceInformSubscription Bool | ||
169 | | PresenceRequestStatus | ||
170 | | PresenceRequestSubscription Bool | ||
171 | | Message { msgThread :: Maybe MessageThread | ||
172 | , msgLangMap :: [(Lang,LangSpecificMessage)] | ||
173 | } | ||
174 | | NotifyClientVersion { versionName :: Text | ||
175 | , versionVersion :: Text } | ||
176 | | InternalEnableHack ClientHack | ||
177 | | InternalCacheId Text | ||
178 | deriving (Show,Eq) | ||
179 | |||
180 | data StanzaOrigin = LocalPeer | ||
181 | | PeerOrigin PeerAddress (TChan Stanza) | ||
182 | | ClientOrigin ClientAddress (TChan Stanza) | ||
183 | |||
184 | |||
185 | data StanzaWrap a = Stanza | ||
186 | { stanzaType :: StanzaType | ||
187 | , stanzaId :: Maybe Text | ||
188 | , stanzaTo :: Maybe Text | ||
189 | , stanzaFrom :: Maybe Text | ||
190 | , stanzaChan :: a | ||
191 | , stanzaClosers :: TVar (Maybe [XML.Event]) | ||
192 | , stanzaInterrupt :: TMVar () | ||
193 | , stanzaOrigin :: StanzaOrigin | ||
194 | } | ||
195 | |||
196 | type Stanza = StanzaWrap (LockedChan XML.Event) | ||
197 | 116 | ||
198 | newtype Local a = Local a deriving (Eq,Ord,Show) | 117 | newtype Local a = Local a deriving (Eq,Ord,Show) |
199 | newtype Remote a = Remote a deriving (Eq,Ord,Show) | 118 | newtype Remote a = Remote a deriving (Eq,Ord,Show) |
@@ -434,9 +353,6 @@ conduitToStanza stype mid from to c = do | |||
434 | } | 353 | } |
435 | 354 | ||
436 | 355 | ||
437 | ioWriteChan :: MonadIO m => TChan a -> a -> m () | ||
438 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | ||
439 | |||
440 | stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m () | 356 | stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m () |
441 | stanzaToConduit stanza = do | 357 | stanzaToConduit stanza = do |
442 | let xchan = stanzaChan stanza | 358 | let xchan = stanzaChan stanza |
@@ -564,60 +480,6 @@ stanzaFromList stype reply = do | |||
564 | , stanzaOrigin = LocalPeer | 480 | , stanzaOrigin = LocalPeer |
565 | } | 481 | } |
566 | 482 | ||
567 | grokStanzaIQGet :: Monad m => XML.Event -> NestingXML o m (Maybe StanzaType) | ||
568 | grokStanzaIQGet stanza = do | ||
569 | mtag <- nextElement | ||
570 | forM mtag $ \tag -> do | ||
571 | case tagName tag of | ||
572 | "{urn:xmpp:ping}ping" -> return Ping | ||
573 | "{jabber:iq:roster}query" -> return RequestRoster | ||
574 | name -> return $ UnrecognizedQuery name | ||
575 | |||
576 | parseClientVersion :: NestingXML o IO (Maybe StanzaType) | ||
577 | parseClientVersion = parseit Nothing Nothing | ||
578 | where | ||
579 | reportit mname mver = return $ do | ||
580 | name <- mname | ||
581 | ver <- mver | ||
582 | return NotifyClientVersion { versionName=name, versionVersion=ver } | ||
583 | parseit :: Maybe Text -> Maybe Text -> NestingXML o IO (Maybe StanzaType) | ||
584 | parseit mname mver = do | ||
585 | mtag <- nextElement | ||
586 | fromMaybe (reportit mname mver) $ mtag <&> \tag -> do | ||
587 | case tagName tag of | ||
588 | "{jabber:iq:version}name" -> do | ||
589 | x <- XML.content | ||
590 | parseit (Just x) mver | ||
591 | "{jabber:iq:version}version" -> do | ||
592 | x <- XML.content | ||
593 | parseit mname (Just x) | ||
594 | _ -> parseit mname mver | ||
595 | |||
596 | |||
597 | grokStanzaIQResult :: XML.Event -> NestingXML o IO (Maybe StanzaType) | ||
598 | grokStanzaIQResult stanza = do | ||
599 | mtag <- nextElement | ||
600 | fromMaybe (return $ Just Pong) $ mtag <&> \tag -> do | ||
601 | case tagName tag of | ||
602 | "{jabber:iq:version}query" | nameNamespace (tagName stanza)==Just "jabber:client" | ||
603 | -> parseClientVersion | ||
604 | _ -> return Nothing | ||
605 | |||
606 | grokStanzaIQSet :: XML.Event -> NestingXML o IO (Maybe StanzaType) | ||
607 | grokStanzaIQSet stanza = do | ||
608 | mtag <- nextElement | ||
609 | case tagName <$> mtag of | ||
610 | Just "{urn:ietf:params:xml:ns:xmpp-bind}bind" | ||
611 | -> do mchild <- nextElement | ||
612 | case tagName <$> mchild of | ||
613 | Just "{urn:ietf:params:xml:ns:xmpp-bind}resource" | ||
614 | -> do rsc <- XML.content -- TODO: MonadThrow??? | ||
615 | return . Just $ RequestResource Nothing (Just rsc) | ||
616 | Just _ -> return Nothing | ||
617 | Nothing -> return . Just $ RequestResource Nothing Nothing | ||
618 | Just "{urn:ietf:params:xml:ns:xmpp-session}session" | ||
619 | -> return $ Just SessionRequest | ||
620 | _ -> return Nothing | ||
621 | 483 | ||
622 | 484 | ||
623 | {- | 485 | {- |
@@ -628,197 +490,7 @@ C->Unrecognized xmlns="jabber:client"> | |||
628 | C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> | 490 | C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> |
629 | C->Unrecognized </iq> | 491 | C->Unrecognized </iq> |
630 | -} | 492 | -} |
631 | chanContents :: TChan x -> IO [x] | ||
632 | chanContents ch = do | ||
633 | x <- atomically $ do | ||
634 | bempty <- isEmptyTChan ch | ||
635 | if bempty | ||
636 | then return Nothing | ||
637 | else fmap Just $ readTChan ch | ||
638 | maybe (return []) | ||
639 | (\x -> do | ||
640 | xs <- chanContents ch | ||
641 | return (x:xs)) | ||
642 | x | ||
643 | |||
644 | |||
645 | parsePresenceStatus | ||
646 | :: ( MonadThrow m | ||
647 | , MonadIO m | ||
648 | ) => Text -> XML.Event -> NestingXML o m (Maybe StanzaType) | ||
649 | parsePresenceStatus ns stanzaTag = do | ||
650 | 493 | ||
651 | let toStat "away" = Away | ||
652 | toStat "xa" = ExtendedAway | ||
653 | toStat "dnd" = DoNotDisturb | ||
654 | toStat "chat" = Chatty | ||
655 | |||
656 | showv <- liftIO . atomically $ newTVar Available | ||
657 | priov <- liftIO . atomically $ newTVar Nothing | ||
658 | statusv <- liftIO . atomically $ newTChan | ||
659 | fix $ \loop -> do | ||
660 | mtag <- nextElement | ||
661 | forM_ mtag $ \tag -> do | ||
662 | when (nameNamespace (tagName tag) == Just ns) $ do | ||
663 | case nameLocalName (tagName tag) of | ||
664 | "show" -> do t <- XML.content | ||
665 | liftIO . atomically $ writeTVar showv (toStat t) | ||
666 | "priority" -> do t <- XML.content | ||
667 | liftIO . handleIO_ (return ()) $ do | ||
668 | prio <- readIO (Text.unpack t) | ||
669 | atomically $ writeTVar priov (Just prio) | ||
670 | "status" -> do t <- XML.content | ||
671 | lang <- xmlLang | ||
672 | ioWriteChan statusv (maybe "" id lang,t) | ||
673 | _ -> return () | ||
674 | loop | ||
675 | show <- liftIO . atomically $ readTVar showv | ||
676 | prio <- liftIO . atomically $ readTVar priov | ||
677 | status <- liftIO $ chanContents statusv -- Could use unsafeInterleaveIO to | ||
678 | -- avoid multiple passes, but whatever. | ||
679 | let wlist = do | ||
680 | w <- maybeToList $ lookupAttrib "whitelist" (tagAttrs stanzaTag) | ||
681 | Text.words w | ||
682 | return . Just $ PresenceStatus { presenceShow = show | ||
683 | , presencePriority = prio | ||
684 | , presenceStatus = status | ||
685 | , presenceWhiteList = wlist | ||
686 | } | ||
687 | grokPresence | ||
688 | :: ( MonadThrow m | ||
689 | , MonadIO m | ||
690 | ) => Text -> XML.Event -> NestingXML o m (Maybe StanzaType) | ||
691 | grokPresence ns stanzaTag = do | ||
692 | let typ = lookupAttrib "type" (tagAttrs stanzaTag) | ||
693 | case typ of | ||
694 | Nothing -> parsePresenceStatus ns stanzaTag | ||
695 | Just "unavailable" -> fmap (fmap (\p -> p {presenceShow=Offline})) | ||
696 | $ parsePresenceStatus ns stanzaTag | ||
697 | Just "error" -> return . Just $ PresenceInformError | ||
698 | Just "unsubscribed" -> return . Just $ PresenceInformSubscription False | ||
699 | Just "subscribed" -> return . Just $ PresenceInformSubscription True | ||
700 | Just "probe" -> return . Just $ PresenceRequestStatus | ||
701 | Just "unsubscribe" -> return . Just $ PresenceRequestSubscription False | ||
702 | Just "subscribe" -> return . Just $ PresenceRequestSubscription True | ||
703 | _ -> return Nothing | ||
704 | |||
705 | parseMessage | ||
706 | :: ( MonadThrow m | ||
707 | , MonadIO m | ||
708 | ) => Text -> XML.Event -> NestingXML o m StanzaType | ||
709 | parseMessage ns stanza = do | ||
710 | let bodytag = Name { nameNamespace = Just ns | ||
711 | , nameLocalName = "body" | ||
712 | , namePrefix = Nothing } | ||
713 | subjecttag = Name { nameNamespace = Just ns | ||
714 | , nameLocalName = "subject" | ||
715 | , namePrefix = Nothing } | ||
716 | threadtag = Name { nameNamespace = Just ns | ||
717 | , nameLocalName = "thread" | ||
718 | , namePrefix = Nothing } | ||
719 | let emptyMsg = LangSpecificMessage { msgBody=Nothing, msgSubject=Nothing } | ||
720 | parseChildren (th,cmap) = do | ||
721 | child <- nextElement | ||
722 | lvl <- nesting | ||
723 | xmllang <- xmlLang | ||
724 | let lang = maybe "" id xmllang | ||
725 | let c = maybe emptyMsg id (Map.lookup lang cmap) | ||
726 | -- log $ " child: "<> bshow child | ||
727 | case child of | ||
728 | Just tag | tagName tag==bodytag | ||
729 | -> do | ||
730 | txt <- XML.content | ||
731 | awaitCloser lvl | ||
732 | parseChildren (th,Map.insert lang (c { msgBody=Just txt }) cmap) | ||
733 | Just tag | tagName tag==subjecttag | ||
734 | -> do | ||
735 | txt <- XML.content | ||
736 | awaitCloser lvl | ||
737 | parseChildren (th,Map.insert lang (c { msgSubject=Just txt }) cmap) | ||
738 | Just tag | tagName tag==threadtag | ||
739 | -> do | ||
740 | txt <- XML.content | ||
741 | awaitCloser lvl | ||
742 | parseChildren (th {msgThreadContent=txt},cmap) | ||
743 | Just tag -> do | ||
744 | -- let nm = tagName tag | ||
745 | -- attrs = tagAttrs tag | ||
746 | -- -- elems = msgElements c | ||
747 | -- txt <- XML.content | ||
748 | awaitCloser lvl | ||
749 | parseChildren (th,Map.insert lang c cmap) | ||
750 | Nothing -> return (th,cmap) | ||
751 | (th,langmap) <- parseChildren ( MessageThread {msgThreadParent=Nothing, msgThreadContent=""} | ||
752 | , Map.empty ) | ||
753 | return Message { | ||
754 | msgLangMap = Map.toList langmap, | ||
755 | msgThread = if msgThreadContent th/="" then Just th else Nothing | ||
756 | } | ||
757 | |||
758 | findConditionTag :: Monad m => NestingXML o m (Maybe XML.Event) | ||
759 | findConditionTag = do | ||
760 | mx <- nextElement | ||
761 | fmap join $ forM mx $ \x -> do | ||
762 | case nameNamespace (tagName x) of | ||
763 | Just "urn:ietf:params:xml:ns:xmpp-stanzas" -> return (Just x) | ||
764 | _ -> findConditionTag | ||
765 | |||
766 | conditionFromText :: Text -> Maybe StanzaError | ||
767 | conditionFromText t = fmap fst $ listToMaybe ss | ||
768 | where | ||
769 | es = [BadRequest .. UnexpectedRequest] | ||
770 | ts = map (\e->(e,errorTagLocalName e)) es | ||
771 | ss = dropWhile ((/=t) . snd) ts | ||
772 | |||
773 | findErrorTag :: Monad m => Text -> NestingXML o m (Maybe StanzaError) | ||
774 | findErrorTag ns = do | ||
775 | x <- nextElement | ||
776 | fmap join $ forM x $ \x -> | ||
777 | case tagName x of | ||
778 | n | nameNamespace n==Just ns && nameLocalName n=="error" | ||
779 | -> do | ||
780 | mtag <- findConditionTag | ||
781 | return $ do | ||
782 | tag <- {- trace ("mtag = "++show mtag) -} mtag | ||
783 | let t = nameLocalName (tagName tag) | ||
784 | conditionFromText t | ||
785 | _ -> findErrorTag ns | ||
786 | |||
787 | grokMessage | ||
788 | :: ( MonadThrow m | ||
789 | , MonadIO m | ||
790 | ) => Text -> XML.Event -> NestingXML o m (Maybe StanzaType) | ||
791 | grokMessage ns stanzaTag = do | ||
792 | let typ = lookupAttrib "type" (tagAttrs stanzaTag) | ||
793 | case typ of | ||
794 | Just "error" -> do | ||
795 | mb <- findErrorTag ns | ||
796 | return $ do | ||
797 | e <- mb | ||
798 | return $ Error e stanzaTag | ||
799 | _ -> do t <- parseMessage ns stanzaTag | ||
800 | return $ Just t | ||
801 | |||
802 | |||
803 | |||
804 | grokStanza | ||
805 | :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType) | ||
806 | grokStanza "jabber:server" stanzaTag = | ||
807 | case () of | ||
808 | _ | stanzaTag `isServerIQOf` "get" -> grokStanzaIQGet stanzaTag | ||
809 | _ | stanzaTag `isServerIQOf` "result" -> grokStanzaIQResult stanzaTag | ||
810 | _ | tagName stanzaTag == "{jabber:server}presence" -> grokPresence "jabber:server" stanzaTag | ||
811 | _ | tagName stanzaTag == "{jabber:server}message" -> grokMessage "jabber:server" stanzaTag | ||
812 | _ -> return $ Just Unrecognized | ||
813 | |||
814 | grokStanza "jabber:client" stanzaTag = | ||
815 | case () of | ||
816 | _ | stanzaTag `isClientIQOf` "get" -> grokStanzaIQGet stanzaTag | ||
817 | _ | stanzaTag `isClientIQOf` "set" -> grokStanzaIQSet stanzaTag | ||
818 | _ | stanzaTag `isClientIQOf` "result" -> grokStanzaIQResult stanzaTag | ||
819 | _ | tagName stanzaTag == "{jabber:client}presence" -> grokPresence "jabber:client" stanzaTag | ||
820 | _ | tagName stanzaTag == "{jabber:client}message" -> grokMessage "jabber:client" stanzaTag | ||
821 | _ -> return $ Just Unrecognized | ||
822 | 494 | ||
823 | mkname :: Text -> Text -> XML.Name | 495 | mkname :: Text -> Text -> XML.Name |
824 | mkname namespace name = (Name name (Just namespace) Nothing) | 496 | mkname namespace name = (Name name (Just namespace) Nothing) |
@@ -1547,108 +1219,11 @@ socketFromKey sv (ClientAddress addr) = do | |||
1547 | Nothing -> return oops | 1219 | Nothing -> return oops |
1548 | Just cd -> return $ either (const oops) id $ cdAddr $ cdata cd | 1220 | Just cd -> return $ either (const oops) id $ cdAddr $ cdata cd |
1549 | 1221 | ||
1550 | class StanzaFirstTag a where | ||
1551 | stanzaFirstTag :: StanzaWrap a -> IO XML.Event | ||
1552 | instance StanzaFirstTag (TChan XML.Event) where | ||
1553 | stanzaFirstTag stanza = do | ||
1554 | e <-atomically $ peekTChan (stanzaChan stanza) | ||
1555 | return e | ||
1556 | instance StanzaFirstTag (LockedChan XML.Event) where | ||
1557 | stanzaFirstTag stanza = do | ||
1558 | e <-atomically $ peekLChan (stanzaChan stanza) | ||
1559 | return e | ||
1560 | instance StanzaFirstTag XML.Event where | ||
1561 | stanzaFirstTag stanza = return (stanzaChan stanza) | ||
1562 | |||
1563 | data StanzaError | ||
1564 | = BadRequest | ||
1565 | | Conflict | ||
1566 | | FeatureNotImplemented | ||
1567 | | Forbidden | ||
1568 | | Gone | ||
1569 | | InternalServerError | ||
1570 | | ItemNotFound | ||
1571 | | JidMalformed | ||
1572 | | NotAcceptable | ||
1573 | | NotAllowed | ||
1574 | | NotAuthorized | ||
1575 | | PaymentRequired | ||
1576 | | RecipientUnavailable | ||
1577 | | Redirect | ||
1578 | | RegistrationRequired | ||
1579 | | RemoteServerNotFound | ||
1580 | | RemoteServerTimeout | ||
1581 | | ResourceConstraint | ||
1582 | | ServiceUnavailable | ||
1583 | | SubscriptionRequired | ||
1584 | | UndefinedCondition | ||
1585 | | UnexpectedRequest | ||
1586 | deriving (Show,Enum,Ord,Eq) | ||
1587 | |||
1588 | xep0086 :: StanzaError -> (Text, Int) | ||
1589 | xep0086 e = | ||
1590 | case e of | ||
1591 | BadRequest -> ("modify", 400) | ||
1592 | Conflict -> ("cancel", 409) | ||
1593 | FeatureNotImplemented -> ("cancel", 501) | ||
1594 | Forbidden -> ("auth", 403) | ||
1595 | Gone -> ("modify", 302) | ||
1596 | InternalServerError -> ("wait", 500) | ||
1597 | ItemNotFound -> ("cancel", 404) | ||
1598 | JidMalformed -> ("modify", 400) | ||
1599 | NotAcceptable -> ("modify", 406) | ||
1600 | NotAllowed -> ("cancel", 405) | ||
1601 | NotAuthorized -> ("auth", 401) | ||
1602 | PaymentRequired -> ("auth", 402) | ||
1603 | RecipientUnavailable -> ("wait", 404) | ||
1604 | Redirect -> ("modify", 302) | ||
1605 | RegistrationRequired -> ("auth", 407) | ||
1606 | RemoteServerNotFound -> ("cancel", 404) | ||
1607 | RemoteServerTimeout -> ("wait", 504) | ||
1608 | ResourceConstraint -> ("wait", 500) | ||
1609 | ServiceUnavailable -> ("cancel", 503) | ||
1610 | SubscriptionRequired -> ("auth", 407) | ||
1611 | UndefinedCondition -> ("", 500) | ||
1612 | UnexpectedRequest -> ("wait", 400) | ||
1613 | |||
1614 | errorText :: StanzaError -> Text | ||
1615 | errorText e = | ||
1616 | case e of | ||
1617 | BadRequest -> "Bad request" | ||
1618 | Conflict -> "Conflict" | ||
1619 | FeatureNotImplemented -> "This feature is not implemented" | ||
1620 | Forbidden -> "Forbidden" | ||
1621 | Gone -> "Recipient can no longer be contacted" | ||
1622 | InternalServerError -> "Internal server error" | ||
1623 | ItemNotFound -> "Item not found" | ||
1624 | JidMalformed -> "JID Malformed" | ||
1625 | NotAcceptable -> "Message was rejected" | ||
1626 | NotAllowed -> "Not allowed" | ||
1627 | NotAuthorized -> "Not authorized" | ||
1628 | PaymentRequired -> "Payment is required" | ||
1629 | RecipientUnavailable -> "Recipient is unavailable" | ||
1630 | Redirect -> "Redirect" | ||
1631 | RegistrationRequired -> "Registration required" | ||
1632 | RemoteServerNotFound -> "Recipient's server not found" | ||
1633 | RemoteServerTimeout -> "Remote server timeout" | ||
1634 | ResourceConstraint -> "The server is low on resources" | ||
1635 | ServiceUnavailable -> "The service is unavailable" | ||
1636 | SubscriptionRequired -> "A subscription is required" | ||
1637 | UndefinedCondition -> "Undefined condition" | ||
1638 | UnexpectedRequest -> "Unexpected request" | ||
1639 | |||
1640 | eventContent :: Maybe [Content] -> Text | 1222 | eventContent :: Maybe [Content] -> Text |
1641 | eventContent cs = maybe "" (foldr1 (<>) . map content1) cs | 1223 | eventContent cs = maybe "" (foldr1 (<>) . map content1) cs |
1642 | where content1 (ContentText t) = t | 1224 | where content1 (ContentText t) = t |
1643 | content1 (ContentEntity t) = t | 1225 | content1 (ContentEntity t) = t |
1644 | 1226 | ||
1645 | errorTagLocalName :: forall a. Show a => a -> Text | ||
1646 | errorTagLocalName e = Text.pack . drop 1 $ do | ||
1647 | c <- show e | ||
1648 | if 'A' <= c && c <= 'Z' | ||
1649 | then [ '-', chr( ord c - ord 'A' + ord 'a') ] | ||
1650 | else return c | ||
1651 | |||
1652 | makeErrorStanza :: StanzaFirstTag a => StanzaWrap a -> IO [XML.Event] | 1227 | makeErrorStanza :: StanzaFirstTag a => StanzaWrap a -> IO [XML.Event] |
1653 | makeErrorStanza stanza = do | 1228 | makeErrorStanza stanza = do |
1654 | startTag <- stanzaFirstTag stanza | 1229 | startTag <- stanzaFirstTag stanza |