|
|
|
@ -1,83 +1,39 @@ |
|
|
|
|
|
|
|
{-# OPTIONS_HADDOCK hide #-} |
|
|
|
{-# LANGUAGE PatternGuards #-} |
|
|
|
{-# LANGUAGE PatternGuards #-} |
|
|
|
{-# LANGUAGE NoMonomorphismRestriction #-} |
|
|
|
{-# LANGUAGE NoMonomorphismRestriction #-} |
|
|
|
{-# LANGUAGE OverloadedStrings #-} |
|
|
|
{-# LANGUAGE OverloadedStrings #-} |
|
|
|
|
|
|
|
|
|
|
|
module Network.Xmpp.IM.Roster |
|
|
|
module Network.Xmpp.IM.Roster where |
|
|
|
where |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Control.Concurrent.STM |
|
|
|
import Control.Concurrent.STM |
|
|
|
import Control.Monad |
|
|
|
import Control.Monad |
|
|
|
import Data.Text (Text) |
|
|
|
|
|
|
|
import Data.XML.Pickle |
|
|
|
|
|
|
|
import Data.XML.Types |
|
|
|
|
|
|
|
import Network.Xmpp |
|
|
|
|
|
|
|
import Network.Xmpp.Marshal |
|
|
|
|
|
|
|
import System.Log.Logger |
|
|
|
|
|
|
|
import qualified Data.Map.Strict as Map |
|
|
|
import qualified Data.Map.Strict as Map |
|
|
|
|
|
|
|
import Data.Maybe (isJust) |
|
|
|
import Network.Xmpp.Types |
|
|
|
import Data.XML.Pickle |
|
|
|
|
|
|
|
import Data.XML.Types |
|
|
|
data Subscription = None | To | From | Both | Remove deriving Eq |
|
|
|
import System.Log.Logger |
|
|
|
|
|
|
|
|
|
|
|
instance Show Subscription where |
|
|
|
import Network.Xmpp.IM.Roster.Types |
|
|
|
show None = "none" |
|
|
|
import Network.Xmpp.Marshal |
|
|
|
show To = "to" |
|
|
|
import Network.Xmpp.Concurrent.Types |
|
|
|
show From = "from" |
|
|
|
import Network.Xmpp.Types |
|
|
|
show Both = "both" |
|
|
|
import Network.Xmpp.Concurrent.IQ |
|
|
|
show Remove = "remove" |
|
|
|
|
|
|
|
|
|
|
|
getRoster :: Session -> IO Roster |
|
|
|
instance Read Subscription where |
|
|
|
getRoster session = atomically $ readTVar (rosterRef session) |
|
|
|
readsPrec _ "none" = [(None ,"")] |
|
|
|
|
|
|
|
readsPrec _ "to" = [(To ,"")] |
|
|
|
initRoster :: Session -> IO () |
|
|
|
readsPrec _ "from" = [(From ,"")] |
|
|
|
initRoster session = do |
|
|
|
readsPrec _ "both" = [(Both ,"")] |
|
|
|
oldRoster <- getRoster session |
|
|
|
readsPrec _ "remove" = [(Remove ,"")] |
|
|
|
mbRoster <- retrieveRoster (if isJust (ver oldRoster) then Just oldRoster |
|
|
|
readsPrec _ _ = [] |
|
|
|
else Nothing ) session |
|
|
|
|
|
|
|
case mbRoster of |
|
|
|
data Roster = Roster { ver :: Maybe Text |
|
|
|
Nothing -> errorM "Pontarius.Xmpp" |
|
|
|
, items :: Map.Map Jid Item } |
|
|
|
"Server did not return a roster" |
|
|
|
|
|
|
|
Just roster -> atomically $ writeTVar (rosterRef session) roster |
|
|
|
|
|
|
|
|
|
|
|
data Item = Item { approved :: Bool |
|
|
|
|
|
|
|
, ask :: Bool |
|
|
|
|
|
|
|
, jid :: Jid |
|
|
|
|
|
|
|
, name :: Maybe Text |
|
|
|
|
|
|
|
, subscription :: Subscription |
|
|
|
|
|
|
|
, groups :: [Text] |
|
|
|
|
|
|
|
} deriving Show |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data QueryItem = QueryItem { qiApproved :: Maybe Bool |
|
|
|
|
|
|
|
, qiAsk :: Bool |
|
|
|
|
|
|
|
, qiJid :: Jid |
|
|
|
|
|
|
|
, qiName :: Maybe Text |
|
|
|
|
|
|
|
, qiSubscription :: Maybe Subscription |
|
|
|
|
|
|
|
, qiGroups :: [Text] |
|
|
|
|
|
|
|
} deriving Show |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data Query = Query { queryVer :: Maybe Text |
|
|
|
|
|
|
|
, queryItems :: [QueryItem] |
|
|
|
|
|
|
|
} deriving Show |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
withRoster :: Maybe Roster |
|
|
|
|
|
|
|
-> SessionConfiguration |
|
|
|
|
|
|
|
-> (SessionConfiguration -> IO (Either XmppFailure Session)) |
|
|
|
|
|
|
|
-> IO (Either XmppFailure (TVar Roster, Session)) |
|
|
|
|
|
|
|
withRoster oldRoster conf startSession = do |
|
|
|
|
|
|
|
rosterRef <- newTVarIO $ Roster Nothing Map.empty |
|
|
|
|
|
|
|
mbSess <- startSession conf{extraStanzaHandlers = handleRoster rosterRef : |
|
|
|
|
|
|
|
extraStanzaHandlers conf} |
|
|
|
|
|
|
|
case mbSess of |
|
|
|
|
|
|
|
Left e -> return $ Left e |
|
|
|
|
|
|
|
Right sess -> do |
|
|
|
|
|
|
|
mbRoster <- getRoster oldRoster sess |
|
|
|
|
|
|
|
case mbRoster of |
|
|
|
|
|
|
|
Nothing -> errorM "Pontarius.Xmpp" "Server did not return a roster" |
|
|
|
|
|
|
|
Just roster -> atomically $ writeTVar rosterRef roster |
|
|
|
|
|
|
|
return $ Right (rosterRef, sess) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handleRoster :: TVar Roster -> TChan Stanza -> Stanza -> IO Bool |
|
|
|
handleRoster :: TVar Roster -> TChan Stanza -> Stanza -> IO Bool |
|
|
|
handleRoster rosterRef outC sta = do |
|
|
|
handleRoster ref outC sta = do |
|
|
|
case sta of |
|
|
|
case sta of |
|
|
|
IQRequestS (iqr@IQRequest{iqRequestPayload = |
|
|
|
IQRequestS (iqr@IQRequest{iqRequestPayload = |
|
|
|
iqb@Element{elementName = en}}) |
|
|
|
iqb@Element{elementName = en}}) |
|
|
|
@ -98,7 +54,7 @@ handleRoster rosterRef outC sta = do |
|
|
|
return False |
|
|
|
return False |
|
|
|
_ -> return True |
|
|
|
_ -> return True |
|
|
|
where |
|
|
|
where |
|
|
|
handleUpdate v' update = atomically $ modifyTVar rosterRef $ \(Roster v is) -> |
|
|
|
handleUpdate v' update = atomically $ modifyTVar ref $ \(Roster v is) -> |
|
|
|
Roster (v' `mplus` v) $ case qiSubscription update of |
|
|
|
Roster (v' `mplus` v) $ case qiSubscription update of |
|
|
|
Just Remove -> Map.delete (qiJid update) is |
|
|
|
Just Remove -> Map.delete (qiJid update) is |
|
|
|
_ -> Map.insert (qiJid update) (toItem update) is |
|
|
|
_ -> Map.insert (qiJid update) (toItem update) is |
|
|
|
@ -109,8 +65,8 @@ handleRoster rosterRef outC sta = do |
|
|
|
result (IQRequest iqid from _to lang _tp _bd) = |
|
|
|
result (IQRequest iqid from _to lang _tp _bd) = |
|
|
|
IQResultS $ IQResult iqid Nothing from lang Nothing |
|
|
|
IQResultS $ IQResult iqid Nothing from lang Nothing |
|
|
|
|
|
|
|
|
|
|
|
getRoster :: Maybe Roster -> Session -> IO (Maybe Roster) |
|
|
|
retrieveRoster :: Maybe Roster -> Session -> IO (Maybe Roster) |
|
|
|
getRoster oldRoster sess = do |
|
|
|
retrieveRoster oldRoster sess = do |
|
|
|
res <- sendIQ' Nothing Get Nothing |
|
|
|
res <- sendIQ' Nothing Get Nothing |
|
|
|
(pickleElem xpQuery (Query (ver =<< oldRoster) [])) |
|
|
|
(pickleElem xpQuery (Query (ver =<< oldRoster) [])) |
|
|
|
sess |
|
|
|
sess |
|
|
|
@ -120,7 +76,7 @@ getRoster oldRoster sess = do |
|
|
|
Left _e -> do |
|
|
|
Left _e -> do |
|
|
|
errorM "Pontarius.Xmpp.Roster" "getRoster: invalid query element" |
|
|
|
errorM "Pontarius.Xmpp.Roster" "getRoster: invalid query element" |
|
|
|
return Nothing |
|
|
|
return Nothing |
|
|
|
Right roster -> return . Just $ toRoster roster |
|
|
|
Right ros' -> return . Just $ toRoster ros' |
|
|
|
IQResponseResult (IQResult{iqResultPayload = Nothing}) -> do |
|
|
|
IQResponseResult (IQResult{iqResultPayload = Nothing}) -> do |
|
|
|
return $ oldRoster |
|
|
|
return $ oldRoster |
|
|
|
-- sever indicated that no roster updates are necessary |
|
|
|
-- sever indicated that no roster updates are necessary |
|
|
|
|