diff options
author | joe <joe@jerkface.net> | 2014-02-21 13:13:40 -0500 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-02-21 13:13:40 -0500 |
commit | acad0d6ef22cbb75cdc4163c5163306a994cd792 (patch) | |
tree | a5d441398ec131b7a9d3c485c220ac59b7c2b1b4 /Presence/XMPPServer.hs | |
parent | ac3702dd365691cc9abf37248633f00f1e06cb12 (diff) |
pidgin hack, error message in simulated chat.
Diffstat (limited to 'Presence/XMPPServer.hs')
-rw-r--r-- | Presence/XMPPServer.hs | 405 |
1 files changed, 368 insertions, 37 deletions
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index d41e06cb..bd4c56c4 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -1,6 +1,7 @@ | |||
1 | {-# LANGUAGE CPP #-} | 1 | {-# LANGUAGE CPP #-} |
2 | {-# LANGUAGE OverloadedStrings #-} | 2 | {-# LANGUAGE OverloadedStrings #-} |
3 | {-# LANGUAGE RankNTypes #-} | 3 | {-# LANGUAGE RankNTypes #-} |
4 | {-# LANGUAGE FlexibleInstances #-} -- instance for TChan Event | ||
4 | module XMPPServer | 5 | module XMPPServer |
5 | ( xmppServer | 6 | ( xmppServer |
6 | , ConnectionKey(..) | 7 | , ConnectionKey(..) |
@@ -49,7 +50,7 @@ import Data.Maybe | |||
49 | import Data.Monoid ( (<>) ) | 50 | import Data.Monoid ( (<>) ) |
50 | import Data.Text (Text) | 51 | import Data.Text (Text) |
51 | import qualified Data.Text as Text (pack,unpack) | 52 | import qualified Data.Text as Text (pack,unpack) |
52 | import Data.Char (toUpper) | 53 | import Data.Char (toUpper,chr,ord) |
53 | import Data.Map (Map) | 54 | import Data.Map (Map) |
54 | import qualified Data.Map as Map | 55 | import qualified Data.Map as Map |
55 | import Data.Set (Set, (\\) ) | 56 | import Data.Set (Set, (\\) ) |
@@ -112,6 +113,9 @@ data RosterEventType | |||
112 | | RejectSubscriber | 113 | | RejectSubscriber |
113 | deriving (Show,Read,Ord,Eq,Enum) | 114 | deriving (Show,Read,Ord,Eq,Enum) |
114 | 115 | ||
116 | data ClientHack = SimulatedChatErrors | ||
117 | deriving (Show,Read,Ord,Eq,Enum) | ||
118 | |||
115 | data StanzaType | 119 | data StanzaType |
116 | = Unrecognized | 120 | = Unrecognized |
117 | | Ping | 121 | | Ping |
@@ -125,7 +129,7 @@ data StanzaType | |||
125 | | RosterEvent { rosterEventType :: RosterEventType | 129 | | RosterEvent { rosterEventType :: RosterEventType |
126 | , rosterUser :: Text | 130 | , rosterUser :: Text |
127 | , rosterContact :: Text } | 131 | , rosterContact :: Text } |
128 | | Error | 132 | | Error StanzaError XML.Event |
129 | | PresenceStatus { presenceShow :: JabberShow | 133 | | PresenceStatus { presenceShow :: JabberShow |
130 | , presencePriority :: Maybe Int8 | 134 | , presencePriority :: Maybe Int8 |
131 | , presenceStatus :: [(Lang,Text)] | 135 | , presenceStatus :: [(Lang,Text)] |
@@ -137,7 +141,11 @@ data StanzaType | |||
137 | | Message { msgThread :: Maybe MessageThread | 141 | | Message { msgThread :: Maybe MessageThread |
138 | , msgLangMap :: [(Lang,LangSpecificMessage)] | 142 | , msgLangMap :: [(Lang,LangSpecificMessage)] |
139 | } | 143 | } |
140 | deriving Show | 144 | | NotifyClientVersion { versionName :: Text |
145 | , versionVersion :: Text } | ||
146 | | InternalEnableHack ClientHack | ||
147 | | InternalCacheId Text | ||
148 | deriving (Show,Eq) | ||
141 | 149 | ||
142 | data StanzaOrigin = LocalPeer | NetworkOrigin ConnectionKey (TChan Stanza) | 150 | data StanzaOrigin = LocalPeer | NetworkOrigin ConnectionKey (TChan Stanza) |
143 | 151 | ||
@@ -175,6 +183,24 @@ data XMPPServerParameters = | |||
175 | } | 183 | } |
176 | 184 | ||
177 | 185 | ||
186 | enableClientHacks "Pidgin" version replyto = do | ||
187 | wlog "Enabling hack SimulatedChatErrors for client Pidgin" | ||
188 | donevar <- atomically newEmptyTMVar | ||
189 | sendReply donevar | ||
190 | (InternalEnableHack SimulatedChatErrors) | ||
191 | [] | ||
192 | replyto | ||
193 | enableClientHacks _ _ _ = return () | ||
194 | |||
195 | cacheMessageId id' replyto = do | ||
196 | wlog $ "Caching id " ++ Text.unpack id' | ||
197 | donevar <- atomically newEmptyTMVar | ||
198 | sendReply donevar | ||
199 | (InternalCacheId id') | ||
200 | [] | ||
201 | replyto | ||
202 | |||
203 | |||
178 | -- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error | 204 | -- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error |
179 | -- client connection | 205 | -- client connection |
180 | -- socat script to send stanza fragment | 206 | -- socat script to send stanza fragment |
@@ -303,6 +329,27 @@ conduitToChan c = do | |||
303 | atomically $ writeTVar clsrs Nothing | 329 | atomically $ writeTVar clsrs Nothing |
304 | return (chan,clsrs,quitvar) | 330 | return (chan,clsrs,quitvar) |
305 | 331 | ||
332 | conduitToStanza | ||
333 | :: StanzaType | ||
334 | -> Maybe Text -- ^ id | ||
335 | -> Maybe Text -- ^ from | ||
336 | -> Maybe Text -- ^ to | ||
337 | -> Conduit () IO Event | ||
338 | -> IO Stanza | ||
339 | conduitToStanza stype mid from to c = do | ||
340 | (chan,clsrs,quitvar) <- conduitToChan c | ||
341 | return | ||
342 | Stanza { stanzaType = stype | ||
343 | , stanzaId = mid | ||
344 | , stanzaTo = to | ||
345 | , stanzaFrom = from | ||
346 | , stanzaChan = chan | ||
347 | , stanzaClosers = clsrs | ||
348 | , stanzaInterrupt = quitvar | ||
349 | , stanzaOrigin = LocalPeer | ||
350 | } | ||
351 | |||
352 | |||
306 | ioWriteChan :: MonadIO m => TChan a -> a -> m () | 353 | ioWriteChan :: MonadIO m => TChan a -> a -> m () |
307 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | 354 | ioWriteChan c v = liftIO . atomically $ writeTChan c v |
308 | 355 | ||
@@ -358,12 +405,16 @@ sendModifiedStanzaToClient stanza chan = do | |||
358 | -- todo: this should probably be restricted to IO monad | 405 | -- todo: this should probably be restricted to IO monad |
359 | sendReply :: (Functor m, MonadIO m) => TMVar () -> StanzaType -> [Event] -> TChan Stanza -> m () | 406 | sendReply :: (Functor m, MonadIO m) => TMVar () -> StanzaType -> [Event] -> TChan Stanza -> m () |
360 | sendReply donevar stype reply replychan = do | 407 | sendReply donevar stype reply replychan = do |
361 | if null reply then return () | 408 | let stanzaTag = listToMaybe reply |
362 | else do | 409 | mid = stanzaTag >>= lookupAttrib "id" . tagAttrs |
363 | let stanzaTag = head reply | 410 | mfrom = stanzaTag >>= lookupAttrib "from" . tagAttrs |
364 | mid = lookupAttrib "id" (tagAttrs stanzaTag) | 411 | mto = stanzaTag >>= lookupAttrib "to" . tagAttrs |
365 | mfrom = lookupAttrib "from" (tagAttrs stanzaTag) | 412 | isInternal (InternalEnableHack {}) = True |
366 | mto = lookupAttrib "to" (tagAttrs stanzaTag) | 413 | isInternal (InternalCacheId {}) = True |
414 | isInternal _ = False | ||
415 | flip (maybe $ return ()) | ||
416 | (fmap (const ()) stanzaTag `mplus` guard (isInternal stype)) | ||
417 | . const $ do | ||
367 | replyStanza <- liftIO . atomically $ do | 418 | replyStanza <- liftIO . atomically $ do |
368 | replyChan <- newTChan | 419 | replyChan <- newTChan |
369 | replyClsrs <- newTVar (Just []) | 420 | replyClsrs <- newTVar (Just []) |
@@ -391,11 +442,34 @@ grokStanzaIQGet stanza = do | |||
391 | "{jabber:iq:roster}query" -> return $ Just RequestRoster | 442 | "{jabber:iq:roster}query" -> return $ Just RequestRoster |
392 | name -> return . Just $ UnrecognizedQuery name | 443 | name -> return . Just $ UnrecognizedQuery name |
393 | 444 | ||
394 | grokStanzaIQResult :: Monad m => XML.Event -> NestingXML o m (Maybe StanzaType) | 445 | parseClientVersion :: NestingXML o IO (Maybe StanzaType) |
446 | parseClientVersion = parseit Nothing Nothing | ||
447 | where | ||
448 | reportit mname mver = return $ do | ||
449 | name <- mname | ||
450 | ver <- mver | ||
451 | return NotifyClientVersion { versionName=name, versionVersion=ver } | ||
452 | parseit :: Maybe Text -> Maybe Text -> NestingXML o IO (Maybe StanzaType) | ||
453 | parseit mname mver = do | ||
454 | mtag <- nextElement | ||
455 | flip (maybe $ reportit mname mver) mtag $ \tag -> do | ||
456 | case tagName tag of | ||
457 | "{jabber:iq:version}name" -> do | ||
458 | x <- XML.content | ||
459 | parseit (Just x) mver | ||
460 | "{jabber:iq:version}version" -> do | ||
461 | x <- XML.content | ||
462 | parseit mname (Just x) | ||
463 | _ -> parseit mname mver | ||
464 | |||
465 | |||
466 | grokStanzaIQResult :: XML.Event -> NestingXML o IO (Maybe StanzaType) | ||
395 | grokStanzaIQResult stanza = do | 467 | grokStanzaIQResult stanza = do |
396 | mtag <- nextElement | 468 | mtag <- nextElement |
397 | flip (maybe $ return (Just Pong)) mtag $ \tag -> do | 469 | flip (maybe $ return (Just Pong)) mtag $ \tag -> do |
398 | case tagName tag of | 470 | case tagName tag of |
471 | "{jabber:iq:version}query" | nameNamespace (tagName stanza)==Just "jabber:client" | ||
472 | -> parseClientVersion | ||
399 | _ -> return Nothing | 473 | _ -> return Nothing |
400 | 474 | ||
401 | grokStanzaIQSet :: XML.Event -> NestingXML o IO (Maybe StanzaType) | 475 | grokStanzaIQSet :: XML.Event -> NestingXML o IO (Maybe StanzaType) |
@@ -598,13 +672,12 @@ xmppInbound :: Server ConnectionKey SockAddr | |||
598 | -> XMPPServerParameters | 672 | -> XMPPServerParameters |
599 | -> ConnectionKey | 673 | -> ConnectionKey |
600 | -> SockAddr | 674 | -> SockAddr |
601 | -> FlagCommand | 675 | -> FlagCommand -- ^ action to check whether the connection needs a ping |
602 | -> Source IO XML.Event | 676 | -> TChan Stanza -- ^ channel to announce incomming stanzas on |
603 | -> TChan Stanza | 677 | -> TChan Stanza -- ^ channel used to send stanzas |
604 | -> TChan Stanza | 678 | -> TMVar () -- ^ mvar that is filled when the connection quits |
605 | -> TMVar () | ||
606 | -> Sink XML.Event IO () | 679 | -> Sink XML.Event IO () |
607 | xmppInbound sv xmpp k laddr pingflag src stanzas output donevar = doNestingXML $ do | 680 | xmppInbound sv xmpp k laddr pingflag stanzas output donevar = doNestingXML $ do |
608 | let (namespace,tellmyname,tellyourname) = case k of | 681 | let (namespace,tellmyname,tellyourname) = case k of |
609 | ClientKey {} -> ( "jabber:client" | 682 | ClientKey {} -> ( "jabber:client" |
610 | , xmppTellMyNameToClient xmpp | 683 | , xmppTellMyNameToClient xmpp |
@@ -726,9 +799,10 @@ streamFeatures "jabber:server" = | |||
726 | 799 | ||
727 | 800 | ||
728 | greet' :: Text -> Text -> [XML.Event] | 801 | greet' :: Text -> Text -> [XML.Event] |
729 | greet' namespace host = | 802 | greet' namespace host = EventBeginDocument : greet'' namespace host |
730 | [ EventBeginDocument | 803 | |
731 | , EventBeginElement (streamP "stream") | 804 | greet'' namespace host = |
805 | [ EventBeginElement (streamP "stream") | ||
732 | [("from",[ContentText host]) | 806 | [("from",[ContentText host]) |
733 | ,("id",[ContentText "someid"]) | 807 | ,("id",[ContentText "someid"]) |
734 | ,("xmlns",[ContentText namespace]) | 808 | ,("xmlns",[ContentText namespace]) |
@@ -770,6 +844,18 @@ iq_bind_reply mid jid = | |||
770 | , EventEndElement "{urn:ietf:params:xml:ns:xmpp-bind}jid" | 844 | , EventEndElement "{urn:ietf:params:xml:ns:xmpp-bind}jid" |
771 | , EventEndElement "{urn:ietf:params:xml:ns:xmpp-bind}bind" | 845 | , EventEndElement "{urn:ietf:params:xml:ns:xmpp-bind}bind" |
772 | , EventEndElement "{jabber:client}iq" | 846 | , EventEndElement "{jabber:client}iq" |
847 | |||
848 | {- | ||
849 | -- query for client version | ||
850 | , EventBeginElement "{jabber:client}iq" | ||
851 | [ attr "to" jid | ||
852 | , attr "from" hostname | ||
853 | , attr "type" "get" | ||
854 | , attr "id" "version"] | ||
855 | , EventBeginElement "{jabber:iq:version}query" [] | ||
856 | , EventEndElement "{jabber:iq:version}query" | ||
857 | , EventEndElement "{jabber:client}iq" | ||
858 | -} | ||
773 | ] | 859 | ] |
774 | 860 | ||
775 | iq_session_reply :: Maybe Text -> Text -> [XML.Event] | 861 | iq_session_reply :: Maybe Text -> Text -> [XML.Event] |
@@ -845,6 +931,26 @@ goodbye = | |||
845 | , EventEndDocument | 931 | , EventEndDocument |
846 | ] | 932 | ] |
847 | 933 | ||
934 | simulateChatError err mfrom = | ||
935 | [ EventBeginElement "{jabber:client}message" | ||
936 | ((maybe id (\t->(attr "from" t:)) mfrom) | ||
937 | [attr "type" "normal" ]) | ||
938 | , EventBeginElement "{jabber:client}body" [] | ||
939 | , EventContent $ ContentText ("/me " <> errorText err) | ||
940 | , EventEndElement "{jabber:client}body" | ||
941 | , EventBeginElement "{http://jabber.org/protocol/xhtml-im}html" [] | ||
942 | , EventBeginElement "{http://www.w3.org/1999/xhtml}body" [] | ||
943 | , EventBeginElement "{http://www.w3.org/1999/xhtml}p" | ||
944 | [ attr "style" "font-weight:bold; color:red" | ||
945 | ] | ||
946 | , EventContent $ ContentText ("/me " <> errorText err) | ||
947 | , EventEndElement "{http://www.w3.org/1999/xhtml}p" | ||
948 | , EventEndElement "{http://www.w3.org/1999/xhtml}body" | ||
949 | , EventEndElement "{http://jabber.org/protocol/xhtml-im}html" | ||
950 | , EventEndElement "{jabber:client}message" | ||
951 | ] | ||
952 | |||
953 | |||
848 | forkConnection :: Server ConnectionKey SockAddr | 954 | forkConnection :: Server ConnectionKey SockAddr |
849 | -> XMPPServerParameters | 955 | -> XMPPServerParameters |
850 | -> ConnectionKey | 956 | -> ConnectionKey |
@@ -917,6 +1023,8 @@ forkConnection sv xmpp k laddr pingflag src snk stanzas = do | |||
917 | mapM_ fail es' -- TODO: queue or save these for re-connect? | 1023 | mapM_ fail es' -- TODO: queue or save these for re-connect? |
918 | wlog $ "end post-queue fork: " ++ show k | 1024 | wlog $ "end post-queue fork: " ++ show k |
919 | output <- atomically newTChan | 1025 | output <- atomically newTChan |
1026 | hacks <- atomically $ newTVar Map.empty | ||
1027 | msgids <- atomically $ newTVar [] | ||
920 | forkIO $ do | 1028 | forkIO $ do |
921 | -- mapM_ (atomically . Slotted.push slots Nothing) greetPeer | 1029 | -- mapM_ (atomically . Slotted.push slots Nothing) greetPeer |
922 | fix $ \loop -> do | 1030 | fix $ \loop -> do |
@@ -936,9 +1044,33 @@ forkConnection sv xmpp k laddr pingflag src snk stanzas = do | |||
936 | PeerKey {} -> "P" | 1044 | PeerKey {} -> "P" |
937 | wlog "" | 1045 | wlog "" |
938 | stanzaToConduit dup $$ prettyPrint typ | 1046 | stanzaToConduit dup $$ prettyPrint typ |
1047 | case stanzaType stanza of | ||
1048 | InternalEnableHack hack -> do | ||
1049 | -- wlog $ "enable hack: " ++ show hack | ||
1050 | atomically $ modifyTVar' hacks (Map.insert hack ()) | ||
1051 | InternalCacheId x -> do | ||
1052 | -- wlog $ "cache id thread: " ++ show x | ||
1053 | atomically $ modifyTVar' msgids (take 3 . (x:)) | ||
1054 | _ -> return () | ||
939 | stanzaToConduit stanza =$= wrapStanzaConduit stanza | 1055 | stanzaToConduit stanza =$= wrapStanzaConduit stanza |
940 | $$ awaitForever | 1056 | $$ awaitForever |
941 | $ liftIO . atomically . Slotted.push slots Nothing | 1057 | $ liftIO . atomically . Slotted.push slots Nothing |
1058 | case stanzaType stanza of | ||
1059 | Error err tag | tagName tag=="{jabber:client}message" -> do | ||
1060 | -- ids <- atomically $ readTVar msgids | ||
1061 | -- wlog $ "ids = " ++ show (stanzaId stanza, ids) | ||
1062 | b <- atomically $ do m <- readTVar hacks | ||
1063 | cached <- readTVar msgids | ||
1064 | flip (maybe $ return False) (stanzaId stanza) $ \id' -> do | ||
1065 | return $ Map.member SimulatedChatErrors m | ||
1066 | && elem id' cached | ||
1067 | when b $ do | ||
1068 | let sim = simulateChatError err (stanzaFrom stanza) | ||
1069 | wlog $ "sending simulated chat for error message." | ||
1070 | CL.sourceList sim =$= wrapStanzaConduit stanza -- not quite right, but whatever | ||
1071 | $$ awaitForever | ||
1072 | $ liftIO . atomically . Slotted.push slots Nothing | ||
1073 | _ -> return () | ||
942 | loop | 1074 | loop |
943 | ,do pingflag >>= check | 1075 | ,do pingflag >>= check |
944 | return $ do | 1076 | return $ do |
@@ -963,7 +1095,7 @@ forkConnection sv xmpp k laddr pingflag src snk stanzas = do | |||
963 | wlog $ "end pre-queue fork: " ++ show k | 1095 | wlog $ "end pre-queue fork: " ++ show k |
964 | forkIO $ do | 1096 | forkIO $ do |
965 | -- src $$ awaitForever (lift . putStrLn . takeWhile (/=' ') . show) | 1097 | -- src $$ awaitForever (lift . putStrLn . takeWhile (/=' ') . show) |
966 | src $$ xmppInbound sv xmpp k laddr pingflag src stanzas output rdone | 1098 | src $$ xmppInbound sv xmpp k laddr pingflag stanzas output rdone |
967 | atomically $ putTMVar rdone () | 1099 | atomically $ putTMVar rdone () |
968 | wlog $ "end reader fork: " ++ show k | 1100 | wlog $ "end reader fork: " ++ show k |
969 | return output | 1101 | return output |
@@ -1017,6 +1149,7 @@ sendRoster query xmpp replyto = do | |||
1017 | jid <- case k of | 1149 | jid <- case k of |
1018 | ClientKey {} -> xmppTellClientHisName xmpp k -- LookupClientJID xmpp k | 1150 | ClientKey {} -> xmppTellClientHisName xmpp k -- LookupClientJID xmpp k |
1019 | PeerKey {} -> xmppTellClientNameOfPeer xmpp k | 1151 | PeerKey {} -> xmppTellClientNameOfPeer xmpp k |
1152 | hostname <- xmppTellMyNameToClient xmpp | ||
1020 | let getlist f = do | 1153 | let getlist f = do |
1021 | bs <- f xmpp k | 1154 | bs <- f xmpp k |
1022 | -- js <- mapM parseHostNameJID bs | 1155 | -- js <- mapM parseHostNameJID bs |
@@ -1041,17 +1174,23 @@ sendRoster query xmpp replyto = do | |||
1041 | xmlifyRosterItems solicited "none" subnone | 1174 | xmlifyRosterItems solicited "none" subnone |
1042 | yield $ EventEndElement "{jabber:iq:roster}query" | 1175 | yield $ EventEndElement "{jabber:iq:roster}query" |
1043 | yield $ EventEndElement "{jabber:client}iq" | 1176 | yield $ EventEndElement "{jabber:client}iq" |
1044 | (chan,clsrs,quitvar) <- conduitToChan roster | 1177 | |
1045 | ioWriteChan replyto | 1178 | conduitToStanza Roster |
1046 | Stanza { stanzaType = Roster | 1179 | (stanzaId query) |
1047 | , stanzaId = (stanzaId query) | 1180 | Nothing |
1048 | , stanzaTo = Just jid | 1181 | (Just jid) |
1049 | , stanzaFrom = Nothing | 1182 | roster >>= ioWriteChan replyto |
1050 | , stanzaChan = chan | 1183 | {- |
1051 | , stanzaClosers = clsrs | 1184 | let debugpresence = |
1052 | , stanzaInterrupt = quitvar | 1185 | [ EventBeginElement "{jabber:client}presence" |
1053 | , stanzaOrigin = LocalPeer | 1186 | [ attr "from" "guest@oxio4inifatsetlx.onion" |
1054 | } | 1187 | , attr "to" jid] |
1188 | , EventEndElement "{jabber:client}presence" | ||
1189 | ] | ||
1190 | quitvar <- atomically newEmptyTMVar | ||
1191 | sendReply quitvar Unrecognized debugpresence replyto | ||
1192 | -} | ||
1193 | |||
1055 | 1194 | ||
1056 | socketFromKey :: Server ConnectionKey SockAddr -> ConnectionKey -> IO SockAddr | 1195 | socketFromKey :: Server ConnectionKey SockAddr -> ConnectionKey -> IO SockAddr |
1057 | socketFromKey sv k = do | 1196 | socketFromKey sv k = do |
@@ -1065,6 +1204,152 @@ socketFromKey sv k = do | |||
1065 | -- Shouldnt happen anyway. | 1204 | -- Shouldnt happen anyway. |
1066 | Just cd -> return $ cdata cd | 1205 | Just cd -> return $ cdata cd |
1067 | 1206 | ||
1207 | class StanzaFirstTag a where | ||
1208 | stanzaFirstTag :: StanzaWrap a -> IO XML.Event | ||
1209 | instance StanzaFirstTag (TChan XML.Event) where | ||
1210 | stanzaFirstTag stanza = do | ||
1211 | e <-atomically $ peekTChan (stanzaChan stanza) | ||
1212 | return e | ||
1213 | |||
1214 | data StanzaError | ||
1215 | = BadRequest | ||
1216 | | Conflict | ||
1217 | | FeatureNotImplemented | ||
1218 | | Forbidden | ||
1219 | | Gone | ||
1220 | | InternalServerError | ||
1221 | | ItemNotFound | ||
1222 | | JidMalformed | ||
1223 | | NotAcceptable | ||
1224 | | NotAllowed | ||
1225 | | NotAuthorized | ||
1226 | | PaymentRequired | ||
1227 | | RecipientUnavailable | ||
1228 | | Redirect | ||
1229 | | RegistrationRequired | ||
1230 | | RemoteServerNotFound | ||
1231 | | RemoteServerTimeout | ||
1232 | | ResourceConstraint | ||
1233 | | ServiceUnavailable | ||
1234 | | SubscriptionRequired | ||
1235 | | UndefinedCondition | ||
1236 | | UnexpectedRequest | ||
1237 | deriving (Show,Enum,Ord,Eq) | ||
1238 | |||
1239 | xep0086 e = | ||
1240 | case e of | ||
1241 | BadRequest -> ("modify", 400) | ||
1242 | Conflict -> ("cancel", 409) | ||
1243 | FeatureNotImplemented -> ("cancel", 501) | ||
1244 | Forbidden -> ("auth", 403) | ||
1245 | Gone -> ("modify", 302) | ||
1246 | InternalServerError -> ("wait", 500) | ||
1247 | ItemNotFound -> ("cancel", 404) | ||
1248 | JidMalformed -> ("modify", 400) | ||
1249 | NotAcceptable -> ("modify", 406) | ||
1250 | NotAllowed -> ("cancel", 405) | ||
1251 | NotAuthorized -> ("auth", 401) | ||
1252 | PaymentRequired -> ("auth", 402) | ||
1253 | RecipientUnavailable -> ("wait", 404) | ||
1254 | Redirect -> ("modify", 302) | ||
1255 | RegistrationRequired -> ("auth", 407) | ||
1256 | RemoteServerNotFound -> ("cancel", 404) | ||
1257 | RemoteServerTimeout -> ("wait", 504) | ||
1258 | ResourceConstraint -> ("wait", 500) | ||
1259 | ServiceUnavailable -> ("cancel", 503) | ||
1260 | SubscriptionRequired -> ("auth", 407) | ||
1261 | UndefinedCondition -> ("", 500) | ||
1262 | UnexpectedRequest -> ("wait", 400) | ||
1263 | |||
1264 | errorText :: StanzaError -> Text | ||
1265 | errorText e = | ||
1266 | case e of | ||
1267 | BadRequest -> "Bad request" | ||
1268 | Conflict -> "Conflict" | ||
1269 | FeatureNotImplemented -> "This feature is not implemented" | ||
1270 | Forbidden -> "Forbidden" | ||
1271 | Gone -> "Recipient can no longer be contacted" | ||
1272 | InternalServerError -> "Internal server error" | ||
1273 | ItemNotFound -> "Item not found" | ||
1274 | JidMalformed -> "JID Malformed" | ||
1275 | NotAcceptable -> "Message was rejected" | ||
1276 | NotAllowed -> "Not allowed" | ||
1277 | NotAuthorized -> "Not authorized" | ||
1278 | PaymentRequired -> "Payment is required" | ||
1279 | RecipientUnavailable -> "Recipient is unavailable" | ||
1280 | Redirect -> "Redirect" | ||
1281 | RegistrationRequired -> "Registration required" | ||
1282 | RemoteServerNotFound -> "Recipient's server not found" | ||
1283 | RemoteServerTimeout -> "Remote server timeout" | ||
1284 | ResourceConstraint -> "The server is low on resources" | ||
1285 | ServiceUnavailable -> "The service is unavailable" | ||
1286 | SubscriptionRequired -> "A subscription is required" | ||
1287 | UndefinedCondition -> "Undefined condition" | ||
1288 | UnexpectedRequest -> "Unexpected request" | ||
1289 | |||
1290 | eventContent cs = maybe "" (foldr1 (<>) . map content1) cs | ||
1291 | where content1 (ContentText t) = t | ||
1292 | content1 (ContentEntity t) = t | ||
1293 | |||
1294 | makeErrorStanza :: StanzaFirstTag a => StanzaWrap a -> IO [XML.Event] | ||
1295 | makeErrorStanza stanza = do | ||
1296 | startTag <- stanzaFirstTag stanza | ||
1297 | let n = tagName startTag | ||
1298 | endTag = EventEndElement n | ||
1299 | amap0 = Map.fromList (tagAttrs startTag) | ||
1300 | mto = Map.lookup "to" amap0 | ||
1301 | mfrom = Map.lookup "from" amap0 | ||
1302 | mtype = Map.lookup "type" amap0 | ||
1303 | mid = Map.lookup "id" amap0 | ||
1304 | amap1 = Map.alter (const mto) "from" amap0 | ||
1305 | -- amap2 = Map.alter (const $ Just $ [ContentText "blackbird"]) {-mfrom)-} "to" amap1 | ||
1306 | amap2 = Map.alter (const mfrom) "to" amap1 | ||
1307 | amap3 = Map.insert "type" [XML.ContentText "error"] amap2 | ||
1308 | startTag' = EventBeginElement | ||
1309 | (tagName startTag) | ||
1310 | (Map.toList amap3) | ||
1311 | -- err = Gone -- FeatureNotImplemented -- UndefinedCondition -- RecipientUnavailable | ||
1312 | err = RecipientUnavailable | ||
1313 | errname = n { nameLocalName = "error" } | ||
1314 | -- errattrs = [attr "type" "wait"] -- "modify"] | ||
1315 | errorAttribs e xs = ys ++ xs -- todo replace instead of append | ||
1316 | where (typ,code) = xep0086 e | ||
1317 | ys = [attr "type" typ, attr "code" (Text.pack . show $ code)] | ||
1318 | errorTagLocalName e = Text.pack . drop 1 $ do | ||
1319 | c <- show e | ||
1320 | if 'A' <= c && c <= 'Z' | ||
1321 | then [ '-', chr( ord c - ord 'A' + ord 'a') ] | ||
1322 | else return c | ||
1323 | errorTagName = Name { nameNamespace = Just "urn:ietf:params:xml:ns:xmpp-stanzas" | ||
1324 | , nameLocalName = errorTagLocalName err | ||
1325 | , namePrefix = Nothing } | ||
1326 | errattrs = errorAttribs err [] | ||
1327 | let wlogd v s = do | ||
1328 | wlog $ "error "++show (lookupAttrib "id" $ tagAttrs startTag)++" " ++ v ++ " = " ++ show s | ||
1329 | {- | ||
1330 | wlogd "amap0" amap0 | ||
1331 | wlogd "mto" mto | ||
1332 | wlogd "mfrom" mfrom | ||
1333 | wlogd "amap3" amap3 | ||
1334 | -} | ||
1335 | if eventContent mtype=="error" then return [] else do | ||
1336 | return [ startTag' | ||
1337 | , EventBeginElement errname errattrs | ||
1338 | , EventBeginElement errorTagName [] | ||
1339 | , EventEndElement errorTagName | ||
1340 | , EventEndElement errname | ||
1341 | {- | ||
1342 | , EventBeginElement "{jabber:client}body" [] | ||
1343 | , EventContent (ContentText "what?") | ||
1344 | , EventEndElement "{jabber:client}body" | ||
1345 | -} | ||
1346 | {- | ||
1347 | , EventBeginElement "{154ae29f-98f2-4af4-826d-a40c8a188574}dummy" [] | ||
1348 | , EventEndElement "{154ae29f-98f2-4af4-826d-a40c8a188574}dummy" | ||
1349 | -} | ||
1350 | , endTag | ||
1351 | ] | ||
1352 | |||
1068 | monitor :: | 1353 | monitor :: |
1069 | Server ConnectionKey SockAddr | 1354 | Server ConnectionKey SockAddr |
1070 | -> ConnectionParameters ConnectionKey SockAddr | 1355 | -> ConnectionParameters ConnectionKey SockAddr |
@@ -1100,11 +1385,6 @@ monitor sv params xmpp = do | |||
1100 | return dup | 1385 | return dup |
1101 | _ -> return stanza | 1386 | _ -> return stanza |
1102 | forkIO $ do | 1387 | forkIO $ do |
1103 | case stanzaType stanza of | ||
1104 | Message {} -> do | ||
1105 | let fail = wlog $ "Failed delivery id="++show (stanzaId stanza) -- TODO | ||
1106 | xmppDeliverMessage xmpp fail stanza | ||
1107 | _ -> return () | ||
1108 | case stanzaOrigin stanza of | 1388 | case stanzaOrigin stanza of |
1109 | NetworkOrigin k@(ClientKey {}) replyto -> | 1389 | NetworkOrigin k@(ClientKey {}) replyto -> |
1110 | case stanzaType stanza of | 1390 | case stanzaType stanza of |
@@ -1112,7 +1392,34 @@ monitor sv params xmpp = do | |||
1112 | sockaddr <- socketFromKey sv k | 1392 | sockaddr <- socketFromKey sv k |
1113 | rsc <- xmppChooseResourceName xmpp k sockaddr wanted | 1393 | rsc <- xmppChooseResourceName xmpp k sockaddr wanted |
1114 | let reply = iq_bind_reply (stanzaId stanza) rsc | 1394 | let reply = iq_bind_reply (stanzaId stanza) rsc |
1395 | -- sendReply quitVar SetResource reply replyto | ||
1396 | hostname <- xmppTellMyNameToClient xmpp | ||
1397 | let requestVersion = do | ||
1398 | yield $ EventBeginElement "{jabber:client}iq" | ||
1399 | [ attr "to" rsc | ||
1400 | , attr "from" hostname | ||
1401 | , attr "type" "get" | ||
1402 | , attr "id" "version"] | ||
1403 | yield $ EventBeginElement "{jabber:iq:version}query" [] | ||
1404 | yield $ EventEndElement "{jabber:iq:version}query" | ||
1405 | yield $ EventEndElement "{jabber:client}iq" | ||
1406 | {- | ||
1407 | -- XXX Debug chat: | ||
1408 | yield $ EventBeginElement "{jabber:client}message" | ||
1409 | [ attr "from" $ eventContent (Just [ContentText rsc]) | ||
1410 | , attr "type" "normal" ] -- "blackbird" ] | ||
1411 | yield $ EventBeginElement "{jabber:client}body" [] | ||
1412 | yield $ EventContent $ ContentText ("hello?") | ||
1413 | yield $ EventEndElement "{jabber:client}body" | ||
1414 | yield $ EventEndElement "{jabber:client}message" | ||
1415 | -} | ||
1115 | sendReply quitVar SetResource reply replyto | 1416 | sendReply quitVar SetResource reply replyto |
1417 | conduitToStanza (UnrecognizedQuery "{jabber:iq:version}query") | ||
1418 | Nothing -- id | ||
1419 | (Just hostname) -- from | ||
1420 | (Just rsc) -- to | ||
1421 | requestVersion | ||
1422 | >>= ioWriteChan replyto | ||
1116 | SessionRequest -> do | 1423 | SessionRequest -> do |
1117 | me <- xmppTellMyNameToClient xmpp | 1424 | me <- xmppTellMyNameToClient xmpp |
1118 | let reply = iq_session_reply (stanzaId stanza) me | 1425 | let reply = iq_session_reply (stanzaId stanza) me |
@@ -1122,12 +1429,36 @@ monitor sv params xmpp = do | |||
1122 | xmppSubscribeToRoster xmpp k | 1429 | xmppSubscribeToRoster xmpp k |
1123 | PresenceStatus {} -> do | 1430 | PresenceStatus {} -> do |
1124 | xmppInformClientPresence xmpp k stanza | 1431 | xmppInformClientPresence xmpp k stanza |
1432 | NotifyClientVersion name version -> do | ||
1433 | enableClientHacks name version replyto | ||
1125 | UnrecognizedQuery query -> do | 1434 | UnrecognizedQuery query -> do |
1126 | me <- xmppTellMyNameToClient xmpp | 1435 | me <- xmppTellMyNameToClient xmpp |
1127 | let reply = iq_service_unavailable (stanzaId stanza) me query | 1436 | let reply = iq_service_unavailable (stanzaId stanza) me query |
1128 | sendReply quitVar Error reply replyto | 1437 | sendReply quitVar (Error ServiceUnavailable (head reply)) reply replyto |
1438 | Message {} -> do | ||
1439 | -- wlog $ "LANGMAP "++show (stanzaId stanza, msgLangMap (stanzaType stanza)) | ||
1440 | maybe (return ()) (flip cacheMessageId replyto) $ do | ||
1441 | guard . not . null . mapMaybe (msgBody . snd) $ msgLangMap (stanzaType stanza) | ||
1442 | stanzaId stanza | ||
1129 | _ -> return () | 1443 | _ -> return () |
1130 | _ -> return () | 1444 | _ -> return () |
1445 | case stanzaType stanza of | ||
1446 | Message {} -> do | ||
1447 | case stanzaOrigin stanza of | ||
1448 | LocalPeer {} -> return () | ||
1449 | NetworkOrigin _ replyto -> do | ||
1450 | let fail = wlog $ "Failed delivery id="++show (stanzaId stanza) -- TODO | ||
1451 | xmppDeliverMessage xmpp fail stanza | ||
1452 | {- | ||
1453 | -- test error | ||
1454 | reply <- makeErrorStanza stanza | ||
1455 | tag <- stanzaFirstTag stanza | ||
1456 | sendReply quitVar (Error RecipientUnavailable tag) reply replyto | ||
1457 | -} | ||
1458 | -- -- bad idea: | ||
1459 | -- let newStream = greet'' "jabber:client" "blackbird" | ||
1460 | -- sendReply quitVar Error newStream replyto | ||
1461 | _ -> return () | ||
1131 | -- We need to clone in the case the stanza is passed on as for Message. | 1462 | -- We need to clone in the case the stanza is passed on as for Message. |
1132 | #ifndef PINGNOISE | 1463 | #ifndef PINGNOISE |
1133 | let notping f = case stanzaType stanza of Pong -> return () | 1464 | let notping f = case stanzaType stanza of Pong -> return () |