summaryrefslogtreecommitdiff
path: root/Presence/XMPPServer.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2013-06-25 18:09:08 -0400
committerjoe <joe@jerkface.net>2013-06-25 18:09:08 -0400
commite74b4448748af7e068a9d162b62fccc0ede0e81a (patch)
treede08edd1b13cb922f908684c0d8e28020f18e430 /Presence/XMPPServer.hs
parent4ae6bf78a836cf35450387431aea93d522ce8f84 (diff)
SEnd addresses on the wire between peers rather than domain names.
Diffstat (limited to 'Presence/XMPPServer.hs')
-rw-r--r--Presence/XMPPServer.hs222
1 files changed, 183 insertions, 39 deletions
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs
index 387b223e..aa3140e5 100644
--- a/Presence/XMPPServer.hs
+++ b/Presence/XMPPServer.hs
@@ -21,6 +21,9 @@ import Data.ByteString.Lazy.Char8 as L
21 ( hPutStrLn 21 ( hPutStrLn
22 , unlines 22 , unlines
23 , lines 23 , lines
24 , uncons
25 , takeWhile
26 , concat
24 , splitWith 27 , splitWith
25 , drop 28 , drop
26 , ByteString 29 , ByteString
@@ -57,19 +60,69 @@ import Data.Binary.Builder as B
57import Data.Binary.Put 60import Data.Binary.Put
58import qualified Data.Map as Map 61import qualified Data.Map as Map
59import GHC.Conc 62import GHC.Conc
60import Network.BSD 63import Network.BSD hiding (getHostByAddr)
61import Control.Concurrent.Async 64import Control.Concurrent.Async
62import qualified Data.Set as Set 65import qualified Data.Set as Set
66import GetHostByAddr
67
68data Peer = LocalHost | RemotePeer SockAddr
69 deriving (Eq,Prelude.Show)
70
71instance Ord Peer where
72 LocalHost <= _
73 = True
74 RemotePeer (SockAddrUnix a) <= RemotePeer (SockAddrUnix b)
75 = a <= b
76 RemotePeer (SockAddrUnix _) <= _
77 = True
78 RemotePeer (SockAddrInet aport a) <= RemotePeer (SockAddrInet bport b)
79 = (a,aport) <= (b,bport)
80 RemotePeer (SockAddrInet aport a) <= _
81 = True
82 RemotePeer (SockAddrInet6 aport aflow a ascope) <= RemotePeer (SockAddrInet6 bport bflow b bscope)
83 = (a,aport,ascope,aflow) <= (b,bport,bscope,bflow)
84 a <= b = False
63 85
64-- | Jabber ID (JID) datatype 86-- | Jabber ID (JID) datatype
65data JID = JID { name :: Maybe ByteString 87data JID = JID { name :: Maybe ByteString
66 , server :: ByteString 88 , peer :: Peer
67 , resource :: Maybe ByteString 89 , resource :: Maybe ByteString
68 } 90 }
69 deriving (Ord,Eq) 91 deriving (Eq,Ord)
92
93is_remote (RemotePeer _) = True
94is_remote _ = False
95
96getNamesForPeer :: Peer -> IO [ByteString]
97getNamesForPeer LocalHost = fmap ((:[]) . pack) getHostName
98getNamesForPeer peer@(RemotePeer addr) = do
99 {-
100 let hints = Just $ defaultHints { addrFlags = [ AI_CANONNAME ] }
101 L.putStrLn $ "getAddrInfo 1 " <++> showPeer peer
102 infos <- getAddrInfo hints (Just . unpack . showPeer $ peer) Nothing
103 return . map pack . mapMaybe addrCanonName $ infos
104 -}
105 -- ent <- getHostByName (unpack . showPeer $ peer)
106 ent <- getHostByAddr addr -- AF_UNSPEC addr
107 let names = hostName ent : hostAliases ent
108 return . map pack $ names
109
110
111showPeer :: Peer -> ByteString
112showPeer LocalHost = "localhost"
113showPeer (RemotePeer addr@(SockAddrInet _ _)) = pack $ stripColon (Prelude.show addr)
114 where stripColon s = pre where (pre,port) = break (==':') s
115showPeer (RemotePeer addr@(SockAddrInet6 _ _ _ _)) = pack $ stripColon (Prelude.show addr)
116 where stripColon s = if null bracket then pre else pre ++ "]"
117 where
118 (pre,bracket) = break (==']') s
119
120peerAddr :: Peer -> SockAddr
121peerAddr (RemotePeer addr) = addr
122-- peerAddr LocalHost = throw exception
70 123
71instance L.Show JID where 124instance L.Show JID where
72 showp (JID n s r ) = putBuilder . B.fromLazyByteString $ n <$++> "@" <?++> s <++?> "/" <++$> r 125 showp (JID n s r ) = putBuilder . B.fromLazyByteString $ n <$++> "@" <?++> showPeer s <++?> "/" <++$> r
73 126
74instance Prelude.Show JID where 127instance Prelude.Show JID where
75 show jid = L.unpack $ L.show jid 128 show jid = L.unpack $ L.show jid
@@ -87,12 +140,38 @@ data JabberShow = Offline
87data Presence = Presence JID JabberShow 140data Presence = Presence JID JabberShow
88 deriving Prelude.Show 141 deriving Prelude.Show
89 142
90xmlifyPresence (Presence jid stat) = L.unlines 143xmlifyPresenceForPeer sock (Presence jid stat) = do
91 [ "<presence from='" <++> L.show jid <++> "' " <++> typ stat <++> ">" 144 -- TODO: accept socket argument and determine local ip address
92 , "<show>" <++> shw stat <++> "</show>" 145 -- connected to this peer.
93 , "</presence>" 146 addr <- getSocketName sock
94 ] 147 let n = name jid
148 rsc = resource jid
149 jid_str = n <$++> "@" <?++> showPeer (RemotePeer addr) <++?> "/" <++$> rsc
150 return . L.unlines $
151 [ "<presence from='" <++> jid_str <++> "' " <++> typ stat <++> ">"
152 , "<show>" <++> shw stat <++> "</show>"
153 , "</presence>"
154 ]
155 where
156 typ Offline = " type='unavailable'"
157 typ _ = ""
158 shw Available = "chat"
159 shw Away = "away"
160 shw Offline = "away" -- Is this right?
161
162xmlifyPresenceForClient (Presence jid stat) = do
163 let n = name jid
164 rsc = resource jid
165 names <- getNamesForPeer (peer jid)
166 let tostr p = n <$++> "@" <?++> p <++?> "/" <++$> rsc
167 jidstrs = fmap tostr names
168 return (L.concat $ map doit jidstrs)
95 where 169 where
170 doit jidstr = L.unlines
171 [ "<presence from='" <++> jidstr <++> "' " <++> typ stat <++> ">"
172 , "<show>" <++> shw stat <++> "</show>"
173 , "</presence>"
174 ]
96 typ Offline = " type='unavailable'" 175 typ Offline = " type='unavailable'"
97 typ _ = "" 176 typ _ = ""
98 shw Available = "chat" 177 shw Available = "chat"
@@ -160,7 +239,7 @@ startCon session_factory sock st = do
160 L.putStrLn $ "PRESENCE: " <++> bshow presence 239 L.putStrLn $ "PRESENCE: " <++> bshow presence
161 -- TODO: it violates spec to send presence information before 240 -- TODO: it violates spec to send presence information before
162 -- a resource is bound. 241 -- a resource is bound.
163 let r = xmlifyPresence presence 242 r <- xmlifyPresenceForClient presence
164 hPutStrLn h r 243 hPutStrLn h r
165 L.putStrLn $ "\nOUT:\n" <++> r 244 L.putStrLn $ "\nOUT:\n" <++> r
166 Right (Send r) -> 245 Right (Send r) ->
@@ -282,7 +361,11 @@ doCon st elem cont = do
282 atomically $ writeTChan cmdChan (Send r) 361 atomically $ writeTChan cmdChan (Send r)
283 -- hPutStrLn h r 362 -- hPutStrLn h r
284 L.putStrLn $ "\nOUT:\n" <++> r 363 L.putStrLn $ "\nOUT:\n" <++> r
285 host <- fmap server $ getJID session 364 -- host <- fmap pack $ getHostName -- Assume localhost for client session JID
365 host <- do
366 jid <- getJID session
367 names <- getNamesForPeer (peer jid)
368 return (head names)
286 369
287 putStrLn $ (Prelude.show $ hang (text "\nIN:") 2 $ pp elem) ++ "\n" 370 putStrLn $ (Prelude.show $ hang (text "\nIN:") 2 $ pp elem) ++ "\n"
288 371
@@ -369,16 +452,18 @@ doPeer st elem cont = do
369 case (jid,typ) of 452 case (jid,typ) of
370 (Just jid,Just "unavailable") -> do 453 (Just jid,Just "unavailable") -> do
371 L.putStrLn $ "INBOUND PRESENCE! Offline jid=" <++> jid 454 L.putStrLn $ "INBOUND PRESENCE! Offline jid=" <++> jid
372 announcePresence session (Presence (parseJID jid) Offline) 455 -- parseAddressJID -- convert peer reported user@address to JID data structure
456 peer_jid <- parseAddressJID jid
457 announcePresence session (Presence peer_jid Offline)
373 (Just jid,Just typ) -> 458 (Just jid,Just typ) ->
374 -- possible probe, ignored for now 459 -- possible probe, ignored for now
375 L.putStrLn $ "INBOUND PRESENCE! "<++>typ<++>" jid="<++>jid 460 L.putStrLn $ "INBOUND PRESENCE! "<++>typ<++>" jid="<++>jid
376 (Just jid,Nothing) -> do 461 (Just jid,Nothing) -> do
377 let string (CString _ s _) = [s] 462 let string (CString _ s _) = [s]
378 show = listToMaybe . Prelude.concatMap string . tagcontent "show" $ content 463 stat = listToMaybe . Prelude.concatMap string . tagcontent "show" $ content
379
380 -- Available or Away. 464 -- Available or Away.
381 L.putStrLn $ "INBOUND PRESENCE! avail/away jid=" <++> jid 465 names <- parseAddressJID jid >>= getNamesForPeer . peer
466 L.putStrLn $ "INBOUND PRESENCE! "<++>bshow (stat,names)<++>" avail/away jid=" <++> jid
382 -- todo: announcePresence 467 -- todo: announcePresence
383 _ -> return () -- putStrLn $ "inbound unhandled: "++show v 468 _ -> return () -- putStrLn $ "inbound unhandled: "++show v
384 cont () 469 cont ()
@@ -426,16 +511,16 @@ sendMessage cons msg peer = do
426 let newEntry = do 511 let newEntry = do
427 chan <- atomically newTChan 512 chan <- atomically newTChan
428 t <- forkIO $ connect_to_server chan peer 513 t <- forkIO $ connect_to_server chan peer
429 L.putStrLn $ "remote-map new: " <++> peer 514 L.putStrLn $ "remote-map new: " <++> showPeer peer
430 return (True,(chan,t)) 515 return (True,(chan,t))
431 (is_new,entry) <- maybe newEntry 516 (is_new,entry) <- maybe newEntry
432 ( \(chan,t) -> do 517 ( \(chan,t) -> do
433 st <- threadStatus t 518 st <- threadStatus t
434 let running = do 519 let running = do
435 L.putStrLn $ "remote-map, thread running: " <++> peer 520 L.putStrLn $ "remote-map, thread running: " <++> showPeer peer
436 return (False,(chan,t)) 521 return (False,(chan,t))
437 died = do 522 died = do
438 L.putStrLn $ "remote-map, thread died("<++>bshow st<++>"): " <++> peer 523 L.putStrLn $ "remote-map, thread died("<++>bshow st<++>"): " <++> showPeer peer
439 newEntry 524 newEntry
440 case st of 525 case st of
441 ThreadRunning -> running 526 ThreadRunning -> running
@@ -444,7 +529,7 @@ sendMessage cons msg peer = do
444 ThreadFinished -> died 529 ThreadFinished -> died
445 ) 530 )
446 found 531 found
447 L.putStrLn $ "sendMessage ->"<++>peer<++>": "<++>bshow msg 532 L.putStrLn $ "sendMessage ->"<++>showPeer peer<++>": "<++>bshow msg
448 atomically $ writeTChan (fst entry) msg 533 atomically $ writeTChan (fst entry) msg
449 when is_new . atomically $ 534 when is_new . atomically $
450 readTVar cons >>= writeTVar cons . Map.insert peer entry 535 readTVar cons >>= writeTVar cons . Map.insert peer entry
@@ -452,7 +537,7 @@ sendMessage cons msg peer = do
452connect_to_server chan peer = (>> return ()) . runMaybeT $ do 537connect_to_server chan peer = (>> return ()) . runMaybeT $ do
453 let port = "5269" 538 let port = "5269"
454 539
455 connected <- liftIO . async $ connect' (unpack peer) port 540 connected <- liftIO . async $ connect' (peerAddr peer) port
456 541
457 -- We'll cache Presence notifications until the socket 542 -- We'll cache Presence notifications until the socket
458 -- is ready. 543 -- is ready.
@@ -486,14 +571,14 @@ connect_to_server chan peer = (>> return ()) . runMaybeT $ do
486 cache <- fmap Map.assocs . readIORef $ cached 571 cache <- fmap Map.assocs . readIORef $ cached
487 writeIORef cached Map.empty -- hint garbage collector: we're done with this 572 writeIORef cached Map.empty -- hint garbage collector: we're done with this
488 forM_ cache $ \(jid,st) -> do 573 forM_ cache $ \(jid,st) -> do
489 let r = xmlifyPresence (Presence jid st) 574 r <- xmlifyPresenceForPeer sock (Presence jid st)
490 hPutStrLn h r 575 hPutStrLn h r
491 L.putStrLn $ "REMOTE-OUT (cache):\n" <++> r <++> "\n" 576 L.putStrLn $ "REMOTE-OUT (cache):\n" <++> r <++> "\n"
492 fix $ \loop -> do 577 fix $ \loop -> do
493 event <- atomically $ readTChan chan 578 event <- atomically $ readTChan chan
494 case event of 579 case event of
495 OutBoundPresence p -> do 580 OutBoundPresence p -> do
496 let r = xmlifyPresence p 581 r <- xmlifyPresenceForPeer sock p
497 hPutStrLn h r 582 hPutStrLn h r
498 L.putStrLn $ "REMOTE-OUT:\n" <++> r <++> "\n" 583 L.putStrLn $ "REMOTE-OUT:\n" <++> r <++> "\n"
499 loop 584 loop
@@ -501,34 +586,90 @@ connect_to_server chan peer = (>> return ()) . runMaybeT $ do
501 L.putStrLn $ "REMOTE-OUT: </stream>" 586 L.putStrLn $ "REMOTE-OUT: </stream>"
502 587
503 588
504parseJID :: ByteString -> JID 589splitJID :: ByteString -> (Maybe ByteString,ByteString,Maybe ByteString)
505parseJID bjid = 590splitJID bjid =
506 let xs = L.splitWith (=='@') bjid 591 let xs = L.splitWith (=='@') bjid
507 ys = L.splitWith (=='/') (last xs) 592 ys = L.splitWith (=='/') (last xs)
508 (name,server) 593 server = head ys
594 name
509 = case xs of 595 = case xs of
510 (n:s:_) -> (Just n,s) 596 (n:s:_) -> Just n
511 (s:_) -> (Nothing,s) 597 (s:_) -> Nothing
512 rsrc = case ys of 598 rsrc = case ys of
513 (s:_:_) -> Just $ last ys 599 (s:_:_) -> Just $ last ys
514 _ -> Nothing 600 _ -> Nothing
515 in JID name server rsrc 601 in (name,server,rsrc)
516 602
517connect' :: HostName -> ServiceName -> IO (Maybe Socket) 603strip_brackets s =
518connect' host serv = do 604 case L.uncons s of
605 Just ('[',t) -> L.takeWhile (/=']') t
606 _ -> s
607
608parseAddressJID :: ByteString -> IO JID
609parseAddressJID jid = do
610 let (name,peer_string,rsc) = splitJID jid
611 hints = Just $ defaultHints { addrFlags = [ {- AI_NUMERICHOST, -} AI_CANONNAME ] }
612 peer_string' = unpack . strip_brackets $ peer_string
613 peer <- do
614 putStrLn $ "getAddrInfo 2 " ++ Prelude.show ( Just (unpack peer_string))
615 info <- getAddrInfo hints (Just peer_string') Nothing -- (Just "xmpp-server")
616 let info0 = head info
617 return . RemotePeer . addrAddress $ info0
618 return $ JID name peer rsc
619
620parseHostNameJID :: ByteString -> IO JID
621parseHostNameJID jid = do
622 let (name,peer_string,rsc) = splitJID jid
623 hints = Just $ defaultHints { addrFlags = [ AI_CANONNAME ] }
624 peer <- do
625 if peer_string=="localhost"
626 then return LocalHost
627 else do
628 putStrLn $ "getAddrInfo 3 " ++ Prelude.show ( Just (unpack peer_string))
629 info <- getAddrInfo hints (Just (unpack peer_string)) Nothing -- (Just "xmpp-server")
630 let info0 = head info
631 cname = addrCanonName info0
632 if cname==Just "localhost"
633 then return LocalHost
634 else do
635 self <- getHostName
636 return $ if Just self==cname
637 then LocalHost
638 else RemotePeer (addrAddress info0)
639 return $ JID name peer rsc
640
641socketFamily (SockAddrInet _ _) = AF_INET
642socketFamily (SockAddrInet6 _ _ _ _) = AF_INET6
643socketFamily (SockAddrUnix _) = AF_UNIX
644
645connect' :: SockAddr -> ServiceName -> IO (Maybe Socket)
646connect' addr serv = do
519 proto <- getProtocolNumber "tcp" 647 proto <- getProtocolNumber "tcp"
648 {-
649 -- Given (host :: HostName) ...
520 let hints = defaultHints { addrFlags = [AI_ADDRCONFIG] 650 let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]
521 , addrProtocol = proto 651 , addrProtocol = proto
522 , addrSocketType = Stream } 652 , addrSocketType = Stream }
523 addrs <- getAddrInfo (Just hints) (Just host) (Just serv) 653 addrs <- getAddrInfo (Just hints) (Just host) (Just serv)
524 firstSuccessful $ map tryToConnect addrs 654 firstSuccessful $ map tryToConnect addrs
655 -}
656 let getport (SockAddrInet port _) = port
657 getport (SockAddrInet6 port _ _ _) = port
658 let port = getport addr
659 let withPort (SockAddrInet _ a) port = SockAddrInet port a
660 withPort (SockAddrInet6 _ a b c) port = SockAddrInet6 port a b c
661 let doException (SomeException e) = do
662 L.putStrLn $ "\nFailed to reach "<++> showPeer (RemotePeer addr) <++> " on port "<++>bshow port<++>": " <++> bshow e
663 return Nothing
664 handle doException
665 $ tryToConnect proto (addr `withPort` 5269)
525 where 666 where
526 tryToConnect addr = 667 tryToConnect proto addr =
527 bracketOnError 668 bracketOnError
528 (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)) 669 (socket (socketFamily addr) Stream proto)
529 (sClose ) -- only done if there's an error 670 (sClose ) -- only done if there's an error
530 (\sock -> do 671 (\sock -> do
531 connect sock (addrAddress addr) 672 connect sock addr
532 return (Just sock) -- socketToHandle sock ReadWriteMode 673 return (Just sock) -- socketToHandle sock ReadWriteMode
533 ) 674 )
534 675
@@ -547,8 +688,8 @@ firstSuccessful (p:ps) = catchIO p $ \e ->
547 688
548 689
549seekRemotePeers :: XMPPConfig config => 690seekRemotePeers :: XMPPConfig config =>
550 (ByteString -> Bool) -> config -> TChan Presence -> IO b0 691 config -> TChan Presence -> IO b0
551seekRemotePeers is_peer config chan = do 692seekRemotePeers config chan = do
552 server_connections <- newServerConnections 693 server_connections <- newServerConnections
553 fix $ \loop -> do 694 fix $ \loop -> do
554 event <- atomically $ readTChan chan 695 event <- atomically $ readTChan chan
@@ -557,10 +698,13 @@ seekRemotePeers is_peer config chan = do
557 L.putStrLn $ "seekRemotePeers: " <++> L.show jid <++> " " <++> bshow stat 698 L.putStrLn $ "seekRemotePeers: " <++> L.show jid <++> " " <++> bshow stat
558 runMaybeT $ do 699 runMaybeT $ do
559 u <- MaybeT . return $ name jid 700 u <- MaybeT . return $ name jid
560 subscribers <- liftIO $ getSubscribers config u 701 subscribers <- liftIO $ do
702 subs <- getSubscribers config u
703 mapM parseHostNameJID subs
561 liftIO . L.putStrLn $ "subscribers: " <++> bshow subscribers 704 liftIO . L.putStrLn $ "subscribers: " <++> bshow subscribers
562 let peers = Set.map (server . parseJID) (Set.fromList subscribers) 705 -- parseJID -- get subscriber list reported user@hostname to JID data structure
706 let peers = Set.map peer (Set.fromList subscribers)
563 forM_ (Set.toList peers) $ \peer -> do 707 forM_ (Set.toList peers) $ \peer -> do
564 when (is_peer peer) $ 708 when (is_remote peer) $
565 liftIO $ sendMessage server_connections (OutBoundPresence p) peer 709 liftIO $ sendMessage server_connections (OutBoundPresence p) peer
566 loop 710 loop