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.
206 lines
7.0 KiB
206 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.Connection |
|
import Network.Xmpp.Pickle |
|
import Network.Xmpp.Types |
|
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 |
|
| IbrNoConnection |
|
| 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 IbrNoConnection |
|
-- 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 Connection -> 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 Connection |
|
-> 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 Connection -> 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
|
|
|