2012-11-02 16:59:31 +00:00
|
|
|
{- core xmpp support
|
2012-10-26 18:44:36 +00:00
|
|
|
-
|
|
|
|
- Copyright 2012 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Assistant.XMPP where
|
|
|
|
|
2012-11-03 18:16:17 +00:00
|
|
|
import Assistant.Common
|
2012-11-03 20:00:38 +00:00
|
|
|
import Assistant.Types.NetMessager
|
2012-11-03 18:16:17 +00:00
|
|
|
import Assistant.Pairing
|
2012-10-26 18:44:36 +00:00
|
|
|
|
|
|
|
import Network.Protocol.XMPP
|
2012-11-05 19:40:56 +00:00
|
|
|
import Data.Text (Text)
|
2012-10-26 18:44:36 +00:00
|
|
|
import qualified Data.Text as T
|
|
|
|
import Data.XML.Types
|
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- Name of the git-annex tag, in our own XML namespace.
|
2012-10-26 18:44:36 +00:00
|
|
|
- (Not using a namespace URL to avoid unnecessary bloat.) -}
|
|
|
|
gitAnnexTagName :: Name
|
|
|
|
gitAnnexTagName = Name (T.pack "git-annex") (Just $ T.pack "git-annex") Nothing
|
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- Creates a git-annex tag containing a particular attribute and value. -}
|
|
|
|
gitAnnexTag :: Name -> Text -> Element
|
|
|
|
gitAnnexTag attr val = Element gitAnnexTagName [(attr, [ContentText val])] []
|
|
|
|
|
2012-11-03 18:16:17 +00:00
|
|
|
isGitAnnexTag :: Element -> Bool
|
|
|
|
isGitAnnexTag t = elementName t == gitAnnexTagName
|
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- Things that a git-annex tag can inserted into. -}
|
|
|
|
class GitAnnexTaggable a where
|
|
|
|
insertGitAnnexTag :: a -> Element -> a
|
2012-10-28 21:07:29 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
extractGitAnnexTag :: a -> Maybe Element
|
2012-11-03 18:16:17 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
hasGitAnnexTag :: a -> Bool
|
|
|
|
hasGitAnnexTag = isJust . extractGitAnnexTag
|
2012-10-26 18:44:36 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
instance GitAnnexTaggable Message where
|
2012-11-07 19:47:30 +00:00
|
|
|
insertGitAnnexTag m elt = m { messagePayloads = elt : messagePayloads m }
|
2012-11-05 19:40:56 +00:00
|
|
|
extractGitAnnexTag = headMaybe . filter isGitAnnexTag . messagePayloads
|
2012-11-04 01:19:59 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
instance GitAnnexTaggable Presence where
|
|
|
|
-- always mark extended away
|
2012-11-07 19:47:30 +00:00
|
|
|
insertGitAnnexTag p elt = p { presencePayloads = extendedAway : elt : presencePayloads p }
|
2012-11-05 19:40:56 +00:00
|
|
|
extractGitAnnexTag = headMaybe . filter isGitAnnexTag . presencePayloads
|
2012-11-03 18:16:17 +00:00
|
|
|
|
2012-11-07 19:47:30 +00:00
|
|
|
{- Gets the attr and its value value from a git-annex tag.
|
|
|
|
-
|
|
|
|
- Each git-annex tag has a single attribute. -}
|
2012-11-05 19:40:56 +00:00
|
|
|
getGitAnnexAttrValue :: GitAnnexTaggable a => a -> Maybe (Name, Text)
|
|
|
|
getGitAnnexAttrValue a = case extractGitAnnexTag a of
|
2012-11-07 19:47:30 +00:00
|
|
|
Just (tag@(Element _ [(attr, _)] _)) -> (,)
|
|
|
|
<$> pure attr
|
|
|
|
<*> attributeText attr tag
|
2012-11-05 19:40:56 +00:00
|
|
|
_ -> Nothing
|
2012-11-04 01:19:59 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- A presence with a git-annex tag in it. -}
|
|
|
|
gitAnnexPresence :: Element -> Presence
|
|
|
|
gitAnnexPresence = insertGitAnnexTag $ emptyPresence PresenceAvailable
|
2012-10-26 18:44:36 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- A presence with an empty git-annex tag in it, used for letting other
|
|
|
|
- clients know we're around and are a git-annex client. -}
|
|
|
|
gitAnnexSignature :: Presence
|
|
|
|
gitAnnexSignature = gitAnnexPresence $ Element gitAnnexTagName [] []
|
2012-10-26 18:44:36 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- A message with a git-annex tag in it. -}
|
|
|
|
gitAnnexMessage :: Element -> Message
|
|
|
|
gitAnnexMessage = insertGitAnnexTag silentMessage
|
2012-11-03 18:16:17 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- A notification that we've pushed to some repositories, listing their
|
|
|
|
- UUIDs. -}
|
2012-11-03 18:25:06 +00:00
|
|
|
pushNotification :: [UUID] -> Presence
|
2012-11-05 19:40:56 +00:00
|
|
|
pushNotification = gitAnnexPresence . gitAnnexTag pushAttr . encodePushNotification
|
|
|
|
|
|
|
|
pushAttr :: Name
|
|
|
|
pushAttr = Name (T.pack "push") Nothing Nothing
|
|
|
|
|
|
|
|
uuidSep :: T.Text
|
|
|
|
uuidSep = T.pack ","
|
|
|
|
|
|
|
|
encodePushNotification :: [UUID] -> Text
|
|
|
|
encodePushNotification = T.intercalate uuidSep . map (T.pack . fromUUID)
|
|
|
|
|
|
|
|
decodePushNotification :: Text -> [UUID]
|
|
|
|
decodePushNotification = map (toUUID . T.unpack) . T.splitOn uuidSep
|
2012-11-03 18:25:06 +00:00
|
|
|
|
2012-11-03 18:16:17 +00:00
|
|
|
{- A request for other git-annex clients to send presence. -}
|
|
|
|
presenceQuery :: Presence
|
2012-11-05 19:40:56 +00:00
|
|
|
presenceQuery = gitAnnexPresence $ gitAnnexTag queryAttr T.empty
|
|
|
|
|
|
|
|
queryAttr :: Name
|
|
|
|
queryAttr = Name (T.pack "query") Nothing Nothing
|
2012-11-03 18:16:17 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
{- A notification about a stage of pairing. -}
|
|
|
|
pairingNotification :: PairStage -> UUID -> JID -> JID -> Message
|
|
|
|
pairingNotification pairstage u tojid fromjid =
|
|
|
|
(gitAnnexMessage tag)
|
|
|
|
{ messageTo = Just tojid
|
|
|
|
, messageFrom = Just fromjid
|
|
|
|
}
|
|
|
|
where
|
|
|
|
tag = gitAnnexTag pairAttr $
|
|
|
|
encodePairingNotification pairstage u
|
2012-11-03 18:16:17 +00:00
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
pairAttr :: Name
|
|
|
|
pairAttr = Name (T.pack "pair") Nothing Nothing
|
|
|
|
|
|
|
|
encodePairingNotification :: PairStage -> UUID -> Text
|
|
|
|
encodePairingNotification pairstage u = T.unwords $ map T.pack
|
2012-11-04 02:52:41 +00:00
|
|
|
[ show pairstage
|
|
|
|
, fromUUID u
|
|
|
|
]
|
|
|
|
|
2012-11-05 19:40:56 +00:00
|
|
|
decodePairingNotification :: Text -> Message -> Maybe NetMessage
|
|
|
|
decodePairingNotification t msg = parse $ words $ T.unpack t
|
2012-11-03 20:00:38 +00:00
|
|
|
where
|
2012-11-04 02:52:41 +00:00
|
|
|
parse [stage, u] = PairingNotification
|
|
|
|
<$> readish stage
|
2012-11-05 19:40:56 +00:00
|
|
|
<*> (formatJID <$> messageFrom msg)
|
2012-11-04 02:52:41 +00:00
|
|
|
<*> pure (toUUID u)
|
2012-11-04 01:19:59 +00:00
|
|
|
parse _ = Nothing
|
2012-11-04 02:52:41 +00:00
|
|
|
|
|
|
|
{- The JID without the client part. -}
|
|
|
|
baseJID :: JID -> JID
|
|
|
|
baseJID j = JID (jidNode j) (jidDomain j) Nothing
|
2012-11-05 19:40:56 +00:00
|
|
|
|
|
|
|
{- An XMPP chat message with an empty body. This should not be displayed
|
|
|
|
- by clients, but can be used for communications. -}
|
|
|
|
silentMessage :: Message
|
|
|
|
silentMessage = (emptyMessage MessageChat)
|
|
|
|
{ messagePayloads = [ emptybody ] }
|
|
|
|
where
|
|
|
|
emptybody = Element
|
|
|
|
{ elementName = Name (T.pack "body") Nothing Nothing
|
|
|
|
, elementAttributes = []
|
|
|
|
, elementNodes = []
|
|
|
|
}
|
|
|
|
|
|
|
|
{- Add to a presence to mark its client as extended away. -}
|
|
|
|
extendedAway :: Element
|
|
|
|
extendedAway = Element (Name (T.pack "show") Nothing Nothing) []
|
|
|
|
[NodeContent $ ContentText $ T.pack "xa"]
|