diff options
author | James Crayne <jim.crayne@gmail.com> | 2013-06-30 18:27:12 -0400 |
---|---|---|
committer | James Crayne <jim.crayne@gmail.com> | 2013-06-30 18:27:12 -0400 |
commit | b70209c295681a89b64f7527a2ecae23d9bb9bc2 (patch) | |
tree | d86775d956e4d69f9308a20695a6683b2ce2a9dc /Presence | |
parent | ffa072d469c904bf30756e2acbdb1c9b78508c35 (diff) | |
parent | 332002c101682f9c796a973cf62a82bef2c4659e (diff) |
Merge branch 'master' of samwise:presence
Diffstat (limited to 'Presence')
-rw-r--r-- | Presence/ServerC.hs | 9 | ||||
-rw-r--r-- | Presence/XMPP.hs | 319 | ||||
-rw-r--r-- | Presence/XMPPServer.hs | 63 | ||||
-rw-r--r-- | Presence/XMPPTypes.hs | 86 |
4 files changed, 395 insertions, 82 deletions
diff --git a/Presence/ServerC.hs b/Presence/ServerC.hs index b16a0099..22104a31 100644 --- a/Presence/ServerC.hs +++ b/Presence/ServerC.hs | |||
@@ -8,6 +8,7 @@ module ServerC | |||
8 | , ServerHandle | 8 | , ServerHandle |
9 | , quitListening | 9 | , quitListening |
10 | , dummyServerHandle | 10 | , dummyServerHandle |
11 | , packetSink | ||
11 | ) where | 12 | ) where |
12 | 13 | ||
13 | import Network.Socket as Socket | 14 | import Network.Socket as Socket |
@@ -123,13 +124,13 @@ packets h = do | |||
123 | where | 124 | where |
124 | getPacket h = do { hWaitForInput h (-1) ; hGetNonBlocking h 1024 } | 125 | getPacket h = do { hWaitForInput h (-1) ; hGetNonBlocking h 1024 } |
125 | 126 | ||
126 | outgoing :: MonadIO m => Handle -> Sink S.ByteString m () | 127 | packetSink :: MonadIO m => Handle -> Sink S.ByteString m () |
127 | outgoing h = do | 128 | packetSink h = do |
128 | -- liftIO . L.putStrLn $ "outgoing: waiting" | 129 | -- liftIO . L.putStrLn $ "outgoing: waiting" |
129 | mpacket <- await | 130 | mpacket <- await |
130 | -- liftIO . L.putStrLn $ "outgoing: got packet " <++> bshow mpacket | 131 | -- liftIO . L.putStrLn $ "outgoing: got packet " <++> bshow mpacket |
131 | maybe (return ()) | 132 | maybe (return ()) |
132 | (\r -> (liftIO . S.hPutStrLn h $ r) >> outgoing h) | 133 | (\r -> (liftIO . S.hPutStrLn h $ r) >> packetSink h) |
133 | mpacket | 134 | mpacket |
134 | 135 | ||
135 | 136 | ||
@@ -148,5 +149,5 @@ runConn g st (sock,_) = do | |||
148 | h <- socketToHandle sock ReadWriteMode | 149 | h <- socketToHandle sock ReadWriteMode |
149 | hSetBuffering h NoBuffering | 150 | hSetBuffering h NoBuffering |
150 | let doException (SomeException e) = Prelude.putStrLn ("\n\nexception: " ++ show e ++ "\n\n") | 151 | let doException (SomeException e) = Prelude.putStrLn ("\n\nexception: " ++ show e ++ "\n\n") |
151 | handle doException (g (restrictSocket sock `HCons` st) (packets h) (outgoing h)) | 152 | handle doException (g (restrictSocket sock `HCons` st) (packets h) (packetSink h)) |
152 | hClose h | 153 | hClose h |
diff --git a/Presence/XMPP.hs b/Presence/XMPP.hs index 1a4b0e7b..36630bc7 100644 --- a/Presence/XMPP.hs +++ b/Presence/XMPP.hs | |||
@@ -16,8 +16,29 @@ import SocketLike | |||
16 | import ByteStringOperators | 16 | import ByteStringOperators |
17 | 17 | ||
18 | import Data.HList | 18 | import Data.HList |
19 | import Network.Socket (Family) | 19 | import Network.Socket |
20 | import Network.BSD (PortNumber) | 20 | ( Family |
21 | , connect | ||
22 | , socketToHandle | ||
23 | , sClose | ||
24 | , Socket(..) | ||
25 | , socket | ||
26 | , SocketType(..) | ||
27 | ) | ||
28 | import Network.BSD | ||
29 | ( PortNumber | ||
30 | , getHostName | ||
31 | , hostName | ||
32 | , hostAliases | ||
33 | , getProtocolNumber | ||
34 | ) | ||
35 | import System.IO | ||
36 | ( BufferMode(..) | ||
37 | , IOMode(..) | ||
38 | , hSetBuffering | ||
39 | ) | ||
40 | import Control.Exception | ||
41 | ( bracketOnError ) | ||
21 | import Control.Concurrent.STM | 42 | import Control.Concurrent.STM |
22 | import Data.Conduit | 43 | import Data.Conduit |
23 | import qualified Data.Conduit.List as CL | 44 | import qualified Data.Conduit.List as CL |
@@ -27,6 +48,8 @@ import qualified Data.ByteString.Char8 as S (pack,putStr,putStrLn,append) | |||
27 | import qualified Data.ByteString.Lazy.Char8 as L | 48 | import qualified Data.ByteString.Lazy.Char8 as L |
28 | ( putStrLn | 49 | ( putStrLn |
29 | , fromChunks | 50 | , fromChunks |
51 | , unlines | ||
52 | , hPutStrLn | ||
30 | ) | 53 | ) |
31 | import Control.Concurrent (forkIO,killThread) | 54 | import Control.Concurrent (forkIO,killThread) |
32 | import Control.Concurrent.Async | 55 | import Control.Concurrent.Async |
@@ -39,7 +62,6 @@ import Control.Monad as Monad | |||
39 | import Text.XML.Stream.Parse (parseBytes,content) | 62 | import Text.XML.Stream.Parse (parseBytes,content) |
40 | import Text.XML.Stream.Render | 63 | import Text.XML.Stream.Render |
41 | import Data.XML.Types as XML | 64 | import Data.XML.Types as XML |
42 | import Network.BSD (getHostName,hostName,hostAliases) | ||
43 | import Data.Text.Encoding as S (decodeUtf8,encodeUtf8) | 65 | import Data.Text.Encoding as S (decodeUtf8,encodeUtf8) |
44 | import Data.Text.Lazy.Encoding as L (decodeUtf8,encodeUtf8) | 66 | import Data.Text.Lazy.Encoding as L (decodeUtf8,encodeUtf8) |
45 | import Data.Text.Lazy (toStrict) | 67 | import Data.Text.Lazy (toStrict) |
@@ -53,7 +75,13 @@ import Data.Conduit.Blaze | |||
53 | import Data.List (find) | 75 | import Data.List (find) |
54 | import qualified Text.Show.ByteString as L | 76 | import qualified Text.Show.ByteString as L |
55 | import NestingXML | 77 | import NestingXML |
78 | import Data.Set as Set (Set) | ||
56 | import qualified Data.Set as Set | 79 | import qualified Data.Set as Set |
80 | import qualified Data.Map as Map | ||
81 | import GHC.Conc | ||
82 | ( threadStatus | ||
83 | , ThreadStatus(..) | ||
84 | ) | ||
57 | 85 | ||
58 | data Commands = Send [XML.Event] | QuitThread | 86 | data Commands = Send [XML.Event] | QuitThread |
59 | deriving Prelude.Show | 87 | deriving Prelude.Show |
@@ -76,11 +104,11 @@ xmlifyPresenceForClient (Presence jid stat) = do | |||
76 | return (concatMap presenceEvents jidstrs) | 104 | return (concatMap presenceEvents jidstrs) |
77 | where | 105 | where |
78 | presenceEvents jidstr = | 106 | presenceEvents jidstr = |
79 | [ EventBeginElement "presence" (("from",[ContentText jidstr]):typ stat) | 107 | [ EventBeginElement "{jabber:client}presence" (("from",[ContentText jidstr]):typ stat) |
80 | , EventBeginElement "show" [] | 108 | , EventBeginElement "{jabber:client}show" [] |
81 | , EventContent (ContentText . shw $ stat) | 109 | , EventContent (ContentText . shw $ stat) |
82 | , EventEndElement "show" | 110 | , EventEndElement "{jabber:client}show" |
83 | , EventEndElement "presence" | 111 | , EventEndElement "{jabber:client}presence" |
84 | ] | 112 | ] |
85 | typ Offline = [("type",[ContentText "unavailable"])] | 113 | typ Offline = [("type",[ContentText "unavailable"])] |
86 | typ _ = [] | 114 | typ _ = [] |
@@ -102,8 +130,8 @@ greet host = | |||
102 | ,("version",[ContentText "1.0"]) | 130 | ,("version",[ContentText "1.0"]) |
103 | ] | 131 | ] |
104 | , EventBeginElement (streamP "features") [] | 132 | , EventBeginElement (streamP "features") [] |
105 | , EventBeginElement "bind" [("xmlns",[ContentText "urn:ietf:params:xml:ns:xmpp-bind"])] | 133 | , EventBeginElement "{urn:ietf:params:xml:ns:xmpp-bind}bind" [] |
106 | , EventEndElement "bind" | 134 | , EventEndElement "{urn:ietf:params:xml:ns:xmpp-bind}bind" |
107 | 135 | ||
108 | {- | 136 | {- |
109 | -- , " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" | 137 | -- , " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" |
@@ -121,6 +149,7 @@ greet host = | |||
121 | mawait :: Monad m => MaybeT (ConduitM i o m) i | 149 | mawait :: Monad m => MaybeT (ConduitM i o m) i |
122 | mawait = MaybeT await | 150 | mawait = MaybeT await |
123 | 151 | ||
152 | -- Note: This function ignores name space qualification | ||
124 | elementAttrs expected (EventBeginElement name attrs) | 153 | elementAttrs expected (EventBeginElement name attrs) |
125 | | nameLocalName name==expected | 154 | | nameLocalName name==expected |
126 | = return attrs | 155 | = return attrs |
@@ -409,15 +438,16 @@ prettyPrint prefix xs = | |||
409 | 438 | ||
410 | toClient :: MonadIO m => TChan Presence -> TChan Commands -> Source m [XML.Event] | 439 | toClient :: MonadIO m => TChan Presence -> TChan Commands -> Source m [XML.Event] |
411 | toClient pchan cmdChan = fix $ \loop -> do | 440 | toClient pchan cmdChan = fix $ \loop -> do |
441 | let send xs = yield xs >> prettyPrint ">C: " xs | ||
412 | event <- liftIO . atomically $ | 442 | event <- liftIO . atomically $ |
413 | orElse (fmap Left $ readTChan pchan) | 443 | orElse (fmap Left $ readTChan pchan) |
414 | (fmap Right $ readTChan cmdChan) | 444 | (fmap Right $ readTChan cmdChan) |
415 | case event of | 445 | case event of |
416 | Right QuitThread -> return () | 446 | Right QuitThread -> return () |
417 | Right (Send xs) -> yield xs >> prettyPrint ">C: " xs >> loop | 447 | Right (Send xs) -> send xs >> loop |
418 | Left presence -> do | 448 | Left presence -> do |
419 | xs <- liftIO $ xmlifyPresenceForClient presence | 449 | xs <- liftIO $ xmlifyPresenceForClient presence |
420 | yield xs | 450 | send xs |
421 | loop | 451 | loop |
422 | 452 | ||
423 | handleClient | 453 | handleClient |
@@ -501,7 +531,7 @@ handlePeer st src snk = do | |||
501 | jids <- newTVarIO Set.empty | 531 | jids <- newTVarIO Set.empty |
502 | session <- newSession session_factory sock | 532 | session <- newSession session_factory sock |
503 | 533 | ||
504 | finally ( src $= parseBytes def $$ fromPeer session ) | 534 | finally ( src $= parseBytes def $$ fromPeer (session,jids) ) |
505 | $ do | 535 | $ do |
506 | L.putStrLn $ "(P) disconnected " <++> name | 536 | L.putStrLn $ "(P) disconnected " <++> name |
507 | js <- fmap Set.toList (readTVarIO jids) | 537 | js <- fmap Set.toList (readTVarIO jids) |
@@ -509,8 +539,64 @@ handlePeer st src snk = do | |||
509 | forM_ js $ announcePresence session . offline | 539 | forM_ js $ announcePresence session . offline |
510 | closeSession session | 540 | closeSession session |
511 | 541 | ||
542 | |||
543 | handlePeerPresence (session,jids) stanza False = do | ||
544 | -- Offline | ||
545 | withJust (lookupAttrib "from" (tagAttrs stanza)) $ \jid -> do | ||
546 | peer_jid <- liftIO $ parseAddressJID (L.fromChunks [S.encodeUtf8 jid]) | ||
547 | liftIO . atomically $ do | ||
548 | jids_ <- readTVar jids | ||
549 | writeTVar jids (Set.delete peer_jid jids_) | ||
550 | liftIO $ announcePresence session (Presence peer_jid Offline) | ||
551 | handlePeerPresence (session,jids) stanza True = do | ||
552 | -- online (Available or Away) | ||
553 | let log = liftIO . L.putStrLn . ("(P) " <++>) | ||
554 | withJust (lookupAttrib "from" (tagAttrs stanza)) $ \jid -> do | ||
555 | pjid <- liftIO $ parseAddressJID (L.fromChunks [S.encodeUtf8 jid]) | ||
556 | -- stat <- show element content | ||
557 | let parseChildren stat = do | ||
558 | child <- nextElement | ||
559 | case child of | ||
560 | Just tag | tagName tag=="{jabber:server}show" | ||
561 | -> fmap toStat (lift content) | ||
562 | Just tag | otherwise -> parseChildren stat | ||
563 | Nothing -> return stat | ||
564 | toStat "away" = Away | ||
565 | toStat "xa" = Away -- TODO: xa | ||
566 | toStat "dnd" = Away -- TODO: dnd | ||
567 | toStat "chat" = Available | ||
568 | |||
569 | stat' <- parseChildren Available | ||
570 | |||
571 | liftIO . atomically $ do | ||
572 | jids_ <- readTVar jids | ||
573 | writeTVar jids (Set.insert pjid jids_) | ||
574 | liftIO $ announcePresence session (Presence pjid stat') | ||
575 | log $ bshow (Presence pjid stat') | ||
576 | |||
577 | matchAttribMaybe name (Just value) attrs = | ||
578 | case find ( (==name) . fst) attrs of | ||
579 | Just (_,[ContentText x]) | x==value -> True | ||
580 | Just (_,[ContentEntity x]) | x==value -> True | ||
581 | _ -> False | ||
582 | matchAttribMaybe name Nothing attrs | ||
583 | | find ( (==name) . fst) attrs==Nothing | ||
584 | = True | ||
585 | matchAttribMaybe name Nothing attrs | ||
586 | | otherwise | ||
587 | = False | ||
588 | |||
589 | presenceTypeOffline = Just "unavailable" | ||
590 | presenceTypeOnline = Nothing | ||
591 | |||
592 | isPresenceOf (EventBeginElement name attrs) testType | ||
593 | | name=="{jabber:server}presence" | ||
594 | && matchAttribMaybe "type" testType attrs | ||
595 | = True | ||
596 | isPresenceOf _ _ = False | ||
597 | |||
512 | fromPeer :: (MonadThrow m,MonadIO m, XMPPSession session) => | 598 | fromPeer :: (MonadThrow m,MonadIO m, XMPPSession session) => |
513 | session -> Sink XML.Event m () | 599 | (session, TVar (Set JID)) -> Sink XML.Event m () |
514 | fromPeer session = doNestingXML $ do | 600 | fromPeer session = doNestingXML $ do |
515 | let log = liftIO . L.putStrLn . ("(P) " <++>) | 601 | let log = liftIO . L.putStrLn . ("(P) " <++>) |
516 | withXML $ \begindoc -> do | 602 | withXML $ \begindoc -> do |
@@ -525,6 +611,16 @@ fromPeer session = doNestingXML $ do | |||
525 | whenJust nextElement $ \stanza -> do | 611 | whenJust nextElement $ \stanza -> do |
526 | stanza_lvl <- nesting | 612 | stanza_lvl <- nesting |
527 | 613 | ||
614 | let unhandledStanza = do | ||
615 | mb <- lift . runMaybeT $ gatherElement stanza Seq.empty | ||
616 | withJust mb $ \xs -> prettyPrint "P: " (toList xs) | ||
617 | case () of | ||
618 | _ | stanza `isPresenceOf` presenceTypeOnline | ||
619 | -> log "peer online!" >> handlePeerPresence session stanza True | ||
620 | _ | stanza `isPresenceOf` presenceTypeOffline | ||
621 | -> handlePeerPresence session stanza False | ||
622 | _ -> unhandledStanza | ||
623 | |||
528 | awaitCloser stanza_lvl | 624 | awaitCloser stanza_lvl |
529 | loop | 625 | loop |
530 | 626 | ||
@@ -534,9 +630,206 @@ fromPeer session = doNestingXML $ do | |||
534 | 630 | ||
535 | 631 | ||
536 | 632 | ||
633 | {- | ||
537 | seekRemotePeers :: XMPPConfig config => | 634 | seekRemotePeers :: XMPPConfig config => |
538 | config -> TChan Presence -> IO () | 635 | config -> TChan Presence -> IO () |
539 | seekRemotePeers config chan = do | 636 | seekRemotePeers config chan = do |
540 | putStrLn "unimplemented: seekRemotePeers" | 637 | putStrLn "unimplemented: seekRemotePeers" |
541 | -- TODO | 638 | -- TODO |
542 | return () | 639 | return () |
640 | -} | ||
641 | |||
642 | data OutBoundMessage = OutBoundPresence Presence | ||
643 | deriving Prelude.Show | ||
644 | |||
645 | newServerConnections = atomically $ newTVar Map.empty | ||
646 | |||
647 | connect_to_server chan peer = (>> return ()) . runMaybeT $ do | ||
648 | let port = 5269 :: Int | ||
649 | |||
650 | connected <- liftIO . async $ connect' (peerAddr peer) port | ||
651 | |||
652 | -- We'll cache Presence notifications until the socket | ||
653 | -- is ready. | ||
654 | cached <- liftIO $ newIORef Map.empty | ||
655 | |||
656 | sock <- MaybeT . fix $ \loop -> do | ||
657 | e <- atomically $ orElse | ||
658 | (fmap Right $ waitSTM connected) | ||
659 | (fmap Left $ readTChan chan) | ||
660 | case e of | ||
661 | Left (OutBoundPresence (Presence jid Offline)) -> do | ||
662 | cached_map <- readIORef cached | ||
663 | writeIORef cached (Map.delete jid cached_map) | ||
664 | loop | ||
665 | Left (OutBoundPresence p@(Presence jid st)) -> do | ||
666 | cached_map <- readIORef cached | ||
667 | writeIORef cached (Map.insert jid st cached_map) | ||
668 | loop | ||
669 | {- | ||
670 | Left event -> do | ||
671 | L.putStrLn $ "REMOTE-OUT DISCARDED: " <++> bshow event | ||
672 | loop | ||
673 | -} | ||
674 | Right sock -> return sock | ||
675 | |||
676 | liftIO $ do | ||
677 | h <- socketToHandle sock ReadWriteMode | ||
678 | hSetBuffering h NoBuffering | ||
679 | let snk = packetSink h | ||
680 | cache <- fmap Map.assocs . readIORef $ cached | ||
681 | writeIORef cached Map.empty -- hint garbage collector: we're done with this | ||
682 | handleOutgoingToPeer (restrictSocket sock) cache chan snk | ||
683 | |||
684 | |||
685 | greetPeer = | ||
686 | [ EventBeginDocument | ||
687 | , EventBeginElement (streamP "stream") | ||
688 | [("xmlns",[ContentText "jabber:server"]) | ||
689 | ,("version",[ContentText "1.0"]) | ||
690 | ] | ||
691 | ] | ||
692 | |||
693 | goodbyePeer = | ||
694 | [ EventEndElement (streamP "stream") | ||
695 | , EventEndDocument | ||
696 | ] | ||
697 | |||
698 | toPeer sock cache chan = do | ||
699 | let -- log = liftIO . L.putStrLn . ("(>P) " <++>) | ||
700 | send xs = yield xs >> prettyPrint ">P: " xs | ||
701 | send greetPeer | ||
702 | forM_ cache $ \(jid,st) -> do | ||
703 | r <- lift $ xmlifyPresenceForPeer sock (Presence jid st) | ||
704 | send r | ||
705 | fix $ \loop -> do | ||
706 | event <- lift . atomically $ readTChan chan | ||
707 | case event of | ||
708 | OutBoundPresence p -> do | ||
709 | r <- lift $ xmlifyPresenceForPeer sock p | ||
710 | send r | ||
711 | loop | ||
712 | send goodbyePeer | ||
713 | |||
714 | handleOutgoingToPeer sock cache chan snk = do | ||
715 | #ifdef RENDERFLUSH | ||
716 | toPeer sock cache chan | ||
717 | $$ flushList | ||
718 | =$= renderBuilderFlush def | ||
719 | =$= builderToByteStringFlush | ||
720 | =$= discardFlush | ||
721 | =$ snk | ||
722 | #else | ||
723 | toPeer sock cache chan $$ renderChunks =$ snk | ||
724 | #endif | ||
725 | |||
726 | connect' :: SockAddr -> Int -> IO (Maybe Socket) | ||
727 | connect' addr port = do | ||
728 | proto <- getProtocolNumber "tcp" | ||
729 | {- | ||
730 | -- Given (host :: HostName) ... | ||
731 | let hints = defaultHints { addrFlags = [AI_ADDRCONFIG] | ||
732 | , addrProtocol = proto | ||
733 | , addrSocketType = Stream } | ||
734 | addrs <- getAddrInfo (Just hints) (Just host) (Just serv) | ||
735 | firstSuccessful $ map tryToConnect addrs | ||
736 | -} | ||
737 | let getport (SockAddrInet port _) = port | ||
738 | getport (SockAddrInet6 port _ _ _) = port | ||
739 | let withPort (SockAddrInet _ a) port = SockAddrInet (toEnum port) a | ||
740 | withPort (SockAddrInet6 _ a b c) port = SockAddrInet6 (toEnum port) a b c | ||
741 | let doException (SomeException e) = do | ||
742 | L.putStrLn $ "\nFailed to reach "<++> showPeer (RemotePeer addr) <++> " on port "<++>bshow port<++>": " <++> bshow e | ||
743 | return Nothing | ||
744 | handle doException | ||
745 | $ tryToConnect proto (addr `withPort` port) | ||
746 | where | ||
747 | tryToConnect proto addr = | ||
748 | bracketOnError | ||
749 | (socket (socketFamily addr) Stream proto) | ||
750 | (sClose ) -- only done if there's an error | ||
751 | (\sock -> do | ||
752 | connect sock addr | ||
753 | return (Just sock) -- socketToHandle sock ReadWriteMode | ||
754 | ) | ||
755 | |||
756 | |||
757 | |||
758 | sendMessage cons msg peer = do | ||
759 | found <- atomically $ do | ||
760 | consmap <- readTVar cons | ||
761 | return (Map.lookup peer consmap) | ||
762 | let newEntry = do | ||
763 | chan <- atomically newTChan | ||
764 | t <- forkIO $ connect_to_server chan peer | ||
765 | -- L.putStrLn $ "remote-map new: " <++> showPeer peer | ||
766 | return (True,(chan,t)) | ||
767 | (is_new,entry) <- maybe newEntry | ||
768 | ( \(chan,t) -> do | ||
769 | st <- threadStatus t | ||
770 | let running = do | ||
771 | -- L.putStrLn $ "remote-map, thread running: " <++> showPeer peer | ||
772 | return (False,(chan,t)) | ||
773 | died = do | ||
774 | -- L.putStrLn $ "remote-map, thread died("<++>bshow st<++>"): " <++> showPeer peer | ||
775 | newEntry | ||
776 | case st of | ||
777 | ThreadRunning -> running | ||
778 | ThreadBlocked _ -> running | ||
779 | ThreadDied -> died | ||
780 | ThreadFinished -> died | ||
781 | ) | ||
782 | found | ||
783 | -- L.putStrLn $ "sendMessage ->"<++>showPeer peer<++>": "<++>bshow msg | ||
784 | atomically $ writeTChan (fst entry) msg | ||
785 | when is_new . atomically $ | ||
786 | readTVar cons >>= writeTVar cons . Map.insert peer entry | ||
787 | |||
788 | |||
789 | |||
790 | seekRemotePeers :: XMPPConfig config => | ||
791 | config -> TChan Presence -> IO b0 | ||
792 | seekRemotePeers config chan = do | ||
793 | server_connections <- newServerConnections | ||
794 | fix $ \loop -> do | ||
795 | event <- atomically $ readTChan chan | ||
796 | case event of | ||
797 | p@(Presence jid stat) | not (is_remote (peer jid)) -> do | ||
798 | -- L.putStrLn $ "seekRemotePeers: " <++> L.show jid <++> " " <++> bshow stat | ||
799 | runMaybeT $ do | ||
800 | u <- MaybeT . return $ name jid | ||
801 | subscribers <- liftIO $ do | ||
802 | subs <- getSubscribers config u | ||
803 | mapM parseHostNameJID subs | ||
804 | -- liftIO . L.putStrLn $ "subscribers: " <++> bshow subscribers | ||
805 | let peers = Set.map peer (Set.fromList subscribers) | ||
806 | forM_ (Set.toList peers) $ \peer -> do | ||
807 | when (is_remote peer) $ | ||
808 | liftIO $ sendMessage server_connections (OutBoundPresence p) peer | ||
809 | -- TODO: send presence probes for buddies | ||
810 | -- TODO: cache remote presences for clients | ||
811 | _ -> return (Just ()) | ||
812 | loop | ||
813 | |||
814 | xmlifyPresenceForPeer sock (Presence jid stat) = do | ||
815 | -- TODO: accept socket argument and determine local ip address | ||
816 | -- connected to this peer. | ||
817 | addr <- getSocketName sock | ||
818 | let n = name jid | ||
819 | rsc = resource jid | ||
820 | jidstr = toStrict . L.decodeUtf8 | ||
821 | $ n <$++> "@" <?++> showPeer (RemotePeer addr) <++?> "/" <++$> rsc | ||
822 | return | ||
823 | [ EventBeginElement "{jabber:server}presence" | ||
824 | (("from",[ContentText jidstr]):typ stat) | ||
825 | , EventBeginElement "{jabber:server}show" [] | ||
826 | , EventContent (ContentText . shw $ stat) | ||
827 | , EventEndElement "{jabber:server}show" | ||
828 | , EventEndElement "{jabber:server}presence" | ||
829 | ] | ||
830 | where | ||
831 | typ Offline = [("type",[ContentText "unavailable"])] | ||
832 | typ _ = [] | ||
833 | shw Available = "chat" | ||
834 | shw Away = "away" | ||
835 | shw Offline = "away" -- Is this right? | ||
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index f607989d..ff50ab1c 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -67,9 +67,6 @@ import qualified Data.Set as Set | |||
67 | import GetHostByAddr | 67 | import GetHostByAddr |
68 | import XMPPTypes | 68 | import XMPPTypes |
69 | 69 | ||
70 | is_remote (RemotePeer _) = True | ||
71 | is_remote _ = False | ||
72 | |||
73 | getNamesForPeer :: Peer -> IO [ByteString] | 70 | getNamesForPeer :: Peer -> IO [ByteString] |
74 | getNamesForPeer LocalHost = fmap ((:[]) . pack) getHostName | 71 | getNamesForPeer LocalHost = fmap ((:[]) . pack) getHostName |
75 | getNamesForPeer peer@(RemotePeer addr) = do | 72 | getNamesForPeer peer@(RemotePeer addr) = do |
@@ -85,10 +82,6 @@ getNamesForPeer peer@(RemotePeer addr) = do | |||
85 | return . map pack $ names | 82 | return . map pack $ names |
86 | 83 | ||
87 | 84 | ||
88 | peerAddr :: Peer -> SockAddr | ||
89 | peerAddr (RemotePeer addr) = addr | ||
90 | -- peerAddr LocalHost = throw exception | ||
91 | |||
92 | 85 | ||
93 | xmlifyPresenceForPeer sock (Presence jid stat) = do | 86 | xmlifyPresenceForPeer sock (Presence jid stat) = do |
94 | -- TODO: accept socket argument and determine local ip address | 87 | -- TODO: accept socket argument and determine local ip address |
@@ -587,62 +580,6 @@ connect_to_server chan peer = (>> return ()) . runMaybeT $ do | |||
587 | L.putStrLn $ "OUT peer: </stream>" | 580 | L.putStrLn $ "OUT peer: </stream>" |
588 | 581 | ||
589 | 582 | ||
590 | splitJID :: ByteString -> (Maybe ByteString,ByteString,Maybe ByteString) | ||
591 | splitJID bjid = | ||
592 | let xs = L.splitWith (=='@') bjid | ||
593 | ys = L.splitWith (=='/') (last xs) | ||
594 | server = head ys | ||
595 | name | ||
596 | = case xs of | ||
597 | (n:s:_) -> Just n | ||
598 | (s:_) -> Nothing | ||
599 | rsrc = case ys of | ||
600 | (s:_:_) -> Just $ last ys | ||
601 | _ -> Nothing | ||
602 | in (name,server,rsrc) | ||
603 | |||
604 | strip_brackets s = | ||
605 | case L.uncons s of | ||
606 | Just ('[',t) -> L.takeWhile (/=']') t | ||
607 | _ -> s | ||
608 | |||
609 | parseAddressJID :: ByteString -> IO JID | ||
610 | parseAddressJID jid = do | ||
611 | let (name,peer_string,rsc) = splitJID jid | ||
612 | hints = Just $ defaultHints { addrFlags = [ {- AI_NUMERICHOST, -} AI_CANONNAME ] } | ||
613 | peer_string' = unpack . strip_brackets $ peer_string | ||
614 | peer <- do | ||
615 | -- putStrLn $ "getAddrInfo 2 " ++ Prelude.show ( Just (unpack peer_string)) | ||
616 | info <- getAddrInfo hints (Just peer_string') Nothing -- (Just "xmpp-server") | ||
617 | let info0 = head info | ||
618 | return . RemotePeer . addrAddress $ info0 | ||
619 | return $ JID name peer rsc | ||
620 | |||
621 | parseHostNameJID :: ByteString -> IO JID | ||
622 | parseHostNameJID jid = do | ||
623 | let (name,peer_string,rsc) = splitJID jid | ||
624 | hints = Just $ defaultHints { addrFlags = [ AI_CANONNAME ] } | ||
625 | peer <- do | ||
626 | if peer_string=="localhost" | ||
627 | then return LocalHost | ||
628 | else do | ||
629 | -- putStrLn $ "getAddrInfo 3 " ++ Prelude.show ( Just (unpack peer_string)) | ||
630 | info <- getAddrInfo hints (Just (unpack peer_string)) Nothing -- (Just "xmpp-server") | ||
631 | let info0 = head info | ||
632 | cname = addrCanonName info0 | ||
633 | if cname==Just "localhost" | ||
634 | then return LocalHost | ||
635 | else do | ||
636 | self <- getHostName | ||
637 | return $ if Just self==cname | ||
638 | then LocalHost | ||
639 | else RemotePeer (addrAddress info0) | ||
640 | return $ JID name peer rsc | ||
641 | |||
642 | socketFamily (SockAddrInet _ _) = AF_INET | ||
643 | socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6 | ||
644 | socketFamily (SockAddrUnix _) = AF_UNIX | ||
645 | |||
646 | connect' :: SockAddr -> Int -> IO (Maybe Socket) | 583 | connect' :: SockAddr -> Int -> IO (Maybe Socket) |
647 | connect' addr port = do | 584 | connect' addr port = do |
648 | proto <- getProtocolNumber "tcp" | 585 | proto <- getProtocolNumber "tcp" |
diff --git a/Presence/XMPPTypes.hs b/Presence/XMPPTypes.hs index e3bbfd16..8af1018c 100644 --- a/Presence/XMPPTypes.hs +++ b/Presence/XMPPTypes.hs | |||
@@ -2,10 +2,28 @@ | |||
2 | {-# LANGUAGE TypeFamilies #-} | 2 | {-# LANGUAGE TypeFamilies #-} |
3 | module XMPPTypes where | 3 | module XMPPTypes where |
4 | 4 | ||
5 | import Network.Socket (Socket,SockAddr(..)) | 5 | import Network.Socket |
6 | ( Socket | ||
7 | , Family(..) | ||
8 | , SockAddr(..) | ||
9 | , getAddrInfo | ||
10 | , addrCanonName | ||
11 | , addrAddress | ||
12 | , defaultHints | ||
13 | , AddrInfo(..) | ||
14 | , AddrInfoFlag(..) | ||
15 | ) | ||
16 | import Network.BSD (getHostName) | ||
6 | import System.IO (Handle) | 17 | import System.IO (Handle) |
7 | import Control.Concurrent.STM (TChan) | 18 | import Control.Concurrent.STM (TChan) |
8 | import Data.ByteString.Lazy.Char8 as L (ByteString,unpack,pack) | 19 | import Data.ByteString.Lazy.Char8 as L |
20 | ( ByteString | ||
21 | , unpack | ||
22 | , pack | ||
23 | , splitWith | ||
24 | , uncons | ||
25 | , takeWhile | ||
26 | ) | ||
9 | import Text.Show.ByteString as L | 27 | import Text.Show.ByteString as L |
10 | import Data.Binary.Builder as B | 28 | import Data.Binary.Builder as B |
11 | import Data.Binary.Put | 29 | import Data.Binary.Put |
@@ -82,4 +100,68 @@ showPeer (RemotePeer addr@(SockAddrInet6 _ _ _ _)) = pack $ stripColon (Prelude. | |||
82 | where | 100 | where |
83 | (pre,bracket) = break (==']') s | 101 | (pre,bracket) = break (==']') s |
84 | 102 | ||
103 | is_remote (RemotePeer _) = True | ||
104 | is_remote _ = False | ||
105 | |||
106 | parseHostNameJID :: ByteString -> IO JID | ||
107 | parseHostNameJID jid = do | ||
108 | let (name,peer_string,rsc) = splitJID jid | ||
109 | hints = Just $ defaultHints { addrFlags = [ AI_CANONNAME ] } | ||
110 | peer <- do | ||
111 | if peer_string=="localhost" | ||
112 | then return LocalHost | ||
113 | else do | ||
114 | -- putStrLn $ "getAddrInfo 3 " ++ Prelude.show ( Just (unpack peer_string)) | ||
115 | info <- getAddrInfo hints (Just (unpack peer_string)) Nothing -- (Just "xmpp-server") | ||
116 | let info0 = head info | ||
117 | cname = addrCanonName info0 | ||
118 | if cname==Just "localhost" | ||
119 | then return LocalHost | ||
120 | else do | ||
121 | self <- getHostName | ||
122 | return $ if Just self==cname | ||
123 | then LocalHost | ||
124 | else RemotePeer (addrAddress info0) | ||
125 | return $ JID name peer rsc | ||
126 | |||
127 | splitJID :: ByteString -> (Maybe ByteString,ByteString,Maybe ByteString) | ||
128 | splitJID bjid = | ||
129 | let xs = L.splitWith (=='@') bjid | ||
130 | ys = L.splitWith (=='/') (last xs) | ||
131 | server = head ys | ||
132 | name | ||
133 | = case xs of | ||
134 | (n:s:_) -> Just n | ||
135 | (s:_) -> Nothing | ||
136 | rsrc = case ys of | ||
137 | (s:_:_) -> Just $ last ys | ||
138 | _ -> Nothing | ||
139 | in (name,server,rsrc) | ||
140 | |||
141 | strip_brackets s = | ||
142 | case L.uncons s of | ||
143 | Just ('[',t) -> L.takeWhile (/=']') t | ||
144 | _ -> s | ||
145 | |||
146 | |||
147 | parseAddressJID :: ByteString -> IO JID | ||
148 | parseAddressJID jid = do | ||
149 | let (name,peer_string,rsc) = splitJID jid | ||
150 | hints = Just $ defaultHints { addrFlags = [ {- AI_NUMERICHOST, -} AI_CANONNAME ] } | ||
151 | peer_string' = unpack . strip_brackets $ peer_string | ||
152 | peer <- do | ||
153 | -- putStrLn $ "getAddrInfo 2 " ++ Prelude.show ( Just (unpack peer_string)) | ||
154 | info <- getAddrInfo hints (Just peer_string') Nothing -- (Just "xmpp-server") | ||
155 | let info0 = head info | ||
156 | return . RemotePeer . addrAddress $ info0 | ||
157 | return $ JID name peer rsc | ||
158 | |||
159 | peerAddr :: Peer -> SockAddr | ||
160 | peerAddr (RemotePeer addr) = addr | ||
161 | -- peerAddr LocalHost = throw exception | ||
162 | |||
163 | socketFamily (SockAddrInet _ _) = AF_INET | ||
164 | socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6 | ||
165 | socketFamily (SockAddrUnix _) = AF_UNIX | ||
166 | |||
85 | 167 | ||