You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
7.0 KiB

-- | XEP 0077: In-Band Registration
-- http://xmpp.org/extensions/xep-0077.html
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.Xmpp.Xep.InbandRegistration where
import Control.Applicative((<$>))
import Control.Arrow(left)
import Control.Exception
import Control.Monad.Error
import Control.Monad.State
import Data.Either (partitionEithers)
import qualified Data.Text as Text
import Data.XML.Pickle
import qualified Data.XML.Types as XML
import Network.Xmpp.Internal
import Network.Xmpp.Xep.ServiceDiscovery
-- In-Band Registration name space
ibrns :: Text.Text
ibrns = "jabber:iq:register"
ibrName x = (XML.Name x (Just ibrns) Nothing)
data IbrError = IbrNotSupported
| IbrNoStream
| IbrIQError IQError
deriving (Show)
instance Error IbrError
data Query = Query { instructions :: Maybe Text.Text
, registered :: Bool
, remove :: Bool
, fields ::[(Field, Maybe Text.Text)]
} deriving Show
emptyQuery = Query Nothing False False []
-- supported :: XmppConMonad (Either IbrError Bool)
-- supported = runErrorT $ fromFeatures <+> fromDisco
-- where
-- fromFeatures = do
-- fs <- other <$> gets sFeatures
-- let fe = XML.Element
-- "{http://jabber.org/features/iq-register}register"
-- []
-- []
-- return $ fe `elem` fs
-- fromDisco = do
-- hn' <- gets sHostname
-- hn <- case hn' of
-- Just h -> return (Jid Nothing h Nothing)
-- Nothing -> throwError IbrNoStream
-- qi <- lift $ xmppQueryInfo Nothing Nothing
-- case qi of
-- Left e -> return False
-- Right qir -> return $ "jabber:iq:register" `elem` qiFeatures qir
-- f <+> g = do
-- r <- f
-- if r then return True else g
query :: IQRequestType -> Query -> TMVar Stream -> IO (Either IbrError Query)
query queryType x con = do
answer <- pushIQ "ibr" Nothing queryType Nothing (pickleElem xpQuery x) con
case answer of
Right IQResult{iqResultPayload = Just b} ->
case unpickleElem xpQuery b of
Right query -> return $ Right query
Left e -> throw . StreamXMLError $
"RequestField: unpickle failed, got "
++ Text.unpack (ppUnpickleError e)
++ " saw " ++ ppElement b
Right _ -> return $ Right emptyQuery -- TODO: That doesn't seem right
Left e -> return . Left $ IbrIQError e
data RegisterError = IbrError IbrError
| MissingFields [Field]
| AlreadyRegistered
deriving (Show)
instance Error RegisterError
mapError f = mapErrorT (liftM $ left f)
-- | Retrieve the necessary fields and fill them in to register an account with
-- the server
registerWith :: [(Field, Text.Text)]
-> TMVar Stream
-> IO (Either RegisterError Query)
registerWith givenFields con = runErrorT $ do
fs <- mapError IbrError . ErrorT $ requestFields con
when (registered fs) . throwError $ AlreadyRegistered
let res = flip map (fields fs) $ \(field,_) ->
case lookup field givenFields of
Just entry -> Right (field, Just entry)
Nothing -> Left field
fields <- case partitionEithers res of
([],fs) -> return fs
(fs,_) -> throwError $ MissingFields fs
result <- mapError IbrError . ErrorT $ query Set (emptyQuery {fields}) con
return result
-- | Terminate your account on the server. You have to be logged in for this to
-- work. You connection will most likely be terminated after unregistering.
unregister :: TMVar Stream -> IO (Either IbrError Query)
unregister = query Set $ emptyQuery {remove = True}
requestFields con = runErrorT $ do
-- supp <- ErrorT supported
-- unless supp $ throwError $ IbrNotSupported
qr <- ErrorT $ query Get emptyQuery con
return $ qr
xpQuery :: PU [XML.Node] Query
xpQuery = xpWrap
(\(is, r, u, fs) -> Query is r u fs)
(\(Query is r u fs) -> (is, r, u, fs)) $
xpElemNodes (ibrName "query") $
xp4Tuple
(xpOption $
xpElemNodes (ibrName "instructions") (xpContent $ xpText))
(xpElemExists (ibrName "registered"))
(xpElemExists (ibrName "remove"))
(xpAllByNamespace ibrns ( xpWrap
(\(name,_,c) -> (name, c))
(\(name,c) -> (name,(),c)) $
xpElemByNamespace ibrns xpPrim xpUnit
(xpOption $ xpContent xpText)
))
data Field = Username
| Nick
| Password
| Name
| First
| Last
| Email
| Address
| City
| State
| Zip
| Phone
| Url
| Date
| Misc
| Text
| Key
| OtherField Text.Text
deriving Eq
instance Show Field where
show Username = "username"
show Nick = "nick"
show Password = "password"
show Name = "name"
show First = "first"
show Last = "last"
show Email = "email"
show Address = "address"
show City = "city"
show State = "state"
show Zip = "zip"
show Phone = "phone"
show Url = "url"
show Date = "date"
show Misc = "misc"
show Text = "text"
show Key = "key"
show (OtherField x) = Text.unpack x
instance Read Field where
readsPrec _ "username" = [(Username , "")]
readsPrec _ "nick" = [(Nick , "")]
readsPrec _ "password" = [(Password , "")]
readsPrec _ "name" = [(Name , "")]
readsPrec _ "first" = [(First , "")]
readsPrec _ "last" = [(Last , "")]
readsPrec _ "email" = [(Email , "")]
readsPrec _ "address" = [(Address , "")]
readsPrec _ "city" = [(City , "")]
readsPrec _ "state" = [(State , "")]
readsPrec _ "zip" = [(Zip , "")]
readsPrec _ "phone" = [(Phone , "")]
readsPrec _ "url" = [(Url , "")]
readsPrec _ "date" = [(Date , "")]
readsPrec _ "misc" = [(Misc , "")]
readsPrec _ "text" = [(Text , "")]
readsPrec _ "key" = [(Key , "")]
readsPrec _ x = [(OtherField $ Text.pack x , "")]
-- Registered
-- Instructions
ppElement :: Element -> String
ppElement = Text.unpack . Text.decodeUtf8 . renderElement