XMPP pairing notifications are now sent
Rest of pairing process still to do.
This commit is contained in:
parent
cbbfd4d00b
commit
b95c255b6d
6 changed files with 115 additions and 71 deletions
|
@ -33,7 +33,7 @@ xmppClientThread = NamedThread "XMPPClient" $ do
|
||||||
updateBuddyList (updateBuddies p) <<~ buddyList
|
updateBuddyList (updateBuddies p) <<~ buddyList
|
||||||
ioemptybuddies <- asIO $
|
ioemptybuddies <- asIO $
|
||||||
updateBuddyList (const noBuddies) <<~ buddyList
|
updateBuddyList (const noBuddies) <<~ buddyList
|
||||||
iorelay <- asIO relayNetMessage
|
iorelay <- asIO1 relayNetMessage
|
||||||
ioclientthread <- asIO $
|
ioclientthread <- asIO $
|
||||||
go iorelay iodebug iopull ioupdatebuddies ioemptybuddies
|
go iorelay iodebug iopull ioupdatebuddies ioemptybuddies
|
||||||
restartableClient ioclientthread
|
restartableClient ioclientthread
|
||||||
|
@ -45,6 +45,7 @@ xmppClientThread = NamedThread "XMPPClient" $ do
|
||||||
Just c -> liftIO $ loop c =<< getCurrentTime
|
Just c -> liftIO $ loop c =<< getCurrentTime
|
||||||
where
|
where
|
||||||
debug' = void . liftIO . iodebug
|
debug' = void . liftIO . iodebug
|
||||||
|
|
||||||
{- When the client exits, it's restarted;
|
{- When the client exits, it's restarted;
|
||||||
- if it keeps failing, back off to wait 5 minutes before
|
- if it keeps failing, back off to wait 5 minutes before
|
||||||
- trying it again. -}
|
- trying it again. -}
|
||||||
|
@ -59,6 +60,7 @@ xmppClientThread = NamedThread "XMPPClient" $ do
|
||||||
void $ iodebug ["connection failed; will retry"]
|
void $ iodebug ["connection failed; will retry"]
|
||||||
threadDelaySeconds (Seconds 300)
|
threadDelaySeconds (Seconds 300)
|
||||||
loop c =<< getCurrentTime
|
loop c =<< getCurrentTime
|
||||||
|
|
||||||
runclient c = void $ connectXMPP c $ \jid -> do
|
runclient c = void $ connectXMPP c $ \jid -> do
|
||||||
fulljid <- bindJID jid
|
fulljid <- bindJID jid
|
||||||
debug' ["connected", show fulljid]
|
debug' ["connected", show fulljid]
|
||||||
|
@ -69,47 +71,62 @@ xmppClientThread = NamedThread "XMPPClient" $ do
|
||||||
putStanza $ gitAnnexPresence gitAnnexSignature
|
putStanza $ gitAnnexPresence gitAnnexSignature
|
||||||
xmppThread $ receivenotifications fulljid
|
xmppThread $ receivenotifications fulljid
|
||||||
forever $ do
|
forever $ do
|
||||||
a <- liftIO iorelay
|
a <- liftIO $ iorelay fulljid
|
||||||
a
|
a
|
||||||
|
|
||||||
receivenotifications fulljid = forever $ do
|
receivenotifications fulljid = forever $ do
|
||||||
s <- getStanza
|
s <- getStanza
|
||||||
let v = decodeStanza fulljid s
|
let vs = decodeStanza fulljid s
|
||||||
debug' ["received:", show v]
|
debug' ["received:", show vs]
|
||||||
case v of
|
mapM_ handle vs
|
||||||
PresenceMessage p -> void $ liftIO $ ioupdatebuddies p
|
|
||||||
PresenceQuery p -> do
|
|
||||||
void $ liftIO $ ioupdatebuddies p
|
|
||||||
putStanza $ gitAnnexPresence gitAnnexSignature
|
|
||||||
PushNotification us -> void $ liftIO $ iopull us
|
|
||||||
Ignorable _ -> noop
|
|
||||||
Unknown _ -> noop
|
|
||||||
|
|
||||||
{- Waits for a NetMessager message to be sent, and relays it to XMPP. -}
|
handle (PresenceMessage p) =
|
||||||
relayNetMessage :: Assistant (XMPP ())
|
void $ liftIO $ ioupdatebuddies p
|
||||||
relayNetMessage = convert <$> waitNetMessage
|
handle (GotNetMessage QueryPresence) =
|
||||||
where
|
putStanza $ gitAnnexPresence gitAnnexSignature
|
||||||
convert (NotifyPush us) = putStanza $ pushNotification us
|
handle (GotNetMessage (NotifyPush us)) =
|
||||||
convert QueryPresence = putStanza presenceQuery
|
void $ liftIO $ iopull us
|
||||||
|
handle (GotNetMessage (PairingNotification stage t u)) = case parseJID t of
|
||||||
|
Nothing -> noop
|
||||||
|
Just jid -> error "TODO"
|
||||||
|
handle (Ignorable _) = noop
|
||||||
|
handle (Unknown _) = noop
|
||||||
|
|
||||||
data DecodedStanza
|
data XMPPEvent
|
||||||
= PresenceMessage Presence
|
= GotNetMessage NetMessage
|
||||||
| PresenceQuery Presence
|
| PresenceMessage Presence
|
||||||
| PushNotification [UUID]
|
|
||||||
| Ignorable Presence
|
| Ignorable Presence
|
||||||
| Unknown ReceivedStanza
|
| Unknown ReceivedStanza
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
decodeStanza :: JID -> ReceivedStanza -> DecodedStanza
|
{- Decodes an XMPP stanza into one or more events. -}
|
||||||
|
decodeStanza :: JID -> ReceivedStanza -> [XMPPEvent]
|
||||||
decodeStanza fulljid (ReceivedPresence p)
|
decodeStanza fulljid (ReceivedPresence p)
|
||||||
| presenceFrom p == Nothing = Ignorable p
|
| presenceFrom p == Nothing = [Ignorable p]
|
||||||
| presenceFrom p == Just fulljid = Ignorable p
|
| presenceFrom p == Just fulljid = [Ignorable p]
|
||||||
| isPresenceQuery p = PresenceQuery p
|
| not (null pushed) = impliedp $ GotNetMessage $ NotifyPush pushed
|
||||||
| null pushed = PresenceMessage p
|
| isPresenceQuery p = impliedp $ GotNetMessage QueryPresence
|
||||||
| otherwise = PushNotification pushed
|
| otherwise = [PresenceMessage p]
|
||||||
where
|
where
|
||||||
|
-- Some things are sent via presence, so imply a presence message,
|
||||||
|
-- along with their real value.
|
||||||
|
impliedp v = [PresenceMessage p, v]
|
||||||
pushed = concat $ catMaybes $ map decodePushNotification $
|
pushed = concat $ catMaybes $ map decodePushNotification $
|
||||||
presencePayloads p
|
presencePayloads p
|
||||||
decodeStanza _ s = Unknown s
|
decodeStanza _ s@(ReceivedIQ iq) = case decodePairingNotification iq of
|
||||||
|
Nothing -> [Unknown s]
|
||||||
|
Just pn -> [GotNetMessage pn]
|
||||||
|
decodeStanza _ s = [Unknown s]
|
||||||
|
|
||||||
|
{- Waits for a NetMessager message to be sent, and relays it to XMPP. -}
|
||||||
|
relayNetMessage :: JID -> Assistant (XMPP ())
|
||||||
|
relayNetMessage fulljid = convert <$> waitNetMessage
|
||||||
|
where
|
||||||
|
convert (NotifyPush us) = putStanza $ pushNotification us
|
||||||
|
convert QueryPresence = putStanza $ presenceQuery
|
||||||
|
convert (PairingNotification stage t u) = case parseJID t of
|
||||||
|
Nothing -> noop
|
||||||
|
Just tojid -> putStanza $ pairingNotification stage u tojid fulljid
|
||||||
|
|
||||||
{- Runs the client, handing restart events. -}
|
{- Runs the client, handing restart events. -}
|
||||||
restartableClient :: IO () -> Assistant ()
|
restartableClient :: IO () -> Assistant ()
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
module Assistant.Types.NetMessager where
|
module Assistant.Types.NetMessager where
|
||||||
|
|
||||||
import Common.Annex
|
import Common.Annex
|
||||||
|
import Assistant.Pairing
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Concurrent.MSampleVar
|
import Control.Concurrent.MSampleVar
|
||||||
|
|
||||||
|
@ -18,12 +20,11 @@ data NetMessage
|
||||||
= NotifyPush [UUID]
|
= NotifyPush [UUID]
|
||||||
-- requests other clients to inform us of their presence
|
-- requests other clients to inform us of their presence
|
||||||
| QueryPresence
|
| QueryPresence
|
||||||
|
-- notification about a stage in the pairing process,
|
||||||
|
-- involving another client identified by the Text, and a UUID.
|
||||||
|
| PairingNotification PairStage Text UUID
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
{- Controls for the XMPP client.
|
|
||||||
-
|
|
||||||
- It can be fed XMPP messages to send.
|
|
||||||
-
|
|
||||||
- It can also be sent a signal when it should restart for some reason. -}
|
|
||||||
data NetMessagerControl = NetMessagerControl
|
data NetMessagerControl = NetMessagerControl
|
||||||
{ netMessages :: TChan (NetMessage)
|
{ netMessages :: TChan (NetMessage)
|
||||||
, netMessagerRestart :: MSampleVar ()
|
, netMessagerRestart :: MSampleVar ()
|
||||||
|
|
|
@ -30,6 +30,7 @@ import Annex.UUID
|
||||||
#endif
|
#endif
|
||||||
#ifdef WITH_XMPP
|
#ifdef WITH_XMPP
|
||||||
import Assistant.XMPP.Client
|
import Assistant.XMPP.Client
|
||||||
|
import Assistant.XMPP.Buddies
|
||||||
import Network.Protocol.XMPP
|
import Network.Protocol.XMPP
|
||||||
import Assistant.Types.NetMessager
|
import Assistant.Types.NetMessager
|
||||||
import Assistant.NetMessager
|
import Assistant.NetMessager
|
||||||
|
@ -50,8 +51,8 @@ import Control.Concurrent
|
||||||
{- Starts either kind of pairing. -}
|
{- Starts either kind of pairing. -}
|
||||||
getStartPairR :: Handler RepHtml
|
getStartPairR :: Handler RepHtml
|
||||||
#ifdef WITH_XMPP
|
#ifdef WITH_XMPP
|
||||||
getStartPairR = pairPage $ do
|
getStartPairR = do
|
||||||
xmppconfigured <- lift $ isJust <$> runAnnex Nothing getXMPPCreds
|
xmppconfigured <- isJust <$> runAnnex Nothing getXMPPCreds
|
||||||
#ifdef WITH_PAIRING
|
#ifdef WITH_PAIRING
|
||||||
let localsupported = True
|
let localsupported = True
|
||||||
#else
|
#else
|
||||||
|
@ -59,8 +60,9 @@ getStartPairR = pairPage $ do
|
||||||
#endif
|
#endif
|
||||||
{- Ask buddies to send presence info, to get the buddy list
|
{- Ask buddies to send presence info, to get the buddy list
|
||||||
- populated. -}
|
- populated. -}
|
||||||
lift $ liftAssistant $ sendNetMessage QueryPresence
|
liftAssistant $ sendNetMessage QueryPresence
|
||||||
$(widgetFile "configurators/pairing/start")
|
pairPage $
|
||||||
|
$(widgetFile "configurators/pairing/start")
|
||||||
#else
|
#else
|
||||||
#ifdef WITH_PAIRING
|
#ifdef WITH_PAIRING
|
||||||
getStartPairR = redirect StartLocalPairR
|
getStartPairR = redirect StartLocalPairR
|
||||||
|
@ -69,12 +71,26 @@ getStartPairR = noPairing "local or jabber"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{- Starts pairing with an XMPP buddy. -}
|
{- Starts pairing with an XMPP buddy, or with other clients sharing an
|
||||||
|
- XMPP account. -}
|
||||||
getStartXMPPPairR :: BuddyID -> Handler RepHtml
|
getStartXMPPPairR :: BuddyID -> Handler RepHtml
|
||||||
#ifdef WITH_XMPP
|
#ifdef WITH_XMPP
|
||||||
getStartXMPPPairR (BuddyID bid) = case parseJID bid of
|
getStartXMPPPairR (BuddyID bid) = case parseJID bid of
|
||||||
Nothing -> error "bad JID"
|
Nothing -> error "bad JID"
|
||||||
Just jid -> error "TODO"
|
Just jid -> do
|
||||||
|
creds <- runAnnex Nothing getXMPPCreds
|
||||||
|
let ourjid = fromJust $ parseJID =<< xmppJID <$> creds
|
||||||
|
liftAssistant $ do
|
||||||
|
u <- liftAnnex getUUID
|
||||||
|
sendNetMessage $ PairingNotification
|
||||||
|
PairReq (formatJID jid) u
|
||||||
|
pairPage $ do
|
||||||
|
let samejid = equivjids jid ourjid
|
||||||
|
let account = formatJID jid
|
||||||
|
let name = buddyName jid
|
||||||
|
$(widgetFile "configurators/pairing/xmpp/inprogress")
|
||||||
|
where
|
||||||
|
equivjids a b = jidNode a == jidNode b && jidDomain a == jidDomain b
|
||||||
#else
|
#else
|
||||||
getStartXMPPPairR _ = noPairing "XMPP"
|
getStartXMPPPairR _ = noPairing "XMPP"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
module Assistant.XMPP where
|
module Assistant.XMPP where
|
||||||
|
|
||||||
import Assistant.Common
|
import Assistant.Common
|
||||||
import Annex.UUID
|
import Assistant.Types.NetMessager
|
||||||
import Assistant.Pairing
|
import Assistant.Pairing
|
||||||
|
|
||||||
import Network.Protocol.XMPP
|
import Network.Protocol.XMPP
|
||||||
|
@ -45,22 +45,9 @@ queryAttr = Name (T.pack "query") Nothing Nothing
|
||||||
pushAttr :: Name
|
pushAttr :: Name
|
||||||
pushAttr = Name (T.pack "push") Nothing Nothing
|
pushAttr = Name (T.pack "push") Nothing Nothing
|
||||||
|
|
||||||
pairingAttr :: Name
|
|
||||||
pairingAttr = Name (T.pack "pairing") Nothing Nothing
|
|
||||||
|
|
||||||
isAttr :: Name -> (Name, [Content]) -> Bool
|
isAttr :: Name -> (Name, [Content]) -> Bool
|
||||||
isAttr attr (k, _) = k == attr
|
isAttr attr (k, _) = k == attr
|
||||||
|
|
||||||
getAttr :: Name -> [(Name, [Content])] -> Maybe String
|
|
||||||
getAttr wantattr attrs = content <$> headMaybe (filter (isAttr wantattr) attrs)
|
|
||||||
where
|
|
||||||
content (_name, cs) = T.unpack $ T.concat $ map unpack cs
|
|
||||||
unpack (ContentText t) = t
|
|
||||||
unpack (ContentEntity t) = t
|
|
||||||
|
|
||||||
uuidAttr :: Name
|
|
||||||
uuidAttr = Name (T.pack "uuid") Nothing Nothing
|
|
||||||
|
|
||||||
uuidSep :: T.Text
|
uuidSep :: T.Text
|
||||||
uuidSep = T.pack ","
|
uuidSep = T.pack ","
|
||||||
|
|
||||||
|
@ -98,20 +85,25 @@ isPresenceQuery p = case filter isGitAnnexTag (presencePayloads p) of
|
||||||
[] -> False
|
[] -> False
|
||||||
((Element _name attrs _nodes):_) -> any (isAttr queryAttr) attrs
|
((Element _name attrs _nodes):_) -> any (isAttr queryAttr) attrs
|
||||||
|
|
||||||
{- A notification about a stage of pairing. -}
|
{- A notification about a stage of pairing. Sent as an XMPP ping.
|
||||||
pairingNotification :: PairStage -> Annex Presence
|
- The pairing info is sent using its id attribute. -}
|
||||||
pairingNotification pairstage = do
|
pairingNotification :: PairStage -> UUID -> JID -> JID -> IQ
|
||||||
u <- getUUID
|
pairingNotification pairstage u tojid fromjid = (emptyIQ IQGet)
|
||||||
return $ gitAnnexPresence $ Element gitAnnexTagName
|
{ iqTo = Just tojid
|
||||||
[ (pairingAttr, [ContentText $ T.pack $ show pairstage])
|
, iqFrom = Just fromjid
|
||||||
, (uuidAttr, [ContentText $ T.pack $ fromUUID u])
|
, iqID = Just $ T.unwords $ map T.pack
|
||||||
|
[ "git-annex"
|
||||||
|
, show pairstage
|
||||||
|
, fromUUID u
|
||||||
]
|
]
|
||||||
[]
|
}
|
||||||
|
|
||||||
isPairingNotification :: Presence -> Maybe (PairStage, UUID)
|
decodePairingNotification :: IQ -> Maybe NetMessage
|
||||||
isPairingNotification p = case filter isGitAnnexTag (presencePayloads p) of
|
decodePairingNotification iq = parseid =<< words . T.unpack <$> iqID iq
|
||||||
[] -> Nothing
|
where
|
||||||
((Element _name attrs _nodes):_) ->
|
parseid ["git-annex", stage, u] =
|
||||||
(,)
|
PairingNotification
|
||||||
<$> (readish =<< getAttr pairingAttr attrs)
|
<$> readish stage
|
||||||
<*> (toUUID <$> getAttr uuidAttr attrs)
|
<*> (formatJID <$> iqFrom iq)
|
||||||
|
<*> pure (toUUID u)
|
||||||
|
parseid _ = Nothing
|
||||||
|
|
|
@ -23,15 +23,17 @@ genBuddyID j = BuddyID $ formatJID j
|
||||||
genKey :: JID -> BuddyKey
|
genKey :: JID -> BuddyKey
|
||||||
genKey j = BuddyKey $ formatJID $ JID (jidNode j) (jidDomain j) Nothing
|
genKey j = BuddyKey $ formatJID $ JID (jidNode j) (jidDomain j) Nothing
|
||||||
|
|
||||||
|
buddyName :: JID -> Text
|
||||||
|
buddyName j = maybe (T.pack "") strNode (jidNode j)
|
||||||
|
|
||||||
{- Summary of info about a buddy.
|
{- Summary of info about a buddy.
|
||||||
-
|
-
|
||||||
- If the buddy has no clients at all anymore, returns Nothing. -}
|
- If the buddy has no clients at all anymore, returns Nothing. -}
|
||||||
buddySummary :: Buddy -> Maybe (Text, Bool, Bool, BuddyID)
|
buddySummary :: Buddy -> Maybe (Text, Bool, Bool, BuddyID)
|
||||||
buddySummary b = case clients of
|
buddySummary b = case clients of
|
||||||
((Client j):_) -> Just (buddyname j, away, canpair, genBuddyID j)
|
((Client j):_) -> Just (buddyName j, away, canpair, genBuddyID j)
|
||||||
[] -> Nothing
|
[] -> Nothing
|
||||||
where
|
where
|
||||||
buddyname j = maybe (T.pack "") strNode (jidNode j)
|
|
||||||
away = S.null (buddyPresent b) && S.null (buddyAssistants b)
|
away = S.null (buddyPresent b) && S.null (buddyAssistants b)
|
||||||
canpair = not $ S.null (buddyAssistants b)
|
canpair = not $ S.null (buddyAssistants b)
|
||||||
clients = S.toList $ buddyPresent b `S.union` buddyAway b `S.union` buddyAssistants b
|
clients = S.toList $ buddyPresent b `S.union` buddyAway b `S.union` buddyAssistants b
|
||||||
|
|
16
templates/configurators/pairing/xmpp/inprogress.hamlet
Normal file
16
templates/configurators/pairing/xmpp/inprogress.hamlet
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<div .span9 .hero-unit>
|
||||||
|
<h2>
|
||||||
|
Pairing in progress ..
|
||||||
|
$if samejid
|
||||||
|
<p>
|
||||||
|
A pair request has been sent to all other clients using your jabber #
|
||||||
|
account, #{account}.
|
||||||
|
<p>
|
||||||
|
You do not need to leave this page open; pairing will finish #
|
||||||
|
automatically once the other clients see the pair request.
|
||||||
|
$else
|
||||||
|
<p>
|
||||||
|
A pair request has been sent to #{name}.
|
||||||
|
<p>
|
||||||
|
You do not need to leave this page open; pairing will finish #
|
||||||
|
automatically once #{name} accepts the pair request.
|
Loading…
Add table
Add a link
Reference in a new issue