summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Presence/XMPP.hs92
-rw-r--r--Presence/XMPPTypes.hs9
-rw-r--r--Presence/main.hs83
3 files changed, 144 insertions, 40 deletions
diff --git a/Presence/XMPP.hs b/Presence/XMPP.hs
index 7d69e126..c2ae2fe2 100644
--- a/Presence/XMPP.hs
+++ b/Presence/XMPP.hs
@@ -457,6 +457,8 @@ fromClient session cmdChan = doNestingXML $ do
457 -> clientRequestsSubscription session cmdChan stanza 457 -> clientRequestsSubscription session cmdChan stanza
458 _ | stanza `isClientPresenceOf` presenceTypeSubscribed 458 _ | stanza `isClientPresenceOf` presenceTypeSubscribed
459 -> clientApprovesSubscription session stanza 459 -> clientApprovesSubscription session stanza
460 _ | stanza `isClientPresenceOf` presenceTypeUnsubscribed
461 -> clientRejectsSubscription session stanza
460 _ | otherwise -> unhandledStanza 462 _ | otherwise -> unhandledStanza
461 463
462 awaitCloser stanza_lvl 464 awaitCloser stanza_lvl
@@ -544,6 +546,29 @@ toClient session pchan cmdChan rchan = toClient' False False
544 [attr "subscription" subscription] 546 [attr "subscription" subscription]
545 send r 547 send r
546 loop 548 loop
549 RChan (RemovedBuddy who contact) -> do
550 liftIO . putStrLn $ "Roster push: RemovedBuddy "++show (isInterested,who,contact)
551 (jid,me) <- liftIO $ do
552 jid <- getJID session
553 me <- asHostNameJID jid
554 return (jid,me)
555 withJust me $ \me -> do
556 when (isInterested && Just who==name jid) $ do
557 send [ EventBeginElement "{jabber:client}presence"
558 [ attrbs "from" contact
559 , attrbs "to" me
560 , attr "type" "unsubscribed"
561 ]
562 , EventEndElement "{jabber:client}presence" ]
563 let f True = "from"
564 f False = "none"
565 subscription <- fmap f (liftIO $ isSubscribed session contact)
566 r <- liftIO . handleIO (\e -> putStrLn ("Roster RemovedBuddy error: "++show e) >> return []) $ do
567 rosterPush jid
568 (toStrict . L.decodeUtf8 $ contact)
569 [attr "subscription" subscription]
570 send r
571 loop
547 RChan (NewSubscriber who contact) -> do 572 RChan (NewSubscriber who contact) -> do
548 liftIO . putStrLn $ "Roster push: NewSubscriber "++show (isInterested,who,contact) 573 liftIO . putStrLn $ "Roster push: NewSubscriber "++show (isInterested,who,contact)
549 (jid,me) <- liftIO $ do 574 (jid,me) <- liftIO $ do
@@ -561,6 +586,23 @@ toClient session pchan cmdChan rchan = toClient' False False
561 [attr "subscription" subscription] 586 [attr "subscription" subscription]
562 send r 587 send r
563 loop 588 loop
589 RChan (RejectSubscriber who contact) -> do
590 liftIO . putStrLn $ "Roster push: RejectSubscriber "++show (isInterested,who,contact)
591 (jid,me) <- liftIO $ do
592 jid <- getJID session
593 me <- asHostNameJID jid
594 return (jid,me)
595 withJust me $ \me -> do
596 when (isInterested && Just who==name jid) $ do
597 let f True = "to"
598 f False = "none"
599 subscription <- fmap f (liftIO $ isBuddy session contact)
600 r <- liftIO . handleIO (\e -> putStrLn ("Roster RejectSubscriber error: "++show e) >> return []) $ do
601 rosterPush jid
602 (toStrict . L.decodeUtf8 $ contact)
603 [attr "subscription" subscription]
604 send r
605 loop
564 RChan (PendingSubscriber who contact) -> do 606 RChan (PendingSubscriber who contact) -> do
565 liftIO . putStrLn $ "Roster: Pending buddy "++show (isInterested,who,contact) 607 liftIO . putStrLn $ "Roster: Pending buddy "++show (isInterested,who,contact)
566 (jid,me) <- liftIO $ do 608 (jid,me) <- liftIO $ do
@@ -709,11 +751,12 @@ matchAttribMaybe name Nothing attrs
709 | otherwise 751 | otherwise
710 = False 752 = False
711 753
712presenceTypeOffline = Just "unavailable" 754presenceTypeOffline = Just "unavailable"
713presenceTypeOnline = Nothing 755presenceTypeOnline = Nothing
714presenceTypeProbe = Just "probe" 756presenceTypeProbe = Just "probe"
715presenceTypeSubscribe = Just "subscribe" 757presenceTypeSubscribe = Just "subscribe"
716presenceTypeSubscribed = Just "subscribed" 758presenceTypeSubscribed = Just "subscribed"
759presenceTypeUnsubscribed = Just "unsubscribed"
717 760
718isPresenceOf (EventBeginElement name attrs) testType 761isPresenceOf (EventBeginElement name attrs) testType
719 | name=="{jabber:server}presence" 762 | name=="{jabber:server}presence"
@@ -854,6 +897,11 @@ clientApprovesSubscription session stanza = do
854 withJust (lookupAttrib "to" (tagAttrs stanza)) $ \to_str -> do 897 withJust (lookupAttrib "to" (tagAttrs stanza)) $ \to_str -> do
855 liftIO $ approveSubscriber session (textToByteString to_str) 898 liftIO $ approveSubscriber session (textToByteString to_str)
856 899
900clientRejectsSubscription session stanza = do
901 liftIO $ putStrLn $ "CLIENT REJECTS SUBSCRIPTION"
902 withJust (lookupAttrib "to" (tagAttrs stanza)) $ \to_str -> do
903 liftIO $ rejectSubscriber session (textToByteString to_str)
904
857peerApprovesSubscription session stanza = do 905peerApprovesSubscription session stanza = do
858 liftIO $ putStrLn $ "PEER APPROVES SUBSCRIPTION" 906 liftIO $ putStrLn $ "PEER APPROVES SUBSCRIPTION"
859 whenJust (liftIO . handleIO (\e -> putStrLn ("peerApprovesSubscription: "++show e) >> return Nothing) 907 whenJust (liftIO . handleIO (\e -> putStrLn ("peerApprovesSubscription: "++show e) >> return Nothing)
@@ -861,6 +909,13 @@ peerApprovesSubscription session stanza = do
861 withJust (name tojid) $ \user -> do 909 withJust (name tojid) $ \user -> do
862 liftIO $ processApproval session user fromjid 910 liftIO $ processApproval session user fromjid
863 911
912peerRejectsSubscription session stanza = do
913 liftIO $ putStrLn $ "PEER REJECTS SUBSCRIPTION"
914 whenJust (liftIO . handleIO (\e -> putStrLn ("peerRejectsSubscription: "++show e) >> return Nothing)
915 $ stanzaFromTo session stanza) $ \(fromjid,tojid) -> do
916 withJust (name tojid) $ \user -> do
917 liftIO $ processRejection session user fromjid
918
864fromPeer :: (MonadThrow m,MonadIO m, JabberPeerSession session) => 919fromPeer :: (MonadThrow m,MonadIO m, JabberPeerSession session) =>
865 session -> Sink XML.Event m () 920 session -> Sink XML.Event m ()
866fromPeer session = doNestingXML $ do 921fromPeer session = doNestingXML $ do
@@ -891,6 +946,8 @@ fromPeer session = doNestingXML $ do
891 -> peerRequestsSubsription session stanza 946 -> peerRequestsSubsription session stanza
892 _ | stanza `isPresenceOf` presenceTypeSubscribed 947 _ | stanza `isPresenceOf` presenceTypeSubscribed
893 -> peerApprovesSubscription session stanza 948 -> peerApprovesSubscription session stanza
949 _ | stanza `isPresenceOf` presenceTypeUnsubscribed
950 -> peerRejectsSubscription session stanza
894 _ -> unhandledStanza 951 _ -> unhandledStanza
895 952
896 awaitCloser stanza_lvl 953 awaitCloser stanza_lvl
@@ -908,7 +965,7 @@ newServerConnections = newTVar Map.empty
908data CachedMessages = CachedMessages 965data CachedMessages = CachedMessages
909 { presences :: Map JID JabberShow 966 { presences :: Map JID JabberShow
910 , probes :: Map JID (Set (Bool,JID)) -- False means solicitation rather than probe 967 , probes :: Map JID (Set (Bool,JID)) -- False means solicitation rather than probe
911 , approvals :: Map JID (Set JID) 968 , approvals :: Map JID (Set (Bool,JID) ) -- False means rejection rather than approval
912 } 969 }
913newCache = CachedMessages Map.empty Map.empty Map.empty 970newCache = CachedMessages Map.empty Map.empty Map.empty
914 971
@@ -938,7 +995,11 @@ connect_to_server chan peer = (>> return ()) . runMaybeT $ do
938 writeIORef cached (cache { probes=probes' }) 995 writeIORef cached (cache { probes=probes' })
939 cacheCmd (Approval from to) cached = do 996 cacheCmd (Approval from to) cached = do
940 cache <- readIORef cached 997 cache <- readIORef cached
941 let approvals' = mmInsert from to $ approvals cache 998 let approvals' = mmInsert (True,from) to $ approvals cache
999 writeIORef cached (cache { approvals=approvals' })
1000 cacheCmd (Rejection from to) cached = do
1001 cache <- readIORef cached
1002 let approvals' = mmInsert (False,from) to $ approvals cache
942 writeIORef cached (cache { approvals=approvals' }) 1003 writeIORef cached (cache { approvals=approvals' })
943 1004
944 fix $ \sendmsgs -> do 1005 fix $ \sendmsgs -> do
@@ -1038,16 +1099,18 @@ toPeer sock cache chan fail = do
1038 sendSolicitation from to = 1099 sendSolicitation from to =
1039 sendOrFail (presenceStanza sock from to "subscribe") 1100 sendOrFail (presenceStanza sock from to "subscribe")
1040 (Solicitation from to) 1101 (Solicitation from to)
1041 sendApproval from to = 1102 sendApproval approve from to =
1042 sendOrFail (presenceStanza sock from to "subscribed") 1103 sendOrFail (presenceStanza sock from to
1043 (Approval from to) 1104 (if approve then "subscribed" else "unsubscribed"))
1105 (if approve then Approval from to
1106 else Rejection from to)
1044 1107
1045 1108
1046 send greetPeer 1109 send greetPeer
1047 forM_ (Map.assocs . approvals $ cache) $ \(to,froms) -> do 1110 forM_ (Map.assocs . approvals $ cache) $ \(to,froms) -> do
1048 forM_ (Set.toList froms) $ \from -> do 1111 forM_ (Set.toList froms) $ \(approve,from) -> do
1049 liftIO $ L.putStrLn "sending cached approval..." 1112 liftIO $ L.putStrLn "sending cached approval..."
1050 sendApproval from to 1113 sendApproval approve from to
1051 forM_ (Map.assocs . presences $ cache) $ \(jid,st) -> do 1114 forM_ (Map.assocs . presences $ cache) $ \(jid,st) -> do
1052 sendPresence (Presence jid st) 1115 sendPresence (Presence jid st)
1053 forM_ (Map.assocs . probes $ cache) $ \(to,froms) -> do 1116 forM_ (Map.assocs . probes $ cache) $ \(to,froms) -> do
@@ -1073,7 +1136,10 @@ toPeer sock cache chan fail = do
1073 sendSolicitation from to 1136 sendSolicitation from to
1074 Approval from to -> do 1137 Approval from to -> do
1075 liftIO . L.putStrLn $ "sending approval "<++>bshow (from,to) 1138 liftIO . L.putStrLn $ "sending approval "<++>bshow (from,to)
1076 sendApproval from to 1139 sendApproval True from to
1140 Rejection from to -> do
1141 liftIO . L.putStrLn $ "sending rejection "<++>bshow (from,to)
1142 sendApproval False from to
1077 loop 1143 loop
1078 send goodbyePeer 1144 send goodbyePeer
1079 1145
diff --git a/Presence/XMPPTypes.hs b/Presence/XMPPTypes.hs
index 0e98a77b..4507f5bf 100644
--- a/Presence/XMPPTypes.hs
+++ b/Presence/XMPPTypes.hs
@@ -59,6 +59,7 @@ class JabberClientSession session where
59 isSubscribed :: session -> ByteString -> IO Bool 59 isSubscribed :: session -> ByteString -> IO Bool
60 isBuddy :: session -> ByteString -> IO Bool 60 isBuddy :: session -> ByteString -> IO Bool
61 approveSubscriber :: session -> ByteString -> IO () 61 approveSubscriber :: session -> ByteString -> IO ()
62 rejectSubscriber :: session -> ByteString -> IO ()
62 63
63class JabberPeerSession session where 64class JabberPeerSession session where
64 data XMPPPeerClass session 65 data XMPPPeerClass session
@@ -72,6 +73,7 @@ class JabberPeerSession session where
72 getBuddies :: XMPPPeerClass session -> ByteString -> IO [ByteString] 73 getBuddies :: XMPPPeerClass session -> ByteString -> IO [ByteString]
73 getSubscribers :: XMPPPeerClass session -> ByteString -> IO [ByteString] 74 getSubscribers :: XMPPPeerClass session -> ByteString -> IO [ByteString]
74 processApproval :: session -> ByteString -> JID -> IO () 75 processApproval :: session -> ByteString -> JID -> IO ()
76 processRejection :: session -> ByteString -> JID -> IO ()
75 processRequest :: session -> ByteString -> JID -> IO () 77 processRequest :: session -> ByteString -> JID -> IO ()
76 78
77-- | Jabber ID (JID) datatype 79-- | Jabber ID (JID) datatype
@@ -95,12 +97,18 @@ data RosterEvent = RequestedSubscription
95 | NewBuddy 97 | NewBuddy
96 {- user: -} ByteString 98 {- user: -} ByteString
97 {- contact: -} ByteString 99 {- contact: -} ByteString
100 | RemovedBuddy
101 {- user: -} ByteString
102 {- contact: -} ByteString
98 | PendingSubscriber 103 | PendingSubscriber
99 {- user: -} ByteString 104 {- user: -} ByteString
100 {- contact: -} ByteString 105 {- contact: -} ByteString
101 | NewSubscriber 106 | NewSubscriber
102 {- user: -} ByteString 107 {- user: -} ByteString
103 {- contact: -} ByteString 108 {- contact: -} ByteString
109 | RejectSubscriber
110 {- user: -} ByteString
111 {- contact: -} ByteString
104 deriving Prelude.Show 112 deriving Prelude.Show
105 113
106data Peer = LocalHost | RemotePeer SockAddr 114data Peer = LocalHost | RemotePeer SockAddr
@@ -220,6 +228,7 @@ data OutBoundMessage = OutBoundPresence Presence
220 | PresenceProbe JID JID 228 | PresenceProbe JID JID
221 | Solicitation JID JID 229 | Solicitation JID JID
222 | Approval JID JID 230 | Approval JID JID
231 | Rejection JID JID
223 deriving Prelude.Show 232 deriving Prelude.Show
224 233
225getNamesForPeer :: Peer -> IO [S.ByteString] 234getNamesForPeer :: Peer -> IO [S.ByteString]
diff --git a/Presence/main.hs b/Presence/main.hs
index 702b98d5..781b87fa 100644
--- a/Presence/main.hs
+++ b/Presence/main.hs
@@ -87,6 +87,14 @@ data PresenceState = PresenceState
87 } 87 }
88 88
89 89
90rosterPush msg state = do
91 let rchan = rosterChannel state
92 atomically $ do
93 isempty <- isEmptyTMVar rchan
94 when (not isempty) $ do
95 (_,ch) <- readTMVar rchan
96 writeTChan ch msg
97
90getJabberUserForId muid = 98getJabberUserForId muid =
91 maybe (return "nobody") 99 maybe (return "nobody")
92 (\(uid,_) -> 100 (\(uid,_) ->
@@ -157,7 +165,7 @@ instance JabberClientSession ClientSession where
157 165
158 rsc <- readIORef (unix_resource s) 166 rsc <- readIORef (unix_resource s)
159 -- let jid = user <++> "@" <++> host <++?> "/" <++$> rsc 167 -- let jid = user <++> "@" <++> host <++?> "/" <++$> rsc
160 L.putStrLn $ "CLIENT SESSION: jid " <++> L.show (JID (Just user) host rsc) 168 -- L.putStrLn $ "CLIENT SESSION: jid " <++> L.show (JID (Just user) host rsc)
161 return (JID (Just user) host rsc) 169 return (JID (Just user) host rsc)
162 170
163 closeSession session = do 171 closeSession session = do
@@ -228,12 +236,9 @@ instance JabberClientSession ClientSession where
228 me <- getJID s 236 me <- getJID s
229 withJust (name me) $ \user -> do 237 withJust (name me) $ \user -> do
230 addRawJid ConfigFiles.modifySolicited user jid_str 238 addRawJid ConfigFiles.modifySolicited user jid_str
231 let rchan = rosterChannel . presence_state $ s 239
232 atomically $ do 240 rosterPush (RequestedSubscription user jid_str) (presence_state s)
233 isempty <- isEmptyTMVar rchan 241
234 when (not isempty) $ do
235 (_,ch) <- readTMVar rchan
236 writeTChan ch (RequestedSubscription user jid_str)
237 sendMessage (outGoingConnections . presence_state $ s) 242 sendMessage (outGoingConnections . presence_state $ s)
238 (Solicitation me jid) 243 (Solicitation me jid)
239 (peer jid) 244 (peer jid)
@@ -278,17 +283,14 @@ instance JabberClientSession ClientSession where
278 user <- readIORef (unix_uid s) >>= getJabberUserForId 283 user <- readIORef (unix_uid s) >>= getJabberUserForId
279 cjid <- parseHostNameJID contact 284 cjid <- parseHostNameJID contact
280 let rmjid modify user buddy = modify user (cmpJID buddy) Nothing 285 let rmjid modify user buddy = modify user (cmpJID buddy) Nothing
286
287 -- update config files
281 addRawJid ConfigFiles.modifySubscribers user contact -- add subscribers 288 addRawJid ConfigFiles.modifySubscribers user contact -- add subscribers
282 rmjid ConfigFiles.modifyPending user cjid 289 rmjid ConfigFiles.modifyPending user cjid
283 rmjid ConfigFiles.modifyOthers user cjid 290 rmjid ConfigFiles.modifyOthers user cjid
284 291
285 -- roster push 292 -- roster push
286 let rchan = rosterChannel . presence_state $ s 293 rosterPush (NewSubscriber user contact) (presence_state s)
287 atomically $ do
288 isempty <- isEmptyTMVar rchan
289 when (not isempty) $ do
290 (_,ch) <- readTMVar rchan
291 writeTChan ch (NewSubscriber user contact)
292 294
293 -- notify peer 295 -- notify peer
294 sendMessage (outGoingConnections . presence_state $ s) 296 sendMessage (outGoingConnections . presence_state $ s)
@@ -300,6 +302,33 @@ instance JabberClientSession ClientSession where
300 forM_ presence $ \p -> sendMessage cons (OutBoundPresence p) (peer cjid) 302 forM_ presence $ \p -> sendMessage cons (OutBoundPresence p) (peer cjid)
301 return () 303 return ()
302 304
305 rejectSubscriber s contact = do
306 user <- readIORef (unix_uid s) >>= getJabberUserForId
307 cjid <- parseHostNameJID contact
308 let rmjid modify user buddy = modify user (cmpJID buddy) Nothing
309
310 -- update config files
311 was_pending <- rmjid ConfigFiles.modifyPending user cjid
312 was_subscribed <- rmjid ConfigFiles.modifySubscribers user cjid
313 addRawJid ConfigFiles.modifyOthers user contact
314
315 -- roster push
316 rosterPush (RejectSubscriber user contact) (presence_state s)
317
318 -- notify peer
319 when (was_pending || was_subscribed) $ do
320 let cons = outGoingConnections . presence_state $ s
321 isonline (Presence _ Offline) = False
322 isonline _ = True
323 presence <- fmap (filter isonline) $ getUserStatus (presence_state s) user
324 me <- getJID s
325 when (not (null presence)) $
326 sendMessage cons (OutBoundPresence . Presence me $ Offline) (peer cjid)
327 sendMessage (outGoingConnections . presence_state $ s)
328 (Rejection (JID (Just user) LocalHost Nothing)
329 cjid)
330 (peer cjid)
331 return ()
303 332
304tupleToJID (user,tty,pid) = jid user LocalHost tty 333tupleToJID (user,tty,pid) = jid user LocalHost tty
305 334
@@ -375,19 +404,24 @@ instance JabberPeerSession PeerSession where
375 solicited <- ConfigFiles.getSolicited user 404 solicited <- ConfigFiles.getSolicited user
376 let rmjid modify user buddy = modify user (cmpJID buddy) Nothing 405 let rmjid modify user buddy = modify user (cmpJID buddy) Nothing
377 was_sol <- rmjid ConfigFiles.modifySolicited user buddy 406 was_sol <- rmjid ConfigFiles.modifySolicited user buddy
378 putStrLn $ "was_sol = "<++>bshow was_sol
379 when was_sol $ do -- if buddy ∈ solicited: 407 when was_sol $ do -- if buddy ∈ solicited:
380 addJid ConfigFiles.modifyBuddies user buddy -- add buddies 408 addJid ConfigFiles.modifyBuddies user buddy -- add buddies
381 rmjid ConfigFiles.modifyOthers user buddy -- remove others 409 rmjid ConfigFiles.modifyOthers user buddy -- remove others
382 let rchan = rosterChannel . peer_global $ session
383 mbuddy <- asHostNameJID buddy 410 mbuddy <- asHostNameJID buddy
384 withJust mbuddy $ \buddy -> do 411 withJust mbuddy $ \buddy -> do
385 atomically $ do 412 rosterPush (NewBuddy user buddy) (peer_global session)
386 isempty <- isEmptyTMVar rchan 413
387 when (not isempty) $ do 414 processRejection session user buddy = do
388 (_,ch) <- readTMVar rchan 415 solicited <- ConfigFiles.getSolicited user
389 writeTChan ch (NewBuddy user buddy) 416 let rmjid modify user buddy = modify user (cmpJID buddy) Nothing
390 return () 417 was_sol <- rmjid ConfigFiles.modifySolicited user buddy
418 when was_sol $ do -- if buddy ∈ solicited:
419 rmjid ConfigFiles.modifyBuddies user buddy -- remove buddies
420 addJid ConfigFiles.modifyOthers user buddy -- add others
421 mbuddy <- asHostNameJID buddy
422 withJust mbuddy $ \buddy -> do
423 rosterPush (RemovedBuddy user buddy) (peer_global session)
424
391 processRequest session user buddy = do 425 processRequest session user buddy = do
392 let addjid modify user buddy = do 426 let addjid modify user buddy = do
393 hbuddy <- asHostNameJID buddy 427 hbuddy <- asHostNameJID buddy
@@ -403,14 +437,9 @@ instance JabberPeerSession PeerSession where
403 -- contact ∉ subscribers & contact ∈ pending --> SHOULD NOT 437 -- contact ∉ subscribers & contact ∈ pending --> SHOULD NOT
404 -- contact ∉ subscribers & contact ∉ pending --> MUST 438 -- contact ∉ subscribers & contact ∉ pending --> MUST
405 when (not was_pending) $ do 439 when (not was_pending) $ do
406 let rchan = rosterChannel . peer_global $ session
407 mbuddy <- asHostNameJID buddy 440 mbuddy <- asHostNameJID buddy
408 withJust mbuddy $ \buddy -> do 441 withJust mbuddy $ \buddy -> do
409 atomically $ do 442 rosterPush (PendingSubscriber user buddy) (peer_global session)
410 isempty <- isEmptyTMVar rchan
411 when (not isempty) $ do
412 (_,ch) <- readTMVar rchan
413 writeTChan ch (PendingSubscriber user buddy)
414 443
415 444
416data RefCountedChan = forall a. RefCountedChan (TMVar (RefCount,TChan a)) 445data RefCountedChan = forall a. RefCountedChan (TMVar (RefCount,TChan a))