diff options
Diffstat (limited to 'Presence')
-rw-r--r-- | Presence/EventUtil.hs | 6 | ||||
-rw-r--r-- | Presence/XMPPServer.hs | 99 |
2 files changed, 83 insertions, 22 deletions
diff --git a/Presence/EventUtil.hs b/Presence/EventUtil.hs index a1c48e33..adcae1d3 100644 --- a/Presence/EventUtil.hs +++ b/Presence/EventUtil.hs | |||
@@ -29,6 +29,12 @@ isServerIQOf (EventBeginElement name attrs) testType | |||
29 | = True | 29 | = True |
30 | isServerIQOf _ _ = False | 30 | isServerIQOf _ _ = False |
31 | 31 | ||
32 | isClientIQOf (EventBeginElement name attrs) testType | ||
33 | | name=="{jabber:client}iq" | ||
34 | && matchAttrib "type" testType attrs | ||
35 | = True | ||
36 | isClientIQOf _ _ = False | ||
37 | |||
32 | matchAttrib name value attrs = | 38 | matchAttrib name value attrs = |
33 | case List.find ( (==name) . fst) attrs of | 39 | case List.find ( (==name) . fst) attrs of |
34 | Just (_,[ContentText x]) | x==value -> True | 40 | Just (_,[ContentText x]) | x==value -> True |
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index 140c91af..92f59061 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -104,6 +104,8 @@ data Stanza | |||
104 | data StanzaType = Unrecognized | Ping | Pong | 104 | data StanzaType = Unrecognized | Ping | Pong |
105 | deriving Show | 105 | deriving Show |
106 | 106 | ||
107 | data StanzaOrigin = LocalPeer | NetworkOrigin ConnectionKey | ||
108 | |||
107 | data Stanza = Stanza | 109 | data Stanza = Stanza |
108 | { stanzaType :: StanzaType | 110 | { stanzaType :: StanzaType |
109 | , stanzaId :: Maybe Text | 111 | , stanzaId :: Maybe Text |
@@ -112,6 +114,7 @@ data Stanza = Stanza | |||
112 | , stanzaChan :: TChan XML.Event | 114 | , stanzaChan :: TChan XML.Event |
113 | , stanzaClosers :: TVar (Maybe [XML.Event]) | 115 | , stanzaClosers :: TVar (Maybe [XML.Event]) |
114 | , stanzaInterrupt :: TMVar () | 116 | , stanzaInterrupt :: TMVar () |
117 | , stanzaOrigin :: StanzaOrigin | ||
115 | } | 118 | } |
116 | 119 | ||
117 | copyToChannel f chan closer_stack = awaitForever copy | 120 | copyToChannel f chan closer_stack = awaitForever copy |
@@ -145,6 +148,17 @@ grockStanzaIQGet stanza = do | |||
145 | 148 | ||
146 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | 149 | ioWriteChan c v = liftIO . atomically $ writeTChan c v |
147 | 150 | ||
151 | |||
152 | grokStanza "jabber:server" stanzaTag = | ||
153 | case () of | ||
154 | _ | stanzaTag `isServerIQOf` "get" -> grockStanzaIQGet stanzaTag | ||
155 | _ -> return $ Just Unrecognized | ||
156 | |||
157 | grokStanza "jabber:client" stanzaTag = | ||
158 | case () of | ||
159 | _ | stanzaTag `isClientIQOf` "get" -> grockStanzaIQGet stanzaTag | ||
160 | _ -> return $ Just Unrecognized | ||
161 | |||
148 | xmppInbound :: ConnectionKey -> FlagCommand | 162 | xmppInbound :: ConnectionKey -> FlagCommand |
149 | -> Source IO XML.Event | 163 | -> Source IO XML.Event |
150 | -> TChan Stanza | 164 | -> TChan Stanza |
@@ -152,6 +166,9 @@ xmppInbound :: ConnectionKey -> FlagCommand | |||
152 | -> TMVar () | 166 | -> TMVar () |
153 | -> Sink XML.Event IO () | 167 | -> Sink XML.Event IO () |
154 | xmppInbound k pingflag src stanzas output donevar = doNestingXML $ do | 168 | xmppInbound k pingflag src stanzas output donevar = doNestingXML $ do |
169 | let namespace = case k of | ||
170 | ClientKey {} -> "jabber:client" | ||
171 | PeerKey {} -> "jabber:server" | ||
155 | withXML $ \begindoc -> do | 172 | withXML $ \begindoc -> do |
156 | when (begindoc==EventBeginDocument) $ do | 173 | when (begindoc==EventBeginDocument) $ do |
157 | whenJust nextElement $ \xml -> do | 174 | whenJust nextElement $ \xml -> do |
@@ -169,16 +186,17 @@ xmppInbound k pingflag src stanzas output donevar = doNestingXML $ do | |||
169 | let mid = lookupAttrib "id" (tagAttrs stanzaTag) | 186 | let mid = lookupAttrib "id" (tagAttrs stanzaTag) |
170 | mfrom = lookupAttrib "from" (tagAttrs stanzaTag) | 187 | mfrom = lookupAttrib "from" (tagAttrs stanzaTag) |
171 | mto = lookupAttrib "to" (tagAttrs stanzaTag) | 188 | mto = lookupAttrib "to" (tagAttrs stanzaTag) |
172 | dispatch <- | 189 | dispatch <- grokStanza namespace stanzaTag |
173 | case () of | ||
174 | _ | stanzaTag `isServerIQOf` "get" -> grockStanzaIQGet stanzaTag | ||
175 | _ -> return $ Just Unrecognized | ||
176 | flip (maybe $ return ()) dispatch $ \dispatch -> | 190 | flip (maybe $ return ()) dispatch $ \dispatch -> |
177 | case dispatch of | 191 | case dispatch of |
178 | Ping -> do | 192 | Ping -> do |
193 | -- TODO: check that the to-address matches this server. | ||
194 | -- Otherwise it could be a client-to-client ping or a | ||
195 | -- client-to-server for some other server. | ||
196 | -- For now, assuming its for the immediate connection. | ||
179 | let to = maybe "todo" id mto | 197 | let to = maybe "todo" id mto |
180 | from = maybe "todo" id mfrom | 198 | from = maybe "todo" id mfrom |
181 | let pong = peerPong mid to from | 199 | let pong = makePong namespace mid to from |
182 | -- liftIO $ wlog "got ping, sending pong..." | 200 | -- liftIO $ wlog "got ping, sending pong..." |
183 | pongChan <- liftIO $ atomically newTChan | 201 | pongChan <- liftIO $ atomically newTChan |
184 | pongClsrs <- liftIO $ atomically $ newTVar (Just []) | 202 | pongClsrs <- liftIO $ atomically $ newTVar (Just []) |
@@ -189,6 +207,7 @@ xmppInbound k pingflag src stanzas output donevar = doNestingXML $ do | |||
189 | , stanzaChan = pongChan | 207 | , stanzaChan = pongChan |
190 | , stanzaClosers = pongClsrs | 208 | , stanzaClosers = pongClsrs |
191 | , stanzaInterrupt = donevar | 209 | , stanzaInterrupt = donevar |
210 | , stanzaOrigin = LocalPeer | ||
192 | } | 211 | } |
193 | void . liftIO . forkIO $ do | 212 | void . liftIO . forkIO $ do |
194 | mapM_ (ioWriteChan pongChan) pong | 213 | mapM_ (ioWriteChan pongChan) pong |
@@ -202,6 +221,7 @@ xmppInbound k pingflag src stanzas output donevar = doNestingXML $ do | |||
202 | , stanzaChan = chan | 221 | , stanzaChan = chan |
203 | , stanzaClosers = clsrs | 222 | , stanzaClosers = clsrs |
204 | , stanzaInterrupt = donevar | 223 | , stanzaInterrupt = donevar |
224 | , stanzaOrigin = NetworkOrigin k | ||
205 | } | 225 | } |
206 | awaitCloser stanza_lvl | 226 | awaitCloser stanza_lvl |
207 | liftIO . atomically $ writeTVar clsrs Nothing | 227 | liftIO . atomically $ writeTVar clsrs Nothing |
@@ -230,16 +250,39 @@ readUntilNothing ch = do | |||
230 | return (x:xs)) | 250 | return (x:xs)) |
231 | x | 251 | x |
232 | 252 | ||
253 | greet' namespace host = | ||
254 | [ EventBeginDocument | ||
255 | , EventBeginElement (streamP "stream") | ||
256 | [("from",[ContentText host]) | ||
257 | ,("id",[ContentText "someid"]) | ||
258 | ,("xmlns",[ContentText namespace]) | ||
259 | ,("xmlns:stream",[ContentText "http://etherx.jabber.org/streams"]) | ||
260 | ,("version",[ContentText "1.0"]) | ||
261 | ] | ||
262 | , EventBeginElement (streamP "features") [] | ||
263 | , EventBeginElement "{urn:ietf:params:xml:ns:xmpp-bind}bind" [] | ||
264 | , EventEndElement "{urn:ietf:params:xml:ns:xmpp-bind}bind" | ||
265 | |||
266 | {- | ||
267 | -- , " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" | ||
268 | , " <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" | ||
269 | -- , " <mechanism>DIGEST-MD5</mechanism>" | ||
270 | , " <mechanism>PLAIN</mechanism>" | ||
271 | , " </mechanisms> " | ||
272 | -} | ||
273 | |||
274 | , EventEndElement (streamP "features") | ||
275 | ] | ||
233 | 276 | ||
234 | greetPeer = | 277 | greet namespace = |
235 | [ EventBeginDocument | 278 | [ EventBeginDocument |
236 | , EventBeginElement (streamP "stream") | 279 | , EventBeginElement (streamP "stream") |
237 | [ attr "xmlns" "jabber:server" | 280 | [ attr "xmlns" namespace |
238 | , attr "version" "1.0" | 281 | , attr "version" "1.0" |
239 | ] | 282 | ] |
240 | ] | 283 | ] |
241 | 284 | ||
242 | goodbyePeer = | 285 | goodbye = |
243 | [ EventEndElement (streamP "stream") | 286 | [ EventEndElement (streamP "stream") |
244 | , EventEndDocument | 287 | , EventEndDocument |
245 | ] | 288 | ] |
@@ -248,10 +291,11 @@ data XMPPState | |||
248 | = PingSlot | 291 | = PingSlot |
249 | deriving (Eq,Ord) | 292 | deriving (Eq,Ord) |
250 | 293 | ||
294 | mkname namespace name = (Name name (Just namespace) Nothing) | ||
251 | 295 | ||
252 | peerPing :: Maybe Text -> Text -> Text -> [XML.Event] | 296 | makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event] |
253 | peerPing mid to from = | 297 | makePing namespace mid to from = |
254 | [ EventBeginElement "{jabber:server}iq" | 298 | [ EventBeginElement (mkname namespace "iq") |
255 | $ (case mid of | 299 | $ (case mid of |
256 | Just c -> (("id",[ContentText c]):) | 300 | Just c -> (("id",[ContentText c]):) |
257 | _ -> id ) | 301 | _ -> id ) |
@@ -261,10 +305,10 @@ peerPing mid to from = | |||
261 | ] | 305 | ] |
262 | , EventBeginElement "{urn:xmpp:ping}ping" [] | 306 | , EventBeginElement "{urn:xmpp:ping}ping" [] |
263 | , EventEndElement "{urn:xmpp:ping}ping" | 307 | , EventEndElement "{urn:xmpp:ping}ping" |
264 | , EventEndElement "{jabber:server}iq" ] | 308 | , EventEndElement $ mkname namespace "iq"] |
265 | 309 | ||
266 | peerPong mid to from = | 310 | makePong namespace mid to from = |
267 | [ EventBeginElement "{jabber:server}iq" | 311 | [ EventBeginElement (mkname namespace "iq") |
268 | $(case mid of | 312 | $(case mid of |
269 | Just c -> (("id",[ContentText c]):) | 313 | Just c -> (("id",[ContentText c]):) |
270 | _ -> id) | 314 | _ -> id) |
@@ -272,7 +316,7 @@ peerPong mid to from = | |||
272 | , attr "to" to | 316 | , attr "to" to |
273 | , attr "from" from | 317 | , attr "from" from |
274 | ] | 318 | ] |
275 | , EventEndElement "{jabber:server}iq" | 319 | , EventEndElement (mkname namespace "iq") |
276 | ] | 320 | ] |
277 | 321 | ||
278 | 322 | ||
@@ -283,12 +327,15 @@ forkConnection :: ConnectionKey | |||
283 | -> TChan Stanza | 327 | -> TChan Stanza |
284 | -> IO (TChan Stanza) | 328 | -> IO (TChan Stanza) |
285 | forkConnection k pingflag src snk stanzas = do | 329 | forkConnection k pingflag src snk stanzas = do |
330 | let namespace = case k of | ||
331 | ClientKey {} -> "jabber:client" | ||
332 | PeerKey {} -> "jabber:server" | ||
286 | rdone <- atomically newEmptyTMVar | 333 | rdone <- atomically newEmptyTMVar |
287 | slots <- atomically $ Slotted.new isEventBeginElement isEventEndElement | 334 | slots <- atomically $ Slotted.new isEventBeginElement isEventEndElement |
288 | needsFlush <- atomically $ newTVar False | 335 | needsFlush <- atomically $ newTVar False |
289 | let _ = slots :: Slotted.UpdateStream XMPPState XML.Event | 336 | let _ = slots :: Slotted.UpdateStream XMPPState XML.Event |
290 | let greet_src = do | 337 | let greet_src = do |
291 | CL.sourceList greetPeer =$= CL.map Chunk | 338 | CL.sourceList (greet' namespace "localhost") =$= CL.map Chunk |
292 | yield Flush | 339 | yield Flush |
293 | slot_src = do | 340 | slot_src = do |
294 | what <- lift . atomically $ foldr1 orElse | 341 | what <- lift . atomically $ foldr1 orElse |
@@ -324,11 +371,13 @@ forkConnection k pingflag src snk stanzas = do | |||
324 | from = "todo" -- Look it up from Server object | 371 | from = "todo" -- Look it up from Server object |
325 | -- or pass it with Connection event. | 372 | -- or pass it with Connection event. |
326 | mid = Just "ping" | 373 | mid = Just "ping" |
327 | ping = peerPing mid to from | 374 | ping = makePing namespace mid to from |
328 | mapM_ (atomically . Slotted.push slots (Just $ PingSlot)) | 375 | mapM_ (atomically . Slotted.push slots (Just $ PingSlot)) |
329 | ping | 376 | ping |
330 | wlog "" | 377 | wlog "" |
331 | CL.sourceList ping $$ prettyPrint "P<-Ping " | 378 | CL.sourceList ping $$ prettyPrint $ case k of |
379 | ClientKey {} -> "C<-Ping" | ||
380 | PeerKey {} -> "P<-Ping " | ||
332 | loop | 381 | loop |
333 | ,readTMVar rdone >> return (return ()) | 382 | ,readTMVar rdone >> return (return ()) |
334 | ] | 383 | ] |
@@ -409,7 +458,11 @@ monitor sv params = do | |||
409 | RequiresPing -> return () -- wlog $ tomsg k "RequiresPing" | 458 | RequiresPing -> return () -- wlog $ tomsg k "RequiresPing" |
410 | _ -> return () | 459 | _ -> return () |
411 | , readTChan stanzas >>= \stanza -> return $ do | 460 | , readTChan stanzas >>= \stanza -> return $ do |
412 | let typ = Strict8.pack $ "P->"++(show (stanzaType stanza))++" " | 461 | let typ = Strict8.pack $ c ++ "->"++(show (stanzaType stanza))++" " |
462 | c = case stanzaOrigin stanza of | ||
463 | LocalPeer -> "*" | ||
464 | NetworkOrigin (ClientKey {}) -> "C" | ||
465 | NetworkOrigin (PeerKey {}) -> "P" | ||
413 | wlog "" | 466 | wlog "" |
414 | stanzaToConduit stanza $$ prettyPrint typ | 467 | stanzaToConduit stanza $$ prettyPrint typ |
415 | ] | 468 | ] |
@@ -428,10 +481,12 @@ xmppServer = do | |||
428 | { pingInterval = 5000 | 481 | { pingInterval = 5000 |
429 | , timeout = 1000 | 482 | , timeout = 1000 |
430 | , duplex = False } | 483 | , duplex = False } |
431 | client_params = connectionDefaults clientKey | 484 | client_params = (connectionDefaults clientKey) |
485 | { pingInterval = 0 | ||
486 | , timeout = 0 | ||
487 | } | ||
432 | liftIO $ do | 488 | liftIO $ do |
433 | forkIO $ monitor sv peer_params | 489 | forkIO $ monitor sv peer_params |
434 | control sv (Listen peerport peer_params) | 490 | control sv (Listen peerport peer_params) |
435 | -- todo | 491 | control sv (Listen clientport client_params) |
436 | -- control sv (Listen clientport client_params) | ||
437 | return (sv,peer_params) | 492 | return (sv,peer_params) |