Browse Source

switched to hexpat-internals

master
Philipp Balzarek 14 years ago
parent
commit
f54b50d609
  1. 3
      .gitignore
  2. 141
      src/Data/Conduit/Hexpat.hs
  3. 2
      src/Data/Conduit/TLS.hs
  4. 5
      src/Main.hs
  5. 1
      src/Network/XMPP.hs
  6. 21
      src/Network/XMPP/Marshal.hs
  7. 62
      src/Network/XMPP/Monad.hs
  8. 2
      src/Network/XMPP/SASL.hs
  9. 34
      src/Network/XMPP/Stream.hs
  10. 1
      xmpp-lib.cabal

3
.gitignore vendored

@ -1,7 +1,8 @@
cabal-dev/
dist/ dist/
cabal-dev/
*.o *.o
*.hi *.hi
*~ *~
*# *#
*.#* *.#*
*_flymake.hs

141
src/Data/Conduit/Hexpat.hs

@ -0,0 +1,141 @@
{-# LANGUAGE DeriveDataTypeable, NoMonomorphismRestriction #-}
module Data.Conduit.Hexpat
( ParseOptions(..)
, defaultParseOptions
, parseBS
)
where
import Control.Applicative((<$>))
import Control.Exception
import Control.Monad
import Control.Monad.Trans.Class
import qualified Data.ByteString as BS
import Data.Conduit as C
import qualified Data.Text as Text
import qualified Data.Text.Encoding as TE
import Data.Text(Text)
import Data.Typeable
import Data.XML.Types as XML
import Text.XML.Expat.Internal.IO hiding (parse)
import Data.IORef
-- adapted from parseG
-- | Parse a generalized list of ByteStrings containing XML to SAX events.
-- In the event of an error, FailDocument is the last element of the output list.
data HexpatParser = HexpatParser
{ hParser :: Parser
, hQueueRef :: IORef [XML.Event]
}
splitName :: Text -> Name
splitName name = case Text.split (=='}') name of
[n] -> case Text.split (==':') n of
[n'] -> Name n' Nothing Nothing
[p,n'] -> Name n' Nothing (Just p)
_ -> throw . HexpatParseException
$ "Error parsing name: " ++ show name
[ns,n] -> Name n (Just ns) Nothing
_ -> throw . HexpatParseException
$ "Error parsing name: " ++ show name
createParser :: ParseOptions -> Maybe Char -> IO (HexpatParser)
createParser opts delim = do
let enc = overrideEncoding opts
-- let mEntityDecoder = entityDecoder opts
parser <- newParser enc delim
queueRef <- newIORef []
-- setXMLDeclarationHandler parser $ \_ cVer cEnc cSd -> do
-- ver <- textFromCString cVer
-- mEnc <- if cEnc == nullPtr
-- then return Nothing
-- else Just <$> textFromCString cEnc
-- let sd = if cSd < 0
-- then Nothing
-- else Just $ if cSd /= 0 then True else False
-- modifyIORef queueRef (XMLDeclaration ver mEnc sd:)
-- TODO: What to do here?
-- return True
setStartElementHandler parser $ \_ cName cAttrs -> do
name <- splitName <$> textFromCString cName
attrs <- forM cAttrs $ \(cAttrName,cAttrValue) -> do
attrName <- splitName <$> textFromCString cAttrName
attrValue <- ContentText <$> textFromCString cAttrValue
return (attrName, [attrValue])
modifyIORef queueRef (EventBeginElement name attrs:)
return True
setEndElementHandler parser $ \_ cName -> do
name <- splitName <$> textFromCString cName
modifyIORef queueRef (EventEndElement name:)
return True
setCharacterDataHandler parser $ \_ cText -> do
txt <- TE.decodeUtf8 <$> BS.packCStringLen cText
modifyIORef queueRef ((EventContent $ ContentText txt):)
return True
setProcessingInstructionHandler parser $ \_ cTarget cText -> do
target <- textFromCString cTarget
txt <- textFromCString cText
modifyIORef queueRef (EventInstruction (Instruction target txt) :)
return True
setCommentHandler parser $ \_ cText -> do
txt <- textFromCString cText
modifyIORef queueRef (EventComment txt :)
return True
return (HexpatParser parser queueRef)
data HexpatParseException = HexpatParseException String deriving (Typeable, Show)
instance Exception HexpatParseException
parseBS
:: (MonadResource (t IO), MonadTrans t) =>
ParseOptions -> Conduit BS.ByteString (t IO) Event
parseBS opts = conduitIO
(createParser opts (Just '}'))
(\_ -> return ())
(\(HexpatParser parser queueRef) input -> lift $ do
e <- withParser parser $ \pp -> parseChunk pp input False
case e of
Nothing -> return ()
Just (XMLParseError err _) ->
throwIO $ HexpatParseException err
queue <- readIORef queueRef
writeIORef queueRef []
return . IOProducing $ reverse queue
)
(\(HexpatParser parser queueRef) -> lift $ do
e <- withParser parser $ \pp -> parseChunk pp BS.empty True
case e of
Nothing -> return ()
Just (XMLParseError err _) ->
throwIO $ HexpatParseException err
queue <- readIORef queueRef
writeIORef queueRef []
return $ reverse queue
)
whileJust :: Monad m => m (Maybe a) -> m [a]
whileJust f = do
f' <- f
case f' of
Just x -> liftM (x :) $ whileJust f
Nothing -> return []
data StreamUnfinishedException = StreamUnfinishedException deriving (Typeable, Show)
instance Exception StreamUnfinishedException

2
src/Data/Conduit/TLS.hs

@ -46,7 +46,7 @@ tlsinit tlsParams handle = do
(\con bs -> sendData clientContext (BL.fromChunks [bs]) (\con bs -> sendData clientContext (BL.fromChunks [bs])
>> return IOProcessing ) >> return IOProcessing )
(\_ -> return ()) (\_ -> return ())
return ( src $= conduitStdout return ( src
, snk , snk
, \s -> sendData clientContext $ BL.fromChunks [s] ) , \s -> sendData clientContext $ BL.fromChunks [s] )

5
src/Main.hs

@ -53,7 +53,8 @@ main = do
connectXMPP "localhost" "species64739.dyndns.org" "bot" (Just "botsi") "pwd" connectXMPP "localhost" "species64739.dyndns.org" "bot" (Just "botsi") "pwd"
$ do $ do
liftIO $ putStrLn "----------------------------" liftIO $ putStrLn "----------------------------"
-- sendS . SPresence $
-- sendS . SPresence $
-- Presence Nothing Nothing Nothing Nothing (Just Available) Nothing Nothing [] -- Presence Nothing Nothing Nothing Nothing (Just Available) Nothing Nothing []
withNewThread autoAccept withNewThread autoAccept
withNewThread mirror withNewThread mirror
@ -64,7 +65,7 @@ main = do
sendS . SMessage $ Message Nothing philonous Nothing Nothing Nothing sendS . SMessage $ Message Nothing philonous Nothing Nothing Nothing
(Just "bla") Nothing [] (Just "bla") Nothing []
forever $ pullMessage >>= liftIO . print -- forever $ pullMessage >>= liftIO . print
-- withNewThread . void $ (liftIO $ threadDelay 15000000) >> killConnection -- withNewThread . void $ (liftIO $ threadDelay 15000000) >> killConnection
-- forever $ do -- forever $ do

1
src/Network/XMPP.hs

@ -26,7 +26,6 @@ fromHandle :: Handle -> Text -> Text -> Maybe Text -> Text -> XMPPThread a
-> IO ((), XMPPState) -> IO ((), XMPPState)
fromHandle handle hostname username resource password a = fromHandle handle hostname username resource password a =
xmppFromHandle handle hostname username resource $ do xmppFromHandle handle hostname username resource $ do
liftIO $ putStrLn "start stream"
xmppStartStream xmppStartStream
-- this will check whether the server supports tls -- this will check whether the server supports tls
-- on it's own -- on it's own

21
src/Network/XMPP/Marshal.hs

@ -33,7 +33,7 @@ messageP = xpWrap (\((from, to, id, tp),(sub, body, thr,ext))
(\(Message from to id tp sub body thr ext) (\(Message from to id tp sub body thr ext)
-> ((from, to, id, tp), (sub, body, thr,ext))) -> ((from, to, id, tp), (sub, body, thr,ext)))
$ $
xpElem "message" xpElem "{jabber:client}message"
(xp4Tuple (xp4Tuple
(xpAttrImplied "from" xpPrim) (xpAttrImplied "from" xpPrim)
(xpAttr "to" xpPrim) (xpAttr "to" xpPrim)
@ -41,9 +41,9 @@ messageP = xpWrap (\((from, to, id, tp),(sub, body, thr,ext))
(xpAttrImplied "type" xpPrim) (xpAttrImplied "type" xpPrim)
) )
(xp4Tuple (xp4Tuple
(xpOption . xpElemNodes "subject" $ xpContent xpId) (xpOption . xpElemNodes "{jabber:client}subject" $ xpContent xpId)
(xpOption . xpElemNodes "body" $ xpContent xpId) (xpOption . xpElemNodes "{jabber:client}body" $ xpContent xpId)
(xpOption . xpElemNodes "thread" $ xpContent xpId) (xpOption . xpElemNodes "{jabber:client}thread" $ xpContent xpId)
(xpAll xpElemVerbatim) (xpAll xpElemVerbatim)
) )
@ -53,7 +53,7 @@ presenceP = xpWrap (\((from, to, id, tp),(shw, stat, prio, ext))
(\(Presence from to id tp shw stat prio ext) (\(Presence from to id tp shw stat prio ext)
-> ((from, to, id, tp), (shw, stat, prio, ext))) -> ((from, to, id, tp), (shw, stat, prio, ext)))
$ $
xpElem "presence" xpElem "{jabber:client}presence"
(xp4Tuple (xp4Tuple
(xpAttrImplied "from" xpPrim) (xpAttrImplied "from" xpPrim)
(xpAttrImplied "to" xpPrim) (xpAttrImplied "to" xpPrim)
@ -61,9 +61,9 @@ presenceP = xpWrap (\((from, to, id, tp),(shw, stat, prio, ext))
(xpAttrImplied "type" xpPrim) (xpAttrImplied "type" xpPrim)
) )
(xp4Tuple (xp4Tuple
(xpOption . xpElemNodes "show" $ xpContent xpPrim) (xpOption . xpElemNodes "{jabber:client}show" $ xpContent xpPrim)
(xpOption . xpElemNodes "status" $ xpContent xpId) (xpOption . xpElemNodes "{jabber:client}status" $ xpContent xpId)
(xpOption . xpElemNodes "priority" $ xpContent xpPrim) (xpOption . xpElemNodes "{jabber:client}priority" $ xpContent xpPrim)
(xpAll xpElemVerbatim) (xpAll xpElemVerbatim)
) )
@ -71,11 +71,12 @@ iqP :: PU [Node] IQ
iqP = xpWrap (\((from, to, id, tp),body) -> IQ from to id tp body) iqP = xpWrap (\((from, to, id, tp),body) -> IQ from to id tp body)
(\(IQ from to id tp body) -> ((from, to, id, tp), body)) (\(IQ from to id tp body) -> ((from, to, id, tp), body))
$ $
xpElem "iq" xpElem "{jabber:client}iq"
(xp4Tuple (xp4Tuple
(xpAttrImplied "from" xpPrim) (xpAttrImplied "from" xpPrim)
(xpAttrImplied "to" xpPrim) (xpAttrImplied "to" xpPrim)
(xpAttr "id" xpId) (xpAttr "id" xpId)
(xpAttr "type" xpPrim)) ((xpAttr "type" xpPrim) :: PU [(Name,[Content])] IQType)
)
(xpElemVerbatim) (xpElemVerbatim)

62
src/Network/XMPP/Monad.hs

@ -15,16 +15,18 @@ import Data.Text(Text)
import Data.Conduit import Data.Conduit
import Data.Conduit.Binary as CB import Data.Conduit.Binary as CB
import Data.Conduit.Hexpat as CH
import Data.Conduit.List as CL import Data.Conduit.List as CL
import Data.Conduit.Text as CT import Data.Conduit.Text as CT
import Data.Conduit.TLS import Data.Conduit.TLS
import Data.XML.Pickle import Data.XML.Pickle
import Data.XML.Types import Data.XML.Types
import Text.XML.Stream.Parse as XP --import Text.XML.Stream.Parse as XP
import Text.XML.Stream.Render as XR import Text.XML.Stream.Render as XR
import Text.XML.Stream.Elements import Text.XML.Stream.Elements
import qualified Data.Text as Text import qualified Data.Text as Text
import Network.XMPP.Types import Network.XMPP.Types
@ -33,9 +35,6 @@ import Network.XMPP.Pickle
import System.IO import System.IO
-- parseOpts :: ParseOptions tag text
-- parseOpts = ParseOptions (Just UTF8) Nothing
pushN :: Element -> XMPPMonad () pushN :: Element -> XMPPMonad ()
pushN x = do pushN x = do
sink <- gets sConPush sink <- gets sConPush
@ -57,34 +56,27 @@ pulls snk = do
lift $ source $$ snk lift $ source $$ snk
pullE :: XMPPMonad Element pullE :: XMPPMonad Element
pullE = do pullE = pulls elementFromEvents
pulls elementFromEvents
pullPickle :: PU [Node] b -> XMPPMonad b pullPickle :: Show b => PU [Node] b -> XMPPMonad b
pullPickle p = unpickleElem p <$> pullE pullPickle p = unpickleElem p <$> pullE
pull :: XMPPMonad Stanza pull :: XMPPMonad Stanza
pull = pullPickle stanzaP pull = pullPickle stanzaP
-- pull :: XMPPMonad Stanza
-- pull = elementToStanza <$> pullE
xmppFromHandle xmppFromHandle
:: Handle -> Text -> Text -> Maybe Text :: Handle -> Text -> Text -> Maybe Text
-> XMPPMonad a -> XMPPMonad a
-> IO (a, XMPPState) -> IO (a, XMPPState)
xmppFromHandle handle hostname username resource f = runResourceT $ do xmppFromHandle handle hostname username resource f = runResourceT $ do
liftIO $ hSetBuffering handle NoBuffering liftIO $ hSetBuffering handle NoBuffering
let raw = CB.sourceHandle handle -- $= conduitStdout raw <- bufferSource $ CB.sourceHandle handle
liftIO $ BS.hPut handle "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" to=\"species64739.dyndns.org\">" src <- bufferSource $ raw $= CH.parseBS defaultParseOptions
src <- bufferSource $ raw $= CT.decode CT.utf8 $= XP.parseText def
src $= CL.map (Text.pack . show) $= CT.encode CT.utf8 $$ sinkHandle stdout
error "done"
let st = XMPPState let st = XMPPState
src src
undefined -- raw raw
(\xs -> CL.sourceList xs (\xs -> CL.sourceList xs
$$ XR.renderBytes def =$ conduitStdout =$ CB.sinkHandle handle) $$ XR.renderBytes def =$ CB.sinkHandle handle)
(BS.hPut handle) (BS.hPut handle)
(Just handle) (Just handle)
def def
@ -93,39 +85,3 @@ xmppFromHandle handle hostname username resource f = runResourceT $ do
username username
resource resource
runStateT f st runStateT f st
xml =
[ "<?xml version='1.0'?>"
, "<stream:stream xmlns='JABBER15:client' "
, "xmlns:stream='http://etherx.jabber.org/streams' id='1365401808' "
, "from='species64739.dyndns.org' version='1.0' xml:lang='en'>"
, "<stream:features>"
, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
, "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
, "<mechanism>PLAIN"
, "</mechanism>"
, "<mechanism>DIGEST-MD5"
, "</mechanism>"
, "<mechanism>SCRAM-SHA-1"
, "</mechanism>"
, "</mechanisms>"
, "<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='yy7di5kE0syuCXOQTXNBTclpNTo='/>"
, "<register xmlns='http://jabber.org/features/iq-register'/>"
, "</stream:features>"
, error "Booh!"
] :: [ByteString]
xml2 = BS.concat ["<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='2181744549' from='species64739.dyndns.org' version='1.0' xml:lang='en'>"
,"<stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism><mechanism>DIGEST-MD5</mechanism><mechanism>SCRAM-SHA-1</mechanism></mechanisms><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://www.process-one.net/en/ejabberd/' ver='yy7di5kE0syuCXOQTXNBTclpNTo='/><register xmlns='http://jabber.org/features/iq-register'/></stream:features>"]
fooS sr = sr $= CT.decode CT.utf8 $= XP.parseText def
blarg = forever $ do
p <- CL.peek
case p of
Nothing -> error "end"
Just p' -> liftIO $ print p
CL.drop 1
test :: Source (ResourceT IO) ByteString -> ResourceT IO ()
test sr = fooS sr $$ blarg

2
src/Network/XMPP/SASL.hs

@ -58,7 +58,7 @@ saslResponse2E =
xmppSASL :: Text -> XMPPMonad () xmppSASL :: Text -> XMPPMonad ()
xmppSASL passwd = do xmppSASL passwd = do
mechanisms <- gets $ saslMechanisms . sFeatures mechanisms <- gets $ saslMechanisms . sFeatures
unless ("DIGEST-MD5" `elem` mechanisms) $ error "No usable auth mechanism" unless ("DIGEST-MD5" `elem` mechanisms) . error $ "No usable auth mechanism: " ++ show mechanisms
pushN $ saslInitE "DIGEST-MD5" pushN $ saslInitE "DIGEST-MD5"
Right challenge <- B64.decode . Text.encodeUtf8<$> pullPickle challengePickle Right challenge <- B64.decode . Text.encodeUtf8<$> pullPickle challengePickle
let Right pairs = toPairs challenge let Right pairs = toPairs challenge

34
src/Network/XMPP/Stream.hs

@ -14,28 +14,23 @@ import Network.XMPP.Pickle
import Network.XMPP.Types import Network.XMPP.Types
import Data.Conduit import Data.Conduit
import qualified Data.Conduit.Hexpat as CH
import Data.Conduit.List as CL import Data.Conduit.List as CL
import Data.Conduit.Text as CT
import Data.Default(def) import Data.Default(def)
import qualified Data.List as L import qualified Data.List as L
import Data.Conduit.Text as CT
import Data.Text as T import Data.Text as T
import Data.XML.Types
import Data.XML.Pickle import Data.XML.Pickle
import Data.XML.Types
import qualified Text.XML.Stream.Parse as XP -- import qualified Text.XML.Stream.Parse as XP
import Text.XML.Stream.Elements import Text.XML.Stream.Elements
-- import Text.XML.Stream.Elements -- import Text.XML.Stream.Elements
throwOutJunk = do throwOutJunk = do
liftIO $ putStrLn "peeking..."
next <- CL.peek next <- CL.peek
liftIO $ putStrLn "peeked."
liftIO $ do
putStrLn "peek:"
print next
putStrLn "=========="
case next of case next of
Nothing -> return () Nothing -> return ()
Just (EventBeginElement _ _) -> return () Just (EventBeginElement _ _) -> return ()
@ -43,7 +38,6 @@ throwOutJunk = do
openElementFromEvents = do openElementFromEvents = do
throwOutJunk throwOutJunk
liftIO $ putStrLn "starting ------"
Just (EventBeginElement name attrs) <- CL.head Just (EventBeginElement name attrs) <- CL.head
return $ Element name attrs [] return $ Element name attrs []
@ -60,8 +54,7 @@ xmppRestartStream :: XMPPMonad ()
xmppRestartStream = do xmppRestartStream = do
raw <- gets sRawSrc raw <- gets sRawSrc
src <- gets sConSrc src <- gets sConSrc
newsrc <- lift (bufferSource $ raw $= CH.parseBS CH.defaultParseOptions)
newsrc <- lift (bufferSource $ raw $= XP.parseBytes def)
modify (\s -> s{sConSrc = newsrc}) modify (\s -> s{sConSrc = newsrc})
xmppStartStream xmppStartStream
@ -73,9 +66,7 @@ xmppStream = do
xmppStreamHeader :: Sink Event (ResourceT IO) () xmppStreamHeader :: Sink Event (ResourceT IO) ()
xmppStreamHeader = do xmppStreamHeader = do
liftIO $ putStrLn "throwing junk!" throwOutJunk
-- throwOutJunk
liftIO $ putStrLn "junk thrown"
(ver, _, _) <- unpickleElem pickleStream <$> openElementFromEvents (ver, _, _) <- unpickleElem pickleStream <$> openElementFromEvents
unless (ver == "1.0") $ error "Not XMPP version 1.0 " unless (ver == "1.0") $ error "Not XMPP version 1.0 "
return() return()
@ -88,13 +79,7 @@ xmppStreamFeatures = unpickleElem pickleStreamFeatures <$> elementFromEvents
-- Pickling -- Pickling
pickleStream :: PU [Node] (Text, Maybe Text, Maybe Text) pickleStream :: PU [Node] (Text, Maybe Text, Maybe Text)
pickleStream = xpWrap snd (((),()),) . pickleStream = xpElemAttrs (Name "stream" (Just "http://etherx.jabber.org/streams") (Just "stream"))
xpElemAttrs "stream:stream" $
xpPair
(xpPair
(xpAttrFixed "xmlns" "jabber:client" )
(xpAttrFixed "xmlns:stream" "http://etherx.jabber.org/streams" )
)
(xpTriple (xpTriple
(xpAttr "version" xpId) (xpAttr "version" xpId)
(xpOption $ xpAttr "from" xpId) (xpOption $ xpAttr "from" xpId)
@ -107,13 +92,14 @@ pickleTLSFeature = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-tls}starttls"
pickleSaslFeature :: PU [Node] [Text] pickleSaslFeature :: PU [Node] [Text]
pickleSaslFeature = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-sasl}mechanisms" pickleSaslFeature = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-sasl}mechanisms"
(xpAll $ xpElemNodes "mechanism" (xpContent xpId) ) (xpAll $ xpElemNodes
"{urn:ietf:params:xml:ns:xmpp-sasl}mechanism" (xpContent xpId) )
pickleStreamFeatures :: PU [Node] ServerFeatures pickleStreamFeatures :: PU [Node] ServerFeatures
pickleStreamFeatures = xpWrap ( \(tls, sasl, rest) -> SF tls (mbl sasl) rest) pickleStreamFeatures = xpWrap ( \(tls, sasl, rest) -> SF tls (mbl sasl) rest)
(\(SF tls sasl rest) -> (tls, lmb sasl, rest)) (\(SF tls sasl rest) -> (tls, lmb sasl, rest))
$ $
xpElemNodes "stream:features" xpElemNodes (Name "features" (Just "http://etherx.jabber.org/streams") (Just "stream"))
(xpTriple (xpTriple
(xpOption pickleTLSFeature) (xpOption pickleTLSFeature)
(xpOption pickleSaslFeature) (xpOption pickleSaslFeature)

1
xmpp-lib.cabal

@ -29,6 +29,7 @@ library
, resourcet -any , resourcet -any
, containers -any , containers -any
, random -any , random -any
, hexpat-internals -any
, tls -any , tls -any
, tls-extra -any , tls-extra -any
, pureMD5 -any , pureMD5 -any

Loading…
Cancel
Save