summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Crayne <jim.crayne@gmail.com>2013-06-30 18:27:12 -0400
committerJames Crayne <jim.crayne@gmail.com>2013-06-30 18:27:12 -0400
commitb70209c295681a89b64f7527a2ecae23d9bb9bc2 (patch)
treed86775d956e4d69f9308a20695a6683b2ce2a9dc
parentffa072d469c904bf30756e2acbdb1c9b78508c35 (diff)
parent332002c101682f9c796a973cf62a82bef2c4659e (diff)
Merge branch 'master' of samwise:presence
-rw-r--r--Presence/ServerC.hs9
-rw-r--r--Presence/XMPP.hs319
-rw-r--r--Presence/XMPPServer.hs63
-rw-r--r--Presence/XMPPTypes.hs86
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
13import Network.Socket as Socket 14import 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
126outgoing :: MonadIO m => Handle -> Sink S.ByteString m () 127packetSink :: MonadIO m => Handle -> Sink S.ByteString m ()
127outgoing h = do 128packetSink 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
16import ByteStringOperators 16import ByteStringOperators
17 17
18import Data.HList 18import Data.HList
19import Network.Socket (Family) 19import Network.Socket
20import Network.BSD (PortNumber) 20 ( Family
21 , connect
22 , socketToHandle
23 , sClose
24 , Socket(..)
25 , socket
26 , SocketType(..)
27 )
28import Network.BSD
29 ( PortNumber
30 , getHostName
31 , hostName
32 , hostAliases
33 , getProtocolNumber
34 )
35import System.IO
36 ( BufferMode(..)
37 , IOMode(..)
38 , hSetBuffering
39 )
40import Control.Exception
41 ( bracketOnError )
21import Control.Concurrent.STM 42import Control.Concurrent.STM
22import Data.Conduit 43import Data.Conduit
23import qualified Data.Conduit.List as CL 44import qualified Data.Conduit.List as CL
@@ -27,6 +48,8 @@ import qualified Data.ByteString.Char8 as S (pack,putStr,putStrLn,append)
27import qualified Data.ByteString.Lazy.Char8 as L 48import qualified Data.ByteString.Lazy.Char8 as L
28 ( putStrLn 49 ( putStrLn
29 , fromChunks 50 , fromChunks
51 , unlines
52 , hPutStrLn
30 ) 53 )
31import Control.Concurrent (forkIO,killThread) 54import Control.Concurrent (forkIO,killThread)
32import Control.Concurrent.Async 55import Control.Concurrent.Async
@@ -39,7 +62,6 @@ import Control.Monad as Monad
39import Text.XML.Stream.Parse (parseBytes,content) 62import Text.XML.Stream.Parse (parseBytes,content)
40import Text.XML.Stream.Render 63import Text.XML.Stream.Render
41import Data.XML.Types as XML 64import Data.XML.Types as XML
42import Network.BSD (getHostName,hostName,hostAliases)
43import Data.Text.Encoding as S (decodeUtf8,encodeUtf8) 65import Data.Text.Encoding as S (decodeUtf8,encodeUtf8)
44import Data.Text.Lazy.Encoding as L (decodeUtf8,encodeUtf8) 66import Data.Text.Lazy.Encoding as L (decodeUtf8,encodeUtf8)
45import Data.Text.Lazy (toStrict) 67import Data.Text.Lazy (toStrict)
@@ -53,7 +75,13 @@ import Data.Conduit.Blaze
53import Data.List (find) 75import Data.List (find)
54import qualified Text.Show.ByteString as L 76import qualified Text.Show.ByteString as L
55import NestingXML 77import NestingXML
78import Data.Set as Set (Set)
56import qualified Data.Set as Set 79import qualified Data.Set as Set
80import qualified Data.Map as Map
81import GHC.Conc
82 ( threadStatus
83 , ThreadStatus(..)
84 )
57 85
58data Commands = Send [XML.Event] | QuitThread 86data 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 =
121mawait :: Monad m => MaybeT (ConduitM i o m) i 149mawait :: Monad m => MaybeT (ConduitM i o m) i
122mawait = MaybeT await 150mawait = MaybeT await
123 151
152-- Note: This function ignores name space qualification
124elementAttrs expected (EventBeginElement name attrs) 153elementAttrs 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
410toClient :: MonadIO m => TChan Presence -> TChan Commands -> Source m [XML.Event] 439toClient :: MonadIO m => TChan Presence -> TChan Commands -> Source m [XML.Event]
411toClient pchan cmdChan = fix $ \loop -> do 440toClient 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
423handleClient 453handleClient
@@ -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
543handlePeerPresence (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)
551handlePeerPresence (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
577matchAttribMaybe 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
582matchAttribMaybe name Nothing attrs
583 | find ( (==name) . fst) attrs==Nothing
584 = True
585matchAttribMaybe name Nothing attrs
586 | otherwise
587 = False
588
589presenceTypeOffline = Just "unavailable"
590presenceTypeOnline = Nothing
591
592isPresenceOf (EventBeginElement name attrs) testType
593 | name=="{jabber:server}presence"
594 && matchAttribMaybe "type" testType attrs
595 = True
596isPresenceOf _ _ = False
597
512fromPeer :: (MonadThrow m,MonadIO m, XMPPSession session) => 598fromPeer :: (MonadThrow m,MonadIO m, XMPPSession session) =>
513 session -> Sink XML.Event m () 599 (session, TVar (Set JID)) -> Sink XML.Event m ()
514fromPeer session = doNestingXML $ do 600fromPeer 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{-
537seekRemotePeers :: XMPPConfig config => 634seekRemotePeers :: XMPPConfig config =>
538 config -> TChan Presence -> IO () 635 config -> TChan Presence -> IO ()
539seekRemotePeers config chan = do 636seekRemotePeers config chan = do
540 putStrLn "unimplemented: seekRemotePeers" 637 putStrLn "unimplemented: seekRemotePeers"
541 -- TODO 638 -- TODO
542 return () 639 return ()
640-}
641
642data OutBoundMessage = OutBoundPresence Presence
643 deriving Prelude.Show
644
645newServerConnections = atomically $ newTVar Map.empty
646
647connect_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
685greetPeer =
686 [ EventBeginDocument
687 , EventBeginElement (streamP "stream")
688 [("xmlns",[ContentText "jabber:server"])
689 ,("version",[ContentText "1.0"])
690 ]
691 ]
692
693goodbyePeer =
694 [ EventEndElement (streamP "stream")
695 , EventEndDocument
696 ]
697
698toPeer 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
714handleOutgoingToPeer 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
726connect' :: SockAddr -> Int -> IO (Maybe Socket)
727connect' 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
758sendMessage 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
790seekRemotePeers :: XMPPConfig config =>
791 config -> TChan Presence -> IO b0
792seekRemotePeers 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
814xmlifyPresenceForPeer 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
67import GetHostByAddr 67import GetHostByAddr
68import XMPPTypes 68import XMPPTypes
69 69
70is_remote (RemotePeer _) = True
71is_remote _ = False
72
73getNamesForPeer :: Peer -> IO [ByteString] 70getNamesForPeer :: Peer -> IO [ByteString]
74getNamesForPeer LocalHost = fmap ((:[]) . pack) getHostName 71getNamesForPeer LocalHost = fmap ((:[]) . pack) getHostName
75getNamesForPeer peer@(RemotePeer addr) = do 72getNamesForPeer 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
88peerAddr :: Peer -> SockAddr
89peerAddr (RemotePeer addr) = addr
90-- peerAddr LocalHost = throw exception
91
92 85
93xmlifyPresenceForPeer sock (Presence jid stat) = do 86xmlifyPresenceForPeer 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
590splitJID :: ByteString -> (Maybe ByteString,ByteString,Maybe ByteString)
591splitJID 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
604strip_brackets s =
605 case L.uncons s of
606 Just ('[',t) -> L.takeWhile (/=']') t
607 _ -> s
608
609parseAddressJID :: ByteString -> IO JID
610parseAddressJID 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
621parseHostNameJID :: ByteString -> IO JID
622parseHostNameJID 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
642socketFamily (SockAddrInet _ _) = AF_INET
643socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6
644socketFamily (SockAddrUnix _) = AF_UNIX
645
646connect' :: SockAddr -> Int -> IO (Maybe Socket) 583connect' :: SockAddr -> Int -> IO (Maybe Socket)
647connect' addr port = do 584connect' 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 #-}
3module XMPPTypes where 3module XMPPTypes where
4 4
5import Network.Socket (Socket,SockAddr(..)) 5import Network.Socket
6 ( Socket
7 , Family(..)
8 , SockAddr(..)
9 , getAddrInfo
10 , addrCanonName
11 , addrAddress
12 , defaultHints
13 , AddrInfo(..)
14 , AddrInfoFlag(..)
15 )
16import Network.BSD (getHostName)
6import System.IO (Handle) 17import System.IO (Handle)
7import Control.Concurrent.STM (TChan) 18import Control.Concurrent.STM (TChan)
8import Data.ByteString.Lazy.Char8 as L (ByteString,unpack,pack) 19import Data.ByteString.Lazy.Char8 as L
20 ( ByteString
21 , unpack
22 , pack
23 , splitWith
24 , uncons
25 , takeWhile
26 )
9import Text.Show.ByteString as L 27import Text.Show.ByteString as L
10import Data.Binary.Builder as B 28import Data.Binary.Builder as B
11import Data.Binary.Put 29import 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
103is_remote (RemotePeer _) = True
104is_remote _ = False
105
106parseHostNameJID :: ByteString -> IO JID
107parseHostNameJID 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
127splitJID :: ByteString -> (Maybe ByteString,ByteString,Maybe ByteString)
128splitJID 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
141strip_brackets s =
142 case L.uncons s of
143 Just ('[',t) -> L.takeWhile (/=']') t
144 _ -> s
145
146
147parseAddressJID :: ByteString -> IO JID
148parseAddressJID 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
159peerAddr :: Peer -> SockAddr
160peerAddr (RemotePeer addr) = addr
161-- peerAddr LocalHost = throw exception
162
163socketFamily (SockAddrInet _ _) = AF_INET
164socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6
165socketFamily (SockAddrUnix _) = AF_UNIX
166
85 167