udbus 0.1.1 → 0.2.0
raw patch · 15 files changed
+1428/−707 lines, 15 filesdep +QuickCheckdep +containersdep +test-frameworkdep ~cerealnew-component:exe:Tests
Dependencies added: QuickCheck, containers, test-framework, test-framework-quickcheck2, utf8-string
Dependency ranges changed: cereal
Files
- DBus.hs +25/−31
- LICENSE +1/−1
- Network/DBus.hs +240/−143
- Network/DBus/Actions.hs +216/−0
- Network/DBus/IEEE754.hs +7/−0
- Network/DBus/Internal.hs +32/−0
- Network/DBus/Message.hs +271/−242
- Network/DBus/MessageType.hs +93/−0
- Network/DBus/Signature.hs +89/−75
- Network/DBus/StdMessage.hs +35/−0
- Network/DBus/Type.hs +154/−94
- Network/DBus/Wire.hs +128/−104
- README.md +14/−14
- Tests.hs +101/−0
- udbus.cabal +22/−3
DBus.hs view
@@ -1,45 +1,39 @@ {-# LANGUAGE OverloadedStrings #-} -import System.Posix.User import Network.DBus+import Network.DBus.Actions import Network.DBus.Message+import Network.DBus.MessageType+import Network.DBus.StdMessage import Network.DBus.Type import Network.DBus.Signature import Data.Word+import Control.Monad import Control.Monad.Trans+import Control.Concurrent -dbusDestination = "org.freedesktop.DBus"-dbusPath = "/org/freedesktop/DBus"-dbusInterface = "org.freedesktop.DBus" -mainDbus uid = withSession $ do- authenticateUID uid- let msg = msgMethodCall dbusDestination dbusPath dbusInterface "Hello" []- messageSend msg- messageRecv- liftIO $ putStrLn "spurious"- messageRecv+main = do+ con <- establish busGetSession authenticateWithRealUID+ registerCall con "/" $ calltableFromList+ [ ("xyz", "example.test.dbus", \s sig b -> putStrLn (show s ++ ": " ++ show b))+ ] - let msg = msgMethodCall dbusDestination dbusPath dbusInterface "ListNames" []- messageSend msg- msg <- messageRecv- let b = readBody msg- liftIO $ putStrLn $ show b+ forkIO $ forever $ do+ r <- call con dbusDestination msgDBusListNames+ putStrLn $ show r+ threadDelay 100000000 - let msg = msgMethodCall "org.freedesktop.Notifications" "/org/freedesktop/Notifications" "org.freedesktop.Notifications" "Notify"- [ DbusString "y"- , DbusUInt32 1- , DbusString "x"- , DbusString "this is a string"- , DbusString "this is a o----"- , DbusArray (SigString) []- , DbusArray (SigDict SigString SigVariant) []- , DbusInt32 4000- ]+ call con "org.freedesktop.Notifications" $ DBusCall "/org/freedesktop/Notifications" "Notify" (Just "org.freedesktop.Notifications")+ [ DBusString "y"+ , DBusUInt32 1+ , DBusString "x"+ , DBusString "this is a string"+ , DBusString "this is a o----"+ , DBusArray (SigString) []+ , DBusArray (SigDict SigString SigVariant) []+ , DBusInt32 4000+ ] - liftIO $ putStrLn $ show msg- messageSend msg- msgR <- messageRecv- liftIO $ putStrLn $ show msgR -main = getRealUserID >>= mainDbus . fromIntegral+ forever $ threadDelay 10000
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2010-2011 Vincent Hanquez <vincent@snarc.org>+Copyright (c) 2010-2013 Vincent Hanquez <vincent@snarc.org> All rights reserved.
Network/DBus.hs view
@@ -1,176 +1,273 @@+{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : Network.DBus+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+-- module Network.DBus- ( DBusHandle- , authenticate- , authenticateUID-- , connectSession- , connectSystem- , connectHandle-- , withContext- , withSession- , withSystem+ (+ -- * handle connections to DBus+ establish+ , establishWithCatchall+ , DBusConnection+ -- * Types+ , DBusMessageable(..)+ , DBusCall(..)+ , DBusReturn(..)+ , DBusError(..)+ , DBusSignal(..)+ , DBusValue(..)+ , DBusTypeable(..)+ , ObjectPath(..)+ , PackedString(..)+ , packedStringToString+ , Type(..)+ , Signature+ , SignatureElem+ , DBusMatchRules(..)+ , defaultDBusMatchRules+ , MessageType(..)+ , BusName(..)+ , ErrorName(..)+ , Member(..)+ , Interface(..)+ -- * standard way to interact with dbus+ , addMatch+ -- * main loop creation+ , runMainLoop+ , runMainLoopCatchall+ -- * interact with the connection+ , call+ , reply+ , calltableFromList+ , registerPath+ , unregisterPath+ , registerCall+ , unregisterCall+ , registerSignal+ , unregisterSignal+ -- * create a new context on system or session bus+ , busGetSystem+ , busGetSession+ -- * authenticate methods available+ , authenticate+ , authenticateUID+ , authenticateWithRealUID+ ) where - , messageSend- , messageRecv+import Network.DBus.Actions+import Network.DBus.Message+import Network.DBus.MessageType+import Network.DBus.StdMessage+import Control.Concurrent (forkIO, ThreadId)+import Control.Concurrent.MVar+import Control.Exception+import Data.Maybe+import Control.Monad+import qualified Data.Map as M+import Data.List (intercalate)+import System.Posix.User (getRealUserID) - -- * from Message module- , MessageType(..)- , MessageFlag(..)- , Field(..)- , Message(..)- , Serial- , msgMethodCall- , msgMethodReturn- , msgError- , msgSignal+type MessageVar = MVar DBusMessage - -- * read a message body- , readBody- , readBodyWith+newMessageVar = newEmptyMVar - -- * from Signature module- , SignatureElem(..)- , Signature+type Signalback = BusName -> Signature -> Body -> IO ()+type Callback = Serial -> Signature -> Body -> IO ()+type DispatchTable a = M.Map Member [(Interface, a)] - -- * from Type module- , ObjectPath- , DbusType(..)- ) where+calltableFromList :: [ (Member, Interface, a) ] -> DispatchTable a+calltableFromList = foldl f M.empty where+ f acc (member, intf, callback) = M.alter (appendOrCreate intf callback) member acc+ appendOrCreate intf callback Nothing = Just [(intf,callback)]+ appendOrCreate intf callback (Just l) = Just ((intf,callback) : l) -import Numeric (showHex)-import Data.Char (ord)-import Data.ByteString.Char8 (ByteString)-import qualified Data.ByteString.Char8 as BC-import qualified Data.ByteString.Lazy as L+-- | opaque type representing a connection to DBus and a receiving dispatcher thread.+-- maintain table to route message between handlers.+data DBusConnection = DBusConnection+ { connectionContext :: DBusContext+ , connectionSendLock :: MVar ()+ , connectionCallbacks :: MVar (M.Map Serial MessageVar)+ , connectionPaths :: MVar (M.Map ObjectPath (DispatchTable Callback))+ , connectionSignals :: MVar (M.Map ObjectPath (DispatchTable Signalback))+ , connectionDefaultCallback :: DBusMessage -> IO ()+ , connectionMainLoop :: MVar ThreadId+ } -import Control.Arrow-import Control.Applicative ((<$>))-import Control.Exception-import Control.Monad.State+data DBusMatchRules = DBusMatchRules+ { matchType :: Maybe MessageType+ , matchSender :: Maybe BusName+ , matchInterface :: Maybe Interface+ , matchMember :: Maybe Member+ , matchPath :: Maybe ObjectPath+ , matchDestination :: Maybe BusName+ } -import System.Environment-import System.IO hiding (hGetLine)-import Network.Socket-import Network.DBus.Message-import Network.DBus.Type-import Network.DBus.Signature+defaultDBusMatchRules = DBusMatchRules Nothing Nothing Nothing Nothing Nothing Nothing --- | Represent an open access to dbus. for now only based on system handle.-newtype DBusHandle = DBusHandle Handle+sendLock con f = withMVar (connectionSendLock con) $ \() -> f -type DBusContext a = StateT (DBusHandle, Serial) IO a+-- | Establish a new connection to dbus, using the two functions to+-- first establish a new context, and second to authenticate to the bus.+-- this will automatically create a mainloop thread.+establish :: IO DBusContext -- ^ function to create a new dbus context (busGetSystem or busGetSession)+ -> (DBusContext -> IO ()) -- ^ function to authenticate to dbus+ -> IO DBusConnection+establish createContext auth = do+ ctx <- createContext+ auth ctx+ runMainLoop ctx -withHandle :: (Handle -> IO a) -> DBusContext a-withHandle f = do- (DBusHandle h) <- fst <$> get- liftIO (f h)+establishWithCatchall catchall createContext auth = do+ ctx <- createContext+ _ <- auth ctx+ runMainLoopCatchall catchall ctx -hGet :: Int -> DBusContext ByteString-hGet i = withHandle (\h -> BC.hGet h i)+call :: DBusConnection -> BusName -> DBusCall -> IO DBusReturn+call con destination c = do+ let msg = messageMapFields (fieldsSetDestination destination) $ toDBusMessage c+ mvar <- sendLock con $ do+ serial <- busGetNextSerial (connectionContext con)+ mvar <- registerCallback con serial+ messageSendWithSerial (connectionContext con) serial msg+ return mvar+ result <- takeMVar mvar+ case msgType result of+ TypeError ->+ case errorFromDBusMessage result of+ Nothing -> throwIO invalidException+ Just err -> throwIO err+ TypeMethodReturn ->+ case fromDBusMessage result of+ Nothing -> throwIO invalidException+ Just ret -> return ret+ _ -> throwIO invalidException+ where+ invalidException = DBusError 0 "org.freedesktop.dbus.invalidpacket"+ [DBusString "invalid packet received. missing fields"]+ errorFromDBusMessage :: DBusMessage -> Maybe DBusError+ errorFromDBusMessage = fromDBusMessage -hPut :: ByteString -> DBusContext ()-hPut b = withHandle (\h -> BC.hPut h b)+addMatch :: DBusConnection -> DBusMatchRules -> IO ()+addMatch con mr = call con dbusDestination (msgDBusAddMatch serialized) >> return ()+ where+ serialized = intercalate "," $ filter (not . null)+ [ mm "type" show $ matchType mr+ , mm "sender" unBusName $ matchSender mr+ , mm "interface" unInterface $ matchInterface mr+ , mm "member" unMember $ matchMember mr+ , mm "path" unObjectPath $ matchPath mr+ , mm "destination" unBusName $ matchDestination mr+ ]+ mm key f = maybe "" (surroundQuote key . f)+ surroundQuote key v = concat [ key, "='", v, "'" ] -hPuts :: [ByteString] -> DBusContext ()-hPuts bs = withHandle (\h -> L.hPut h $ L.fromChunks bs)+reply con rep = do+ let msg = toDBusMessage rep+ sendLock con $ messageSend (connectionContext con) msg -hGetLine :: DBusContext ()-hGetLine = withHandle BC.hGetLine >> return ()+registerCallback con serial = do+ mvar <- newMessageVar+ modifyMVar_ (connectionCallbacks con) (return . M.insert serial mvar)+ return mvar -authenticateUID :: Int -> DBusContext ()-authenticateUID uid = authenticate hexencoded_uid- where- hexencoded_uid = BC.pack $ concatMap (hex2 . ord) $ show uid- hex2 a- | a < 0x10 = "0" ++ showHex a ""- | otherwise = showHex a ""+registerPath_ f con path callTable =+ modifyMVar_ (f con) (return . M.insert path callTable)+unregisterPath_ f con path =+ modifyMVar_ (f con) (return . M.delete path) -authenticate :: ByteString -> DBusContext ()-authenticate auth = do- hPut $ BC.concat ["\0AUTH EXTERNAL ", auth, "\r\n"]- _ <- hGetLine- hPut "BEGIN\r\n"+registerCall = registerPath_ connectionPaths+unregisterCall = unregisterPath_ connectionPaths -close :: DBusHandle -> IO ()-close (DBusHandle h) = hClose h+{-# DEPRECATED registerPath "use registerCall" #-}+registerPath = registerPath_ connectionPaths+{-# DEPRECATED unregisterPath "use unregisterCall" #-}+unregisterPath = unregisterPath_ connectionPaths -connectUnix :: ByteString -> IO DBusHandle-connectUnix addr = do- let sockaddr = SockAddrUnix $ BC.unpack addr- sock <- socket AF_UNIX Stream 0- connect sock sockaddr- h <- socketToHandle sock ReadWriteMode- hSetBuffering h NoBuffering- return $ DBusHandle h+registerSignal = registerPath_ connectionSignals+unregisterSignal = unregisterPath_ connectionSignals -connectOver :: ByteString -> [(ByteString, ByteString)] -> IO DBusHandle-connectOver "unix" flags = do- let abstract = lookup "abstract" flags- case abstract of- Nothing -> error "no abstract path, use the normal path ..."- Just path -> connectUnix $ BC.concat ["\x00", path]+runMainLoop = runMainLoopCatchall (\_ -> return ()) -connectOver _ _ = do- error "not implemented yet"+runMainLoopCatchall catchAll context = do+ callbacks <- newMVar M.empty+ callPaths <- newMVar M.empty+ signalPaths <- newMVar M.empty+ mainloopPid <- newEmptyMVar+ sendLockVar <- newMVar () -connectSessionAt :: ByteString -> IO DBusHandle-connectSessionAt addr = do- let (domain, flagstr) = second BC.tail $ BC.breakSubstring ":" addr- let flags = map (\x -> let (k:v:[]) = BC.split '=' x in (k,v)) $ BC.split ',' flagstr- connectOver domain flags+ let con = DBusConnection+ { connectionContext = context+ , connectionCallbacks = callbacks+ , connectionPaths = callPaths+ , connectionSignals = signalPaths+ , connectionDefaultCallback = catchAll+ , connectionMainLoop = mainloopPid+ , connectionSendLock = sendLockVar+ }+ pid <- forkIO (dispatcher con)+ putMVar mainloopPid pid --- | connect to the dbus session bus define by the environment variable DBUS_SESSION_BUS_ADDRESS-connectSession :: IO DBusHandle-connectSession = BC.pack <$> getEnv "DBUS_SESSION_BUS_ADDRESS" >>= connectSessionAt+ _ <- call con dbusDestination msgDBusHello+ return con --- | connect to the dbus system bus-connectSystem :: IO DBusHandle-connectSystem = connectUnix "/var/run/dbus/system_bus_socket"+dispatcher con = forever loop where+ loop = do+ msg <- messageRecv (connectionContext con)+ fallback <- dispatch msg+ when fallback $ notifyUser msg+ dispatch msg@(msgType -> TypeMethodCall) = callCallback msg+ dispatch msg@(msgType -> TypeSignal) = signalCallback msg+ dispatch msg@(msgType -> TypeMethodReturn) = withReply msg+ dispatch msg@(msgType -> TypeError) = withReply msg+ dispatch _ = return False --- | connect onto a previously open handle-connectHandle :: Handle -> IO DBusHandle-connectHandle h = return $ DBusHandle h+ withReply (fieldsReplySerial . msgFields -> Nothing) = return False+ withReply msg@(fieldsReplySerial . msgFields -> Just serial) = do+ callback <- modifyMVar (connectionCallbacks con) $ \m ->+ case M.lookup serial m of+ Just c -> return (M.delete serial m, Just c)+ Nothing -> return (m, Nothing)+ case callback of+ Nothing -> return True+ Just c -> putMVar c msg >> return False+ withReply _ = return False --- | create a new Dbus context from a ini function to create a dbusHandle.-withContext :: IO DBusHandle -> DBusContext a -> IO a-withContext ini f = bracket ini Network.DBus.close (\h -> evalStateT f (h,1))+ callCallback msg@(msgFields -> fields) =+ case (fieldsPath fields, fieldsMember fields) of+ (Just path, Just member) -> do+ calltables <- readMVar (connectionPaths con)+ let mcallback = findDispatchTable calltables path member (fieldsInterface fields)+ case mcallback of+ Nothing -> return True+ Just c -> c (msgSerial msg) (fieldsSignature fields) (readBody msg) >> return False+ -- method call is not valid, so just ignore+ _ -> return False --- | create a new Dbus context on session bus-withSession :: DBusContext a -> IO a-withSession = withContext connectSession+ signalCallback msg@(msgFields -> fields) =+ case (fieldsPath fields, fieldsMember fields) of+ (Just path, Just member) -> do+ signaltables <- readMVar (connectionSignals con)+ let mcallback = findDispatchTable signaltables path member (fieldsInterface fields)+ case mcallback of+ Nothing -> return True+ Just c -> c (fromJust $ fieldsSender fields) (fieldsSignature fields) (readBody msg) >> return False+ -- signal is not valid, so just ignore+ _ -> return False --- | create a new Dbus context on system bus-withSystem :: DBusContext a -> IO a-withSystem = withContext connectSystem+ findDispatchTable table path member intf = M.lookup path table >>= M.lookup member >>= getCall where+ getCall callbackList = case intf of+ Nothing -> safeHead $ map snd callbackList+ Just i -> lookup i callbackList --- | send one message to the bus--- note that the serial of the message sent is allocated here.-messageSend :: Message -> DBusContext Serial-messageSend msg = do- serial <- snd <$> get- modify (\(h,_) -> (h, serial+1))- let fieldstr = writeFields (msgFields msg)- let fieldlen = BC.length fieldstr- let alignfields = alignVal 8 fieldlen - fieldlen- let header = (headerFromMessage msg)- { headerBodyLength = BC.length $ msgBody msg- , headerFieldsLength = fieldlen- , headerSerial = serial }- hPuts [ writeHeader header, fieldstr, BC.replicate alignfields '\0', msgBody msg ]- return serial+ safeHead [] = Nothing+ safeHead (x:_) = Just x --- | receive one single message from the bus--- it is not necessarily the reply from a previous sent message.-messageRecv :: DBusContext Message-messageRecv = do- hdr <- readHeader <$> hGet 16- fields <- readFields <$> hGet (alignVal 8 $ headerFieldsLength hdr)- body <- hGet (headerBodyLength hdr)- return $ (messageFromHeader hdr) { msgFields = fields, msgBody = body }+ notifyUser = connectionDefaultCallback con -alignVal :: Int -> Int -> Int-alignVal n x- | x `mod` n == 0 = x- | otherwise = x + (n - (x `mod` n))+-- | use the real user UID to authenticate to DBus.+authenticateWithRealUID :: DBusContext -> IO ()+authenticateWithRealUID ctx = getRealUserID >>= authenticateUID ctx . fromIntegral
+ Network/DBus/Actions.hs view
@@ -0,0 +1,216 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : Network.DBus.Actions+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+--+module Network.DBus.Actions+ ( DBusContext+ , DBusTransport(..)+ , authenticate+ , authenticateUID++ , connectSession+ , connectSystem+ , contextNew+ , contextNewWith++ , busGetSession+ , busGetSystem+ , busGetNextSerial+ , busClose++ , messageSend+ , messageSendWithSerial+ , messageRecv++ -- * from Message module+ , MessageType(..)+ , MessageFlag(..)+ , DBusFields(..)+ , DBusMessage(..)+ , Serial++ -- * read a message body+ , readBody+ , readBodyWith++ -- * from Signature module+ , Type(..)+ , SignatureElem+ , Signature+ , serializeSignature+ , unserializeSignature++ -- * from Type module+ , ObjectPath(..)+ , PackedString(..)+ , packedStringToString+ , DBusValue(..)+ , DBusTypeable(..)+ ) where++import Numeric (showHex)+import Data.Char (ord)+import Data.ByteString.Char8 (ByteString)+import qualified Data.ByteString.Char8 as BC++import Control.Arrow+import Control.Applicative ((<$>))+import Control.Concurrent+import Control.Monad.State++import System.Environment+import System.IO hiding (hGetLine)+import Network.Socket+import Network.DBus.Message+import Network.DBus.Type+import Network.DBus.Internal+import Network.DBus.Signature++data DBusTransport = DBusTransport+ { transportGet :: Int -> IO ByteString+ , transportPut :: ByteString -> IO ()+ , transportClose :: IO ()+ }++data DBusContext = DBusContext+ { contextTransport :: DBusTransport+ , contextSerial :: MVar Serial+ }++withTransport :: DBusContext -> (DBusTransport -> IO a) -> IO a+withTransport ctx f = f $ contextTransport ctx++transportHandle :: Handle -> DBusTransport+transportHandle h = DBusTransport+ { transportGet = BC.hGet h+ , transportPut = BC.hPut h+ , transportClose = hClose h+ }++hGet :: DBusContext -> Int -> IO ByteString+hGet ctx i = withTransport ctx (\t -> transportGet t i)++hPut :: DBusContext -> ByteString -> IO ()+hPut ctx b = withTransport ctx (\t -> transportPut t b)++hPuts :: DBusContext -> [ByteString] -> IO ()+hPuts ctx bs = withTransport ctx (\t -> mapM_ (transportPut t) bs)++hGetLine :: DBusContext -> IO ()+hGetLine ctx = withTransport ctx getTillEOL+ where getTillEOL transport = do+ v <- transportGet transport 1+ if BC.singleton '\n' == v then return () else getTillEOL transport++-- | authenticate to DBus using a UID.+authenticateUID :: DBusContext -> Int -> IO ()+authenticateUID ctx uid = authenticate ctx hexencoded_uid+ where hexencoded_uid = BC.pack $ concatMap (hex2 . ord) $ show uid+ hex2 a+ | a < 0x10 = '0' : showHex a ""+ | otherwise = showHex a ""++-- | authenticate to DBus using a raw bytestring.+authenticate :: DBusContext -> ByteString -> IO ()+authenticate ctx auth = do+ hPut ctx $ BC.concat ["\0AUTH EXTERNAL ", auth, "\r\n"]+ _ <- hGetLine ctx+ hPut ctx "BEGIN\r\n"++close :: DBusTransport -> IO ()+close = transportClose++connectUnix :: ByteString -> IO Handle+connectUnix addr = do+ let sockaddr = SockAddrUnix $ BC.unpack addr+ sock <- socket AF_UNIX Stream 0+ connect sock sockaddr+ h <- socketToHandle sock ReadWriteMode+ hSetBuffering h NoBuffering+ return h++connectOver :: ByteString -> [(ByteString, ByteString)] -> IO Handle+connectOver "unix" flags = do+ let abstract = lookup "abstract" flags+ case abstract of+ Nothing -> error "no abstract path, use the normal path ..."+ Just path -> connectUnix $ BC.concat ["\x00", path]++connectOver _ _ = error "not implemented yet"++connectSessionAt :: ByteString -> IO Handle+connectSessionAt addr = do+ let (domain, flagstr) = second BC.tail $ BC.breakSubstring ":" addr+ let flags = map (\x -> let (k:v:[]) = BC.split '=' x in (k,v)) $ BC.split ',' flagstr+ connectOver domain flags++-- | connect to the dbus session bus define by the environment variable DBUS_SESSION_BUS_ADDRESS+connectSession :: IO Handle+connectSession = BC.pack <$> getEnv "DBUS_SESSION_BUS_ADDRESS" >>= connectSessionAt++-- | connect to the dbus system bus+connectSystem :: IO Handle+connectSystem = connectUnix "/var/run/dbus/system_bus_socket"++-- | create a new DBus context from an handle+contextNew :: Handle -> IO DBusContext+contextNew h = contextNewWith (transportHandle h)++-- | create a new DBus context from a transport+contextNewWith :: DBusTransport -> IO DBusContext+contextNewWith transport = liftM (DBusContext transport) (newMVar 1)++-- | create a new DBus context on session bus+busGetSession :: IO DBusContext +busGetSession = connectSession >>= contextNew++-- | create a new DBus context on system bus+busGetSystem :: IO DBusContext+busGetSystem = connectSystem >>= contextNew++-- | close this DBus context+busClose :: DBusContext -> IO ()+busClose = transportClose . contextTransport++-- | get the next serial usable, and increment the serial state.+busGetNextSerial :: DBusContext -> IO Serial+busGetNextSerial ctx =+ modifyMVar (contextSerial ctx) (\v -> return $! (v+1, v))++-- | send one message to the bus with a predefined serial number.+messageSendWithSerial :: DBusContext -> Serial -> DBusMessage -> IO ()+messageSendWithSerial ctx serial msg = do+ let fieldstr = writeFields (msgFields msg)+ let fieldlen = BC.length fieldstr+ let alignfields = alignVal 8 fieldlen - fieldlen+ let header = (headerFromMessage msg)+ { headerBodyLength = BC.length $ msgBodyRaw msg+ , headerFieldsLength = fieldlen+ , headerSerial = serial }+ hPuts ctx [ writeHeader header, fieldstr, BC.replicate alignfields '\0', msgBodyRaw msg ]++-- | send one message to the bus+-- note that the serial of the message sent is allocated here.+messageSend :: DBusContext -> DBusMessage -> IO Serial+messageSend ctx msg = do+ serial <- busGetNextSerial ctx+ messageSendWithSerial ctx serial msg+ return serial++-- | receive one single message from the bus+-- it is not necessarily the reply from a previous sent message.+messageRecv :: DBusContext -> IO DBusMessage+messageRecv ctx = do+ hdr <- readHeader <$> hGet ctx 16+ fields <- readFields (headerEndian hdr) <$> hGet ctx (alignVal 8 $ headerFieldsLength hdr)+ body <- hGet ctx (headerBodyLength hdr)+ return $ (messageFromHeader hdr) { msgFields = fields, msgBodyRaw = body }++alignVal :: Int -> Int -> Int+alignVal n x+ | x `mod` n == 0 = x+ | otherwise = x + (n - (x `mod` n))
Network/DBus/IEEE754.hs view
@@ -1,4 +1,11 @@ {-# LANGUAGE MagicHash #-}+-- |+-- Module : Network.DBus.IEEE754+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+-- module Network.DBus.IEEE754 (encode, decode) where import GHC.Prim
+ Network/DBus/Internal.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module : Network.DBus.Internal+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+--++module Network.DBus.Internal+ ( ObjectPath(..)+ , PackedString(..)+ , packedStringToString+ ) where++import Data.Data+import Data.String+import Data.ByteString (ByteString)+import qualified Data.ByteString.UTF8 as UTF8++newtype ObjectPath = ObjectPath { unObjectPath :: String }+ deriving (Show,Eq,Ord,Data,Typeable)+instance IsString ObjectPath where+ fromString = ObjectPath++newtype PackedString = PackedString { ustringToBS :: ByteString }+ deriving (Eq,Ord,Data,Typeable)+instance IsString PackedString where+ fromString = PackedString . UTF8.fromString+instance Show PackedString where+ show = show . packedStringToString+packedStringToString = UTF8.toString . ustringToBS
Network/DBus/Message.hs view
@@ -1,299 +1,328 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module : Network.DBus.Message+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+-- module Network.DBus.Message- (- MessageType(..)- , MessageFlag(..)- , Header(..)- , Field(..)- , Message(..)- , Serial- -- * create new message- , msgMethodCall- , msgMethodReturn- , msgError- , msgSignal- -- * Parsing and serializing functions- , headerFromMessage- , messageFromHeader- , readHeader- , writeHeader- , readFields- , writeFields- , readBody- , readBodyWith- ) where+ (+ MessageType(..)+ , MessageFlag(..)+ -- * Serializing header for message+ , DBusHeader(..)+ -- * Fields type and accessor+ , DBusFields(..)+ , fieldsNew+ , fieldsNewWithBody+ , fieldsSetPath+ , fieldsSetInterface+ , fieldsSetMember+ , fieldsSetErrorName+ , fieldsSetReplySerial+ , fieldsSetDestination+ , fieldsSetSender+ , fieldsSetSignature+ , fieldsSetUnixFD+ -- * Message type+ , DBusMessage(..)+ , BusName(..)+ , Body+ , Serial+ , ErrorName(..)+ , Member(..)+ , Interface(..)+ , messageNew+ , messageMapFields+ -- * Parsing and serializing functions+ , headerFromMessage+ , messageFromHeader+ , readHeader+ , writeHeader+ , readFields+ , writeFields+ , writeBody+ , readBody+ , readBodyWith+ , readBodyRaw+ ) where +import Data.Data import Data.Word+import Data.String import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.ByteString.Char8 () import Control.Applicative ((<$>)) import Control.Monad.State +import Network.DBus.Internal import Network.DBus.Wire import Network.DBus.Type import Network.DBus.Signature -- | dbus message types data MessageType =- TypeInvalid- | TypeMethodCall- | TypeMethodReturn- | TypeError- | TypeSignal- deriving (Show,Eq,Enum)+ TypeInvalid+ | TypeMethodCall+ | TypeMethodReturn+ | TypeError+ | TypeSignal+ deriving (Eq,Enum) +instance Show MessageType where+ show TypeInvalid = "invalid"+ show TypeMethodCall = "method_call"+ show TypeMethodReturn = "method_return"+ show TypeError = "error"+ show TypeSignal = "signal"+ -- | dbus message flags data MessageFlag =- FlagNoReplyExpected- | FlagNoAutoStart- deriving (Show,Eq)+ FlagNoReplyExpected+ | FlagNoAutoStart+ deriving (Show,Eq) -- | dbus serial number type Serial = Word32 -data Header = Header- { headerEndian :: DbusEndian- , headerMessageType :: !MessageType- , headerVersion :: !Int- , headerFlags :: !Int- , headerBodyLength :: !Int- , headerSerial :: !Serial- , headerFieldsLength :: !Int- } deriving (Show,Eq)+data DBusHeader = DBusHeader+ { headerEndian :: DBusEndian+ , headerMessageType :: !MessageType+ , headerVersion :: !Int+ , headerFlags :: !Int+ , headerBodyLength :: !Int+ , headerSerial :: !Serial+ , headerFieldsLength :: !Int+ } deriving (Show,Eq) -type Body = [DbusType]+type BodyRaw = (Signature,ByteString)+type Body = [DBusValue] -type Interface = ByteString-type Member = ByteString-type BusName = ByteString-type ErrorName = ByteString+newtype Interface = Interface { unInterface :: String }+ deriving (Show,Eq,Ord)+newtype Member = Member { unMember :: String }+ deriving (Show,Eq,Ord)+newtype BusName = BusName { unBusName :: String }+ deriving (Show,Eq,Ord)+newtype ErrorName = ErrorName { unErrorName :: String }+ deriving (Show,Eq,Ord,Data,Typeable) -data Field =- FieldPath ObjectPath- | FieldInterface Interface- | FieldMember Member- | FieldErrorName ErrorName- | FieldReplySerial Serial- | FieldDestination BusName- | FieldSender ByteString- | FieldSignature Signature- | FieldUnixFds Word32- deriving (Show,Eq)+instance IsString Interface where+ fromString = Interface -fieldVal :: Field -> Int-fieldVal (FieldPath _) = 1-fieldVal (FieldInterface _) = 2-fieldVal (FieldMember _) = 3-fieldVal (FieldErrorName _) = 4-fieldVal (FieldReplySerial _) = 5-fieldVal (FieldDestination _) = 6-fieldVal (FieldSender _) = 7-fieldVal (FieldSignature _) = 8-fieldVal (FieldUnixFds _) = 9- -data Message = Message- { msgEndian :: DbusEndian- , msgType :: !MessageType- , msgVersion :: !Int- , msgFlags :: !Int- , msgSerial :: !Serial- , msgFields :: [Field]- , msgBody :: ByteString- } deriving (Show,Eq)+instance IsString Member where+ fromString = Member -defaultMessage :: Message-defaultMessage = Message- { msgEndian = LE- , msgType = TypeInvalid- , msgVersion = 1- , msgFlags = 0- , msgSerial = 0- , msgFields = []- , msgBody = B.empty- }+instance IsString BusName where+ fromString = BusName -headerFromMessage :: Message -> Header-headerFromMessage msg = Header- { headerEndian = msgEndian msg- , headerMessageType = msgType msg- , headerVersion = msgVersion msg- , headerFlags = msgFlags msg- , headerBodyLength = 0- , headerSerial = msgSerial msg- , headerFieldsLength = 0- }+instance IsString ErrorName where+ fromString = ErrorName -messageFromHeader :: Header -> Message-messageFromHeader hdr = Message- { msgEndian = headerEndian hdr- , msgType = headerMessageType hdr- , msgVersion = headerVersion hdr- , msgFlags = headerFlags hdr- , msgSerial = headerSerial hdr- , msgFields = []- , msgBody = B.empty- }+type UnixFD = Word32 --- | create a new method call message-msgMethodCall :: BusName -> ObjectPath -> Interface -> Member -> Body -> Message-msgMethodCall destination path interface method body = defaultMessage- { msgType = TypeMethodCall- , msgFields =- [ FieldPath path- , FieldDestination destination- , FieldInterface interface- , FieldMember method- ] ++ if null body then [] else [ FieldSignature $ signatureBody body ]- , msgBody = writeBody body- }+data DBusFields = DBusFields+ { fieldsPath :: Maybe ObjectPath+ , fieldsInterface :: Maybe Interface+ , fieldsMember :: Maybe Member+ , fieldsErrorName :: Maybe ErrorName+ , fieldsReplySerial :: Maybe Serial+ , fieldsDestination :: Maybe BusName+ , fieldsSender :: Maybe BusName+ , fieldsSignature :: Signature+ , fieldsUnixFD :: Maybe UnixFD+ } deriving (Show,Eq) --- | create a new signal message-msgSignal :: ObjectPath -> Interface -> Member -> Body -> Message-msgSignal path interface method body = defaultMessage- { msgType = TypeSignal- , msgFields =- [ FieldPath path- , FieldInterface interface- , FieldMember method- ] ++ if null body then [] else [ FieldSignature $ signatureBody body ]- , msgBody = writeBody body- }+data DBusMessage = DBusMessage+ { msgEndian :: DBusEndian+ , msgType :: !MessageType+ , msgVersion :: !Int+ , msgFlags :: !Int+ , msgSerial :: !Serial+ , msgFields :: DBusFields+ , msgBodyRaw :: ByteString+ } deriving (Show,Eq) --- | create a new method return message-msgMethodReturn :: Serial -> Body -> Message-msgMethodReturn replySerial body = defaultMessage- { msgType = TypeMethodReturn- , msgFields =- [ FieldReplySerial replySerial- ] ++ if null body then [] else [ FieldSignature $ signatureBody body ]- , msgBody = writeBody body- }+fieldsSetPath :: ObjectPath -> DBusFields -> DBusFields+fieldsSetPath v fields = fields { fieldsPath = Just v } --- | create a new error message-msgError :: ErrorName -> Serial -> Body -> Message-msgError errorName replySerial body = defaultMessage- { msgType = TypeError- , msgFields =- [ FieldErrorName errorName- , FieldReplySerial replySerial- ] ++ if null body then [] else [ FieldSignature $ signatureBody body ]- , msgBody = writeBody body- }+fieldsSetInterface :: Interface -> DBusFields -> DBusFields+fieldsSetInterface v fields = fields { fieldsInterface = Just v } +fieldsSetMember :: Member -> DBusFields -> DBusFields+fieldsSetMember v fields = fields { fieldsMember = Just v }++fieldsSetErrorName :: ErrorName -> DBusFields -> DBusFields+fieldsSetErrorName v fields = fields { fieldsErrorName = Just v }++fieldsSetReplySerial :: Serial -> DBusFields -> DBusFields+fieldsSetReplySerial v fields = fields { fieldsReplySerial = Just v }++fieldsSetDestination :: BusName -> DBusFields -> DBusFields+fieldsSetDestination v fields = fields { fieldsDestination = Just v }++fieldsSetSender :: BusName -> DBusFields -> DBusFields+fieldsSetSender v fields = fields { fieldsSender = Just v }++fieldsSetSignature :: Signature -> DBusFields -> DBusFields+fieldsSetSignature v fields = fields { fieldsSignature = v }++fieldsSetUnixFD :: UnixFD -> DBusFields -> DBusFields+fieldsSetUnixFD v fields = fields { fieldsUnixFD = Just v }++fieldsNew = DBusFields Nothing Nothing Nothing Nothing Nothing Nothing Nothing [] Nothing++fieldsNewWithBody body = fieldsNew { fieldsSignature = if null body then [] else signatureBody body }++messageNew :: MessageType -> Body -> (DBusFields -> DBusFields) -> DBusMessage+messageNew ty body fieldsSetter = DBusMessage+ { msgEndian = LE+ , msgType = ty+ , msgVersion = 1+ , msgFlags = 0+ , msgSerial = 0+ , msgFields = fieldsSetter $ fieldsNewWithBody body+ , msgBodyRaw = writeBody body+ }++messageMapFields :: (DBusFields -> DBusFields) -> DBusMessage -> DBusMessage+messageMapFields f msg = msg { msgFields = f $ msgFields msg }++headerFromMessage :: DBusMessage -> DBusHeader+headerFromMessage msg = DBusHeader+ { headerEndian = msgEndian msg+ , headerMessageType = msgType msg+ , headerVersion = msgVersion msg+ , headerFlags = msgFlags msg+ , headerBodyLength = 0+ , headerSerial = msgSerial msg+ , headerFieldsLength = 0+ }++messageFromHeader :: DBusHeader -> DBusMessage+messageFromHeader hdr = DBusMessage+ { msgEndian = headerEndian hdr+ , msgType = headerMessageType hdr+ , msgVersion = headerVersion hdr+ , msgFlags = headerFlags hdr+ , msgSerial = headerSerial hdr+ , msgFields = fieldsNew+ , msgBodyRaw = B.empty+ }+ -- | unserialize a dbus header (16 bytes)-readHeader :: ByteString -> Header-readHeader b = getWire LE 0 getHeader b- where getHeader = do- e <- getw8- let bswap32 = id -- FIXME- let swapf = if fromIntegral e /= fromEnum 'l' then bswap32 else id- mt <- toEnum . fromIntegral <$> getw8- flags <- fromIntegral <$> getw8- ver <- fromIntegral <$> getw8- blen <- fromIntegral . swapf <$> getw32- serial <- swapf <$> getw32- flen <- fromIntegral . swapf <$> getw32+readHeader :: ByteString -> DBusHeader+readHeader b = getWire endianness 1 getHeader remainingBytes where+ (firstByte,remainingBytes) = B.splitAt 1 b+ endianness = if (fromIntegral $ B.head firstByte) /= fromEnum 'l' then BE else LE+ getHeader = do+ mt <- toEnum . fromIntegral <$> getw8+ flags <- fromIntegral <$> getw8+ ver <- fromIntegral <$> getw8+ blen <- fromIntegral <$> getw32+ serial <- getw32+ flen <- fromIntegral <$> getw32 - return $! Header- { headerEndian = if fromIntegral e /= fromEnum 'l' then BE else LE- , headerMessageType = mt- , headerVersion = ver- , headerFlags = flags- , headerBodyLength = blen- , headerSerial = serial- , headerFieldsLength = flen- }+ return DBusHeader+ { headerEndian = endianness+ , headerMessageType = mt+ , headerVersion = ver+ , headerFlags = flags+ , headerBodyLength = blen+ , headerSerial = serial+ , headerFieldsLength = flen+ } -- | serialize a dbus header-writeHeader :: Header -> ByteString+writeHeader :: DBusHeader -> ByteString writeHeader hdr = putWire [putHeader]- where putHeader = do- putw8 $ fromIntegral $ fromEnum $ if headerEndian hdr == BE then 'b' else 'l'- putw8 $ fromIntegral $ fromEnum $ headerMessageType hdr- putw8 $ fromIntegral $ headerFlags hdr- putw8 $ fromIntegral $ headerVersion hdr- putw32 $ fromIntegral $ headerBodyLength hdr- putw32 $ fromIntegral $ headerSerial hdr- putw32 $ fromIntegral $ headerFieldsLength hdr+ where putHeader = do+ putw8 $ fromIntegral $ fromEnum $ if headerEndian hdr == BE then 'B' else 'l'+ putw8 $ fromIntegral $ fromEnum $ headerMessageType hdr+ putw8 $ fromIntegral $ headerFlags hdr+ putw8 $ fromIntegral $ headerVersion hdr+ putw32 $ fromIntegral $ headerBodyLength hdr+ putw32 $ fromIntegral $ headerSerial hdr+ putw32 $ fromIntegral $ headerFieldsLength hdr -- | unserialize dbus message fields-readFields :: ByteString -> [Field]-readFields b = getWire LE 16 getFields b- where- getFields :: GetWire [Field]- getFields = isWireEmpty >>= \empty -> if empty then return [] else liftM2 (:) getField getFields+readFields :: DBusEndian -> ByteString -> DBusFields+readFields endianness = getWire endianness 16 (getFields fieldsNew)+ where+ getFields :: DBusFields -> GetWire DBusFields+ getFields fields = isWireEmpty >>= \empty -> if empty then return fields else getField fields >>= getFields - getField :: GetWire Field- getField = do- ty <- fromIntegral <$> getw8- signature <- getVariant- when (getSigVal ty /= signature) $ error "field type invalid"- t <- getFieldVal ty- alignRead 8- return t+ getField :: DBusFields -> GetWire DBusFields+ getField fields = do+ ty <- fromIntegral <$> getw8+ signature <- getVariant+ when (getSigVal ty /= signature) $ error "field type invalid"+ setter <- getFieldVal ty+ alignRead 8+ return (setter fields) - getSigVal 1 = SigObjectPath- getSigVal 2 = SigString- getSigVal 3 = SigString- getSigVal 4 = SigString- getSigVal 5 = SigUInt32- getSigVal 6 = SigString- getSigVal 7 = SigString- getSigVal 8 = SigSignature- getSigVal 9 = SigUnixFD- getSigVal n = error ("unknown field: " ++ show n)+ getSigVal 1 = SigObjectPath+ getSigVal 2 = SigString+ getSigVal 3 = SigString+ getSigVal 4 = SigString+ getSigVal 5 = SigUInt32+ getSigVal 6 = SigString+ getSigVal 7 = SigString+ getSigVal 8 = SigSignature+ getSigVal 9 = SigUnixFD+ getSigVal n = error ("unknown field: " ++ show n) - getFieldVal :: Int -> GetWire Field- getFieldVal 1 = FieldPath <$> getObjectPath- getFieldVal 2 = FieldInterface <$> getString- getFieldVal 3 = FieldMember <$> getString- getFieldVal 4 = FieldErrorName <$> getString- getFieldVal 5 = FieldReplySerial <$> getw32- getFieldVal 6 = FieldDestination <$> getString- getFieldVal 7 = FieldSender <$> getString- getFieldVal 8 = FieldSignature <$> getSignature- getFieldVal 9 = FieldUnixFds <$> getw32- getFieldVal n = error ("unknown field: " ++ show n)- + getFieldVal :: Int -> GetWire (DBusFields -> DBusFields)+ getFieldVal 1 = fieldsSetPath <$> getObjectPath+ getFieldVal 2 = fieldsSetInterface . fromString . packedStringToString <$> getString+ getFieldVal 3 = fieldsSetMember . fromString . packedStringToString <$> getString+ getFieldVal 4 = fieldsSetErrorName . fromString . packedStringToString <$> getString+ getFieldVal 5 = fieldsSetReplySerial <$> getw32+ getFieldVal 6 = fieldsSetDestination . fromString . packedStringToString <$> getString+ getFieldVal 7 = fieldsSetSender . fromString . packedStringToString <$> getString+ getFieldVal 8 = fieldsSetSignature <$> getSignature+ getFieldVal 9 = fieldsSetUnixFD <$> getw32+ getFieldVal n = error ("unknown field: " ++ show n)+ -- | serialize dbus message fields -- this doesn't include the necessary padding at the end.-writeFields :: [Field] -> ByteString-writeFields fields = putWire (putFields fields)- where- putFields :: [Field] -> [PutWire]- putFields l = map putField l- putField f = alignWrite 8 >> putw8 (fromIntegral $ fieldVal f) >> putFieldVal f+writeFields :: DBusFields -> ByteString+writeFields fields = putWire . (:[]) $ do+ putField 1 SigObjectPath putObjectPath $ fieldsPath fields+ putField 2 SigString (putUString . unInterface) $ fieldsInterface fields+ putField 3 SigString (putUString . unMember) $ fieldsMember fields+ putField 4 SigString (putUString . unErrorName) $ fieldsErrorName fields+ putField 5 SigUInt32 putw32 $ fieldsReplySerial fields+ putField 6 SigString (putUString . unBusName) $ fieldsDestination fields+ putField 7 SigString (putUString . unBusName) $ fieldsSender fields+ putField 8 SigSignature putSignature $ if null (fieldsSignature fields) then Nothing else Just $ fieldsSignature fields+ putField 9 SigUInt32 putw32 $ fieldsUnixFD fields+ where+ putUString = putString . fromString - putFieldVal :: Field -> PutWire- putFieldVal (FieldPath s) = putVariant SigObjectPath >> putObjectPath s- putFieldVal (FieldInterface s) = putVariant SigString >> putString s- putFieldVal (FieldMember s) = putVariant SigString >> putString s- putFieldVal (FieldErrorName s) = putVariant SigString >> putString s- putFieldVal (FieldReplySerial s) = putVariant SigUInt32 >> putw32 s- putFieldVal (FieldDestination s) = putVariant SigString >> putString s- putFieldVal (FieldSender s) = putVariant SigString >> putString s- putFieldVal (FieldSignature s) = putVariant SigSignature >> putSignature s- putFieldVal (FieldUnixFds _) = putVariant SigUInt32 >> putw32 0+ putField :: Word8 -> Type -> (a -> PutWire) -> Maybe a -> PutWire+ putField _ _ _ Nothing = return ()+ putField w s putter (Just v) =+ alignWrite 8 >> putw8 w >> putVariant s >> putter v -- | serialize body writeBody :: Body -> ByteString-writeBody els = putWire (map putType els)+writeBody els = putWire (map putValue els) signatureBody :: Body -> Signature-signatureBody body = map sigType body+signatureBody = map sigType +-- | process a raw body (byteString) with the specified endianness and signature.+readBodyRaw :: DBusEndian -> Signature -> ByteString -> Body+readBodyRaw endian sig = getWire endian 0 (mapM getValue sig)+ -- | read message's body with a defined signature-readBodyWith :: Message -> Signature -> Body-readBodyWith m sigs = getWire (msgEndian m) 0 (mapM getType sigs) (msgBody m)+readBodyWith :: DBusMessage -> Signature -> Body+readBodyWith m sigs = readBodyRaw (msgEndian m) sigs (msgBodyRaw m) -- | read message's body using the signature field as reference-readBody :: Message -> Body-readBody m = readBodyWith m (getFieldSig $ msgFields m)- where- getFieldSig fields = case filter isFieldSignature fields of- [FieldSignature s] -> s- _ -> []-- isFieldSignature (FieldSignature _) = True- isFieldSignature _ = False+readBody :: DBusMessage -> Body+readBody m = readBodyWith m (fieldsSignature $ msgFields m)
+ Network/DBus/MessageType.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module : Network.DBus.MessageType+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+--++module Network.DBus.MessageType+ (+ -- * high level message type+ DBusCall(..)+ , DBusReturn(..)+ , DBusError(..)+ , DBusSignal(..)+ , DBusMessageable(..)+ ) where++import Control.Exception+import Data.Data+import Network.DBus.Message+import Network.DBus.Type++class DBusMessageable a where+ toDBusMessage :: a -> DBusMessage+ fromDBusMessage :: DBusMessage -> Maybe a++data DBusSignal = DBusSignal+ { signalPath :: ObjectPath+ , signalMember :: Member+ , signalInterface :: Interface+ , signalBody :: Body+ } deriving (Show,Eq)++data DBusCall = DBusCall+ { callPath :: ObjectPath+ , callMember :: Member+ , callInterface :: Maybe Interface+ , callBody :: Body+ } deriving (Show,Eq)++data DBusReturn = DBusReturn+ { returnReplySerial :: Serial+ , returnBody :: Body+ } deriving (Show,Eq)++data DBusError = DBusError+ { errorReplySerial :: Serial+ , errorName :: ErrorName+ , errorBody :: Body+ } deriving (Show,Eq,Data,Typeable)++instance Exception DBusError++instance DBusMessageable DBusCall where+ toDBusMessage call = messageNew TypeMethodCall (callBody call) $+ (fieldsSetPath (callPath call) .+ maybe id fieldsSetInterface (callInterface call) .+ fieldsSetMember (callMember call))+ fromDBusMessage msg@(msgFields -> fields) =+ case (fieldsPath fields, fieldsMember fields) of+ (Just path, Just member) -> Just $ DBusCall path member (fieldsInterface fields) (readBody msg)+ _ -> Nothing++instance DBusMessageable DBusSignal where+ toDBusMessage signal = messageNew TypeSignal (signalBody signal) $+ (fieldsSetPath (signalPath signal) .+ fieldsSetInterface (signalInterface signal) .+ fieldsSetMember (signalMember signal))+ fromDBusMessage msg@(msgFields -> fields) =+ case (fieldsPath fields, fieldsMember fields, fieldsInterface fields) of+ (Just path, Just member, Just intf) -> Just $ DBusSignal path member intf (readBody msg)+ _ -> Nothing++instance DBusMessageable DBusReturn where+ toDBusMessage r = messageNew TypeMethodReturn (returnBody r) $+ fieldsSetReplySerial (returnReplySerial r)+ fromDBusMessage msg@(msgFields -> fields) =+ case fieldsReplySerial fields of+ Just rserial -> Just $ DBusReturn rserial (readBody msg)+ _ -> Nothing++instance DBusMessageable DBusError where+ toDBusMessage e = messageNew TypeError (errorBody e) $+ (fieldsSetReplySerial (errorReplySerial e)+ . fieldsSetErrorName (errorName e))+ fromDBusMessage msg@(msgFields -> fields) =+ case (fieldsReplySerial fields, fieldsErrorName fields) of+ (Just rserial, Just errname) -> Just $ DBusError rserial errname (readBody msg)+ _ -> Nothing
Network/DBus/Signature.hs view
@@ -1,12 +1,22 @@+{-# LANGUAGE DeriveDataTypeable #-}+-- |+-- Module : Network.DBus.Signature+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+-- module Network.DBus.Signature- ( Signature- , SignatureElem(..)- -- * serialization- , serializeSignature- , unserializeSignature- ) where+ ( Signature+ , SignatureElem+ , Type(..)+ -- * serialization+ , serializeSignature+ , unserializeSignature+ ) where import Data.Char (chr, ord)+import Data.Data import Data.Serialize.Get import Data.ByteString (ByteString) import qualified Data.ByteString as B@@ -14,33 +24,36 @@ import Control.Applicative ((<$>)) -- | One possible signature element-data SignatureElem =- SigByte- | SigBool- | SigInt16- | SigUInt16- | SigInt32- | SigUInt32- | SigInt64- | SigUInt64- | SigDouble- | SigString- | SigObjectPath- | SigSignature- | SigArray SignatureElem- | SigStruct [SignatureElem]- | SigVariant- | SigDict SignatureElem SignatureElem- | SigUnixFD- deriving (Show,Eq)+data Type =+ SigByte+ | SigBool+ | SigInt16+ | SigUInt16+ | SigInt32+ | SigUInt32+ | SigInt64+ | SigUInt64+ | SigDouble+ | SigString+ | SigObjectPath+ | SigSignature+ | SigArray Type+ | SigStruct [Type]+ | SigVariant+ | SigDict Type Type+ | SigUnixFD+ deriving (Show,Eq,Data,Typeable) +{-# DEPRECATED SignatureElem "use Type instead" #-}+type SignatureElem = Type+ -- | A list of signature element-type Signature = [SignatureElem]+type Signature = [Type] marshallString :: String -> ByteString marshallString = B.pack . map (fromIntegral . ord) -marshallSignatureElem :: SignatureElem -> ByteString+marshallSignatureElem :: Type -> ByteString marshallSignatureElem SigByte = marshallString "y" marshallSignatureElem SigBool = marshallString "b" marshallSignatureElem SigInt16 = marshallString "n"@@ -68,51 +81,52 @@ -- | unserialize a signature unserializeSignature :: ByteString -> Either String Signature unserializeSignature = runGet loop where- loop :: Get Signature- loop = do- o <- getOneNoCollection- r <- remaining- if r > 0 then liftM (o :) loop else return [o]- getOneNoCollection = getOne >>= \o -> case o of- Left _ -> error "parsing error, end of collection not in a collection"- Right z -> return z- getOne :: Get (Either SigStop SignatureElem)- getOne = do- t <- chr . fromIntegral <$> getWord8 - case t of- ')' -> return $ Left StopStruct- '}' -> return $ Left StopDict- 'y' -> return $ Right SigByte- 'b' -> return $ Right SigBool- 'n' -> return $ Right SigInt16- 'q' -> return $ Right SigUInt16- 'i' -> return $ Right SigInt32- 'u' -> return $ Right SigUInt32- 'x' -> return $ Right SigInt64- 't' -> return $ Right SigUInt64- 'd' -> return $ Right SigDouble- 's' -> return $ Right SigString- 'o' -> return $ Right SigObjectPath- 'g' -> return $ Right SigSignature- 'a' -> Right <$> getArray- '(' -> Right <$> getStruct- 'v' -> return $ Right SigVariant- '{' -> Right <$> getDict- 'h' -> return $ Right SigUnixFD- _ -> error ("unknown signature element: " ++ (show $ ord t))- getArray = SigArray <$> getOneNoCollection- getDict = do- l <- loopTill StopDict- case l of- [k,v] -> return $ SigDict k v- _ -> error "dictionary wrong size"- getStruct = do- l <- loopTill StopStruct- case l of- [] -> error "structure empty"- _ -> return $ SigStruct l- loopTill stop = do- o <- getOne- case o of- Left s -> if stop == s then return [] else error "collection not terminated properly"- Right z -> liftM (z :) (loopTill stop)+ loop :: Get Signature+ loop = do+ r <- remaining+ if r > 0+ then getOneNoCollection >>= \o -> liftM (o :) loop+ else return []+ getOneNoCollection = getOne >>= \o -> case o of+ Left _ -> error "parsing error, end of collection not in a collection"+ Right z -> return z+ getOne :: Get (Either SigStop SignatureElem)+ getOne = do+ t <- chr . fromIntegral <$> getWord8 + case t of+ ')' -> return $ Left StopStruct+ '}' -> return $ Left StopDict+ 'y' -> return $ Right SigByte+ 'b' -> return $ Right SigBool+ 'n' -> return $ Right SigInt16+ 'q' -> return $ Right SigUInt16+ 'i' -> return $ Right SigInt32+ 'u' -> return $ Right SigUInt32+ 'x' -> return $ Right SigInt64+ 't' -> return $ Right SigUInt64+ 'd' -> return $ Right SigDouble+ 's' -> return $ Right SigString+ 'o' -> return $ Right SigObjectPath+ 'g' -> return $ Right SigSignature+ 'a' -> Right <$> getArray+ '(' -> Right <$> getStruct+ 'v' -> return $ Right SigVariant+ '{' -> Right <$> getDict+ 'h' -> return $ Right SigUnixFD+ _ -> error ("unknown signature element: " ++ (show $ ord t))+ getArray = SigArray <$> getOneNoCollection+ getDict = do+ l <- loopTill StopDict+ case l of+ [k,v] -> return $ SigDict k v+ _ -> error "dictionary wrong size"+ getStruct = do+ l <- loopTill StopStruct+ case l of+ [] -> error "structure empty"+ _ -> return $ SigStruct l+ loopTill stop = do+ o <- getOne+ case o of+ Left s -> if stop == s then return [] else error "collection not terminated properly"+ Right z -> liftM (z :) (loopTill stop)
+ Network/DBus/StdMessage.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-}+-- |+-- Module : Network.DBus.StdMessage+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+--+module Network.DBus.StdMessage+ (+ -- * dbus server standard interface+ dbusDestination+ , dbusPath+ , dbusInterface+ -- * dbus standard message+ , msgDBusHello+ , msgDBusListNames+ , msgDBusAddMatch+ ) where++import Network.DBus.Message+import Network.DBus.MessageType+import Network.DBus.Type+import Data.String++dbusDestination :: BusName+dbusDestination = "org.freedesktop.DBus"+dbusPath = "/org/freedesktop/DBus"+dbusInterface = "org.freedesktop.DBus"++msgDBusHello = DBusCall dbusPath "Hello" (Just dbusInterface) []+msgDBusListNames = DBusCall dbusPath "ListNames" (Just dbusInterface) []++msgDBusAddMatch matchingRule = DBusCall dbusPath "AddMatch" (Just dbusInterface) [ DBusString $ fromString matchingRule ]
Network/DBus/Type.hs view
@@ -1,67 +1,116 @@ {-# LANGUAGE Rank2Types #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+-- |+-- Module : Network.DBus.Type+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+-- module Network.DBus.Type- (- ObjectPath- , DbusType(..)- , putType- , getType- , sigType- ) where+ ( ObjectPath+ , DBusValue(..)+ , DBusTypeable(..)+ , putValue+ , getValue+ , sigType+ ) where import Data.Word+import Data.Data import Data.Int-import Data.ByteString (ByteString)+import Data.String+import qualified Data.ByteString as B import Network.DBus.Wire import Network.DBus.Signature+import Network.DBus.Internal import qualified Network.DBus.IEEE754 as IEEE754 import Control.Applicative ((<$>))+import Control.Monad (liftM) --- | DBus ObjectPath-type ObjectPath = ByteString+-- | DBus Types+data DBusValue =+ DBusByte Word8+ | DBusBoolean Bool+ | DBusInt16 Int16+ | DBusUInt16 Word16+ | DBusInt32 Int32+ | DBusUInt32 Word32+ | DBusInt64 Int64+ | DBusUInt64 Word64+ | DBusDouble Double+ | DBusString PackedString+ | DBusObjectPath ObjectPath+ | DBusSignature Signature+ | DBusByteArray B.ByteString -- special case of the DBusArray+ | DBusArray Type [DBusValue]+ | DBusStruct Signature [DBusValue]+ | DBusDict DBusValue DBusValue+ | DBusVariant DBusValue+ | DBusUnixFD Word32+ deriving (Show,Eq,Data,Typeable) --- | Dbus Types-data DbusType =- DbusByte Word8- | DbusBoolean Bool- | DbusInt16 Int16- | DbusUInt16 Word16- | DbusInt32 Int32- | DbusUInt32 Word32- | DbusInt64 Int64- | DbusUInt64 Word64- | DbusDouble Double- | DbusString ByteString- | DbusObjectPath ObjectPath- | DbusSignature Signature- | DbusArray SignatureElem [DbusType]- | DbusStruct Signature [DbusType]- | DbusDict DbusType DbusType- | DbusVariant DbusType- | DbusUnixFD Word32- deriving (Show,Eq)+class DBusTypeable a where+ toSignature :: a -> Type+ toDBusValue :: a -> DBusValue+ fromDBusValue :: DBusValue -> Maybe a +#define SIMPLE_DBUS_INSTANCE(hsType,constructor) \+ instance DBusTypeable hsType where \+ { toSignature = sigType . constructor \+ ; toDBusValue = constructor \+ ; fromDBusValue (constructor x) = Just x \+ ; fromDBusValue _ = Nothing \+ }++instance DBusTypeable DBusValue where+ toSignature = sigType+ toDBusValue = id+ fromDBusValue = Just++SIMPLE_DBUS_INSTANCE(Word8, DBusByte)+SIMPLE_DBUS_INSTANCE(Bool, DBusBoolean)+SIMPLE_DBUS_INSTANCE(Int16, DBusInt16)+SIMPLE_DBUS_INSTANCE(Word16, DBusUInt16)+SIMPLE_DBUS_INSTANCE(Int32, DBusInt32)+SIMPLE_DBUS_INSTANCE(Word32, DBusUInt32)+SIMPLE_DBUS_INSTANCE(Int64, DBusInt64)+SIMPLE_DBUS_INSTANCE(Word64, DBusUInt64)+SIMPLE_DBUS_INSTANCE(Double, DBusDouble)+SIMPLE_DBUS_INSTANCE(ObjectPath, DBusObjectPath)++instance DBusTypeable String where+ toSignature _ = SigString+ toDBusValue = DBusString . fromString+ fromDBusValue (DBusString s) = Just (show s)+ fromDBusValue _ = Nothing+ -- | return signature element of a dbus type-sigType :: DbusType -> SignatureElem-sigType (DbusByte _) = SigByte-sigType (DbusBoolean _) = SigBool-sigType (DbusInt16 _) = SigInt16-sigType (DbusUInt16 _) = SigUInt16-sigType (DbusInt32 _) = SigInt32-sigType (DbusUInt32 _) = SigUInt32-sigType (DbusInt64 _) = SigInt64-sigType (DbusUInt64 _) = SigUInt64-sigType (DbusDouble _) = SigDouble-sigType (DbusString _) = SigString-sigType (DbusObjectPath _) = SigObjectPath-sigType (DbusSignature _) = SigSignature-sigType (DbusStruct s _) = SigStruct s-sigType (DbusVariant _) = SigVariant-sigType (DbusArray s _) = SigArray s-sigType (DbusDict k v) = SigDict (sigType k) (sigType v)-sigType (DbusUnixFD _) = SigUnixFD+sigType :: DBusValue -> Type+sigType (DBusByte _) = SigByte+sigType (DBusBoolean _) = SigBool+sigType (DBusInt16 _) = SigInt16+sigType (DBusUInt16 _) = SigUInt16+sigType (DBusInt32 _) = SigInt32+sigType (DBusUInt32 _) = SigUInt32+sigType (DBusInt64 _) = SigInt64+sigType (DBusUInt64 _) = SigUInt64+sigType (DBusDouble _) = SigDouble+sigType (DBusString _) = SigString+sigType (DBusObjectPath _) = SigObjectPath+sigType (DBusSignature _) = SigSignature+sigType (DBusStruct s _) = SigStruct s+sigType (DBusVariant _) = SigVariant+sigType (DBusByteArray _) = SigArray SigByte+sigType (DBusArray s _) = SigArray s+sigType (DBusDict k v) = SigDict (sigType k) (sigType v)+sigType (DBusUnixFD _) = SigUnixFD -- | return the alignement required for a specific signature element.-alignSigElement :: SignatureElem -> Int+alignSigElement :: Type -> Int alignSigElement SigByte = 1 alignSigElement SigBool = 1 alignSigElement SigInt16 = 2@@ -81,50 +130,61 @@ alignSigElement SigUnixFD = 4 -- | serialize a dbus type-putType :: DbusType -> PutWire-putType (DbusByte w) = putw8 w-putType (DbusBoolean b) = putw32 (if b then 1 else 0)-putType (DbusInt16 i) = putw16 $ fromIntegral i-putType (DbusUInt16 w) = putw16 w-putType (DbusInt32 i) = putw32 $ fromIntegral i-putType (DbusUInt32 w) = putw32 w-putType (DbusInt64 i) = putw64 $ fromIntegral i-putType (DbusUInt64 w) = putw64 w-putType (DbusDouble d) = putw64 $ IEEE754.encode d-putType (DbusString s) = putString s-putType (DbusObjectPath s) = putString s-putType (DbusSignature s) = putSignature s-putType (DbusUnixFD fd) = putw32 fd-putType (DbusStruct _ l) = alignWrite 8 >> mapM_ putType l-putType (DbusDict k v) = putType (DbusStruct [] [k,v])-putType (DbusVariant t) = putSignature [sigType t] >> putType t-putType (DbusArray s l) = putw32 (fromIntegral len) >> alignWrite alignElement >> mapM_ putType l- where- len = length l * sizeofElement- sizeofElement = 4- alignElement = alignSigElement s+putValue :: DBusValue -> PutWire+putValue (DBusByte w) = putw8 w+putValue (DBusBoolean b) = putw32 (if b then 1 else 0)+putValue (DBusInt16 i) = putw16 $ fromIntegral i+putValue (DBusUInt16 w) = putw16 w+putValue (DBusInt32 i) = putw32 $ fromIntegral i+putValue (DBusUInt32 w) = putw32 w+putValue (DBusInt64 i) = putw64 $ fromIntegral i+putValue (DBusUInt64 w) = putw64 w+putValue (DBusDouble d) = putw64 $ IEEE754.encode d+putValue (DBusString s) = putString s+putValue (DBusObjectPath s) = putObjectPath s+putValue (DBusSignature s) = putSignature s+putValue (DBusUnixFD fd) = putw32 fd+putValue (DBusStruct _ l) = alignWrite 8 >> mapM_ putValue l+putValue (DBusDict k v) = putValue (DBusStruct [] [k,v])+putValue (DBusVariant t) = putSignature [sigType t] >> putValue t+putValue (DBusByteArray l) =+ putw32 (fromIntegral $ B.length l) >> alignWrite alignElement >> putBytes l+ where alignElement = alignSigElement SigByte+putValue (DBusArray s l) = do+ pos <- putWireGetPosition+ let alignmentStart = pos + alignWriteCalculate 4 pos + 4+ let alignmentEnd = alignmentStart + alignWriteCalculate alignElement alignmentStart+ let content = putWireAt alignmentEnd [mapM_ putValue l]+ putw32 (fromIntegral $ B.length content) >> alignWrite alignElement >> putBytes content+ where+ alignElement = alignSigElement s -- | unserialize a dbus type from a signature Element-getType :: SignatureElem -> GetWire DbusType-getType SigByte = DbusByte <$> getw8-getType SigBool = DbusBoolean . iToB <$> getw32 where iToB i = i == 1-getType SigInt16 = DbusInt16 . fromIntegral <$> getw16-getType SigUInt16 = DbusUInt16 <$> getw16-getType SigInt32 = DbusInt32 . fromIntegral <$> getw32-getType SigUInt32 = DbusUInt32 <$> getw32-getType SigInt64 = DbusInt64 . fromIntegral <$> getw64-getType SigUInt64 = DbusUInt64 <$> getw64-getType SigDouble = DbusDouble . IEEE754.decode <$> getw64-getType SigString = DbusString <$> getString-getType SigObjectPath = DbusObjectPath <$> getObjectPath-getType SigSignature = DbusSignature <$> getSignature-getType (SigDict k v) = getType (SigStruct [k,v])-getType SigUnixFD = DbusUnixFD <$> getw32-getType SigVariant = getVariant >>= getType >>= return . DbusVariant-getType (SigStruct sigs) =- alignRead 8 >> mapM getType sigs >>= return . DbusStruct sigs-getType (SigArray t) = do- len <- getw32- alignRead (alignSigElement t)- l <- getMultiple (fromIntegral len) (getType t)- return $ DbusArray t l+getValue :: Type -> GetWire DBusValue+getValue SigByte = DBusByte <$> getw8+getValue SigBool = DBusBoolean . iToB <$> getw32 where iToB i = i == 1+getValue SigInt16 = DBusInt16 . fromIntegral <$> getw16+getValue SigUInt16 = DBusUInt16 <$> getw16+getValue SigInt32 = DBusInt32 . fromIntegral <$> getw32+getValue SigUInt32 = DBusUInt32 <$> getw32+getValue SigInt64 = DBusInt64 . fromIntegral <$> getw64+getValue SigUInt64 = DBusUInt64 <$> getw64+getValue SigDouble = DBusDouble . IEEE754.decode <$> getw64+getValue SigString = DBusString <$> getString+getValue SigObjectPath = DBusObjectPath <$> getObjectPath+getValue SigSignature = DBusSignature <$> getSignature+getValue (SigDict k v) = do+ alignRead 8+ key <- getValue k+ val <- getValue v+ return $ DBusDict key val+getValue SigUnixFD = DBusUnixFD <$> getw32+getValue SigVariant = liftM DBusVariant (getVariant >>= getValue)+getValue (SigStruct sigs) =+ liftM (DBusStruct sigs) (alignRead 8 >> mapM getValue sigs)+getValue (SigArray t) = do+ len <- getw32+ alignRead (alignSigElement t)+ case t of+ SigByte -> DBusByteArray <$> getBytes (fromIntegral len)+ _ -> DBusArray t <$> getMultiple (fromIntegral len) (getValue t)
Network/DBus/Wire.hs view
@@ -1,38 +1,51 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-}+-- |+-- Module : Network.DBus.Wire+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : unknown+-- module Network.DBus.Wire- ( DbusEndian(..)- -- * getter- , GetWire- , getWire- , isWireEmpty- , alignRead- , getw8- , getw16- , getw32- , getw64- , getString- , getSignature- , getVariant- , getObjectPath- , getMultiple- -- * putter- , PutWire- , putWire- , alignWrite- , putw8- , putw16- , putw32- , putw64- , putString- , putSignature- , putVariant- , putObjectPath- ) where+ ( DBusEndian(..)+ -- * getter+ , GetWire+ , getWire+ , isWireEmpty+ , alignRead+ , getw8+ , getw16+ , getw32+ , getw64+ , getString+ , getSignature+ , getVariant+ , getObjectPath+ , getBytes+ , getMultiple+ -- * putter+ , PutWire+ , putWire+ , putWireAt+ , putWireGetPosition+ , putBytes+ , alignWrite+ , alignWriteCalculate+ , putw8+ , putw16+ , putw32+ , putw64+ , putString+ , putSignature+ , putVariant+ , putObjectPath+ ) where import Data.Word import Data.Bits-import Data.Binary.Get+import Data.Binary.Get hiding (getBytes) import Data.ByteString (ByteString)+import Data.String import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L @@ -40,14 +53,15 @@ import Control.Monad.Reader import Control.Monad.State import Network.DBus.Signature+import Network.DBus.Internal -data DbusEndian = LE | BE deriving (Show,Eq)-type DbusGet = (DbusEndian, Int)+data DBusEndian = LE | BE deriving (Show,Eq)+type DBusGet = (DBusEndian, Int) -- Specified endianness and alignment of this context. -newtype GetWire a = GetWire { runGW :: ReaderT DbusGet Get a }- deriving (Monad, MonadReader DbusGet, Functor)+newtype GetWire a = GetWire { runGW :: ReaderT DBusGet Get a }+ deriving (Monad, MonadReader DBusGet, Functor) -getWire :: DbusEndian -> Int -> GetWire a -> ByteString -> a+getWire :: DBusEndian -> Int -> GetWire a -> ByteString -> a getWire endian align f b = runGet (runReaderT (runGW f) (endian,align)) (L.fromChunks [b]) liftGet :: Get a -> GetWire a@@ -61,11 +75,11 @@ alignRead :: Int -> GetWire () alignRead n = do- (_, start) <- ask- br <- liftGet (fromIntegral <$> bytesRead)- case (br + start) `mod` n of- 0 -> return ()- i -> liftGet (skip $ n - i)+ (_, start) <- ask+ br <- liftGet (fromIntegral <$> bytesRead)+ case (br + start) `mod` n of+ 0 -> return ()+ i -> liftGet (skip $ n - i) getw8 :: GetWire Word8 getw8 = liftGet getWord8@@ -79,110 +93,120 @@ getw64 :: GetWire Word64 getw64 = alignRead 8 >> onEndian (liftGet getWord64le) (liftGet getWord64be) -getSignatureOne :: GetWire SignatureElem+getSignatureOne :: GetWire Type getSignatureOne = do- sigs <- getSignature- case sigs of- [s] -> return s- _ -> error "one signature with wrong format"+ sigs <- getSignature+ case sigs of+ [s] -> return s+ _ -> error "one signature with wrong format" getSignature :: GetWire Signature getSignature = do- len <- fromIntegral <$> getw8- sigBS <- liftGet $ getByteString len- _ <- getw8- case unserializeSignature sigBS of- Left err -> error err- Right sig -> return sig+ len <- fromIntegral <$> getw8+ sigBS <- liftGet $ getByteString len+ _ <- getw8+ case unserializeSignature sigBS of+ Left err -> error err+ Right sig -> return sig -getVariant :: GetWire SignatureElem+getVariant :: GetWire Type getVariant = getSignatureOne -getString :: GetWire ByteString+getBytes = liftGet . getByteString++getString :: GetWire PackedString getString = do- nbBytes <- fromIntegral <$> getw32- s <- liftGet $ getByteString nbBytes- _ <- getw8- return s+ nbBytes <- fromIntegral <$> getw32+ s <- liftGet $ getByteString nbBytes+ _ <- getw8+ return $ PackedString s -getObjectPath :: GetWire ByteString-getObjectPath = getString+getObjectPath :: GetWire ObjectPath+getObjectPath = ObjectPath . packedStringToString <$> getString getMultiple :: Show a => Int -> GetWire a -> GetWire [a] getMultiple 0 _ = return [] getMultiple n f = do- r1 <- liftGet remaining- a <- f- r2 <- liftGet remaining- let r = fromIntegral (r1-r2)- liftM (a :) (getMultiple (n-r) f)+ r1 <- liftGet remaining+ a <- f+ r2 <- liftGet remaining+ let r = fromIntegral (r1-r2)+ liftM (a :) (getMultiple (n-r) f) type PutWireM a = State (Int, [ByteString]) a type PutWire = PutWireM () +putWireGetPosition :: PutWireM Int+putWireGetPosition = gets fst++putWireAt :: Int -> [PutWire] -> ByteString+putWireAt i f = B.concat $ reverse $ snd $ execState (sequence_ f) (i, [])+ putWire :: [PutWire] -> ByteString-putWire f = B.concat $ reverse $ snd $ execState (sequence_ f) (0, [])+putWire = putWireAt 0 putBytes :: ByteString -> PutWire putBytes s = modify (\(i, l) -> (i + B.length s, s : l)) +alignWriteCalculate :: Int -> Int -> Int+alignWriteCalculate n pos = negMod $ pos `mod` n+ where+ negMod 0 = 0+ negMod x = n - x+ alignWrite :: Int -> PutWire-alignWrite n = do- i <- fst <$> get- case i `mod` n of- 0 -> return ()- x -> putBytes $ B.replicate (n - x) 0+alignWrite n = gets (alignWriteCalculate n . fst) >>= \l -> putBytes $ B.replicate l 0 putw8 :: Word8 -> PutWire putw8 = putBytes . B.singleton putw16 :: Word16 -> PutWire putw16 w = alignWrite 2 >> putBytes (B.pack le)- where- le = [p2,p1]- be = [p1,p2]- p1 = fromIntegral $ w `shiftR` 8- p2 = fromIntegral w+ where+ le = [p2,p1]+ --be = [p1,p2]+ p1 = fromIntegral $ w `shiftR` 8+ p2 = fromIntegral w putw32 :: Word32 -> PutWire putw32 w = alignWrite 4 >> putBytes (B.pack le)- where- le = [p4,p3,p2,p1]- be = [p1,p2,p3,p4]- p1 = fromIntegral $ w `shiftR` 24- p2 = fromIntegral $ w `shiftR` 16- p3 = fromIntegral $ w `shiftR` 8- p4 = fromIntegral w+ where+ le = [p4,p3,p2,p1]+ --be = [p1,p2,p3,p4]+ p1 = fromIntegral $ w `shiftR` 24+ p2 = fromIntegral $ w `shiftR` 16+ p3 = fromIntegral $ w `shiftR` 8+ p4 = fromIntegral w putw64 :: Word64 -> PutWire putw64 w = alignWrite 8 >> putBytes (B.pack le)- where- le = [p8,p7,p6,p5,p4,p3,p2,p1]- be = [p1,p2,p3,p4,p5,p6,p7,p8]- p1 = fromIntegral $ w `shiftR` 56- p2 = fromIntegral $ w `shiftR` 48- p3 = fromIntegral $ w `shiftR` 40- p4 = fromIntegral $ w `shiftR` 32- p5 = fromIntegral $ w `shiftR` 24- p6 = fromIntegral $ w `shiftR` 16- p7 = fromIntegral $ w `shiftR` 8- p8 = fromIntegral w+ where+ le = [p8,p7,p6,p5,p4,p3,p2,p1]+ --be = [p1,p2,p3,p4,p5,p6,p7,p8]+ p1 = fromIntegral $ w `shiftR` 56+ p2 = fromIntegral $ w `shiftR` 48+ p3 = fromIntegral $ w `shiftR` 40+ p4 = fromIntegral $ w `shiftR` 32+ p5 = fromIntegral $ w `shiftR` 24+ p6 = fromIntegral $ w `shiftR` 16+ p7 = fromIntegral $ w `shiftR` 8+ p8 = fromIntegral w -putString :: ByteString -> PutWire-putString b = do- putw32 (fromIntegral $ B.length b)- putBytes b- putw8 0+putString :: PackedString -> PutWire+putString (PackedString b) = do+ putw32 (fromIntegral $ B.length b)+ putBytes b+ putw8 0 putSignature :: Signature -> PutWire putSignature sig = do- putw8 (fromIntegral $ B.length b)- putBytes b- putw8 0- where b = serializeSignature sig+ putw8 (fromIntegral $ B.length b)+ putBytes b+ putw8 0+ where b = serializeSignature sig -putVariant :: SignatureElem -> PutWire+putVariant :: Type -> PutWire putVariant = putSignature . (:[]) -putObjectPath :: ByteString -> PutWire-putObjectPath = putString+putObjectPath :: ObjectPath -> PutWire+putObjectPath = putString . fromString . unObjectPath
README.md view
@@ -2,33 +2,33 @@ ======== The udbus haskell package provides a BSD implementation of the DBus protocol in-pure Haskell, giving access to a simple lowlevel interface. The interfaces are-coherent and basic, sacrificing some ease of use (in using more appropriate-haskell types for dbus array for example) in favor of being able to write generated-code easily.+pure Haskell, giving access to a simple lowlevel (Network.DBus.Actions) interface+and a simple high level interface with a simple mainloop dispatcher (Network.DBus) +The interfaces are coherent and basic, sacrificing some ease of use (in using+more appropriate haskell types for dbus array for example) in favor of being+able to write generated code easily.+ The code doesn't check if the object values (objectpath, interface, etc) are appropriates on sending, nor on receiving. The dbus daemon should have prevented invalid values to be received, and for sending, the user need to take extra care of the values sent. -Dbus startup negotiation need to be done properly by the user, that includes-sending the hello method call at startup (see Example).- TODO & Caveats -------------- -Dbus endianness is not handle properly to serialize message if the user choose-big endian. User should always select little endian regardless of the actual-machine architecture. However, receiving message in both endianness is supported.+User should always select little endian regardless of the actual machine+architecture when creating message. However, receiving message in both+endianness is supported. -Double are expected to be handled as IEEE754 by the compiler. Serialization of double-is handled in a GHC specific way (making the package GHC specific), expecting-that any architecture represent double the IEEE754 way. So far, this seems to be-true for every architecture supported by GHC.+Double are expected to be handled as IEEE754 by the compiler. Serialization of+double is handled in a GHC specific way (making the package GHC specific),+expecting that any architecture represent double the IEEE754 way. So far, this+seems to be true for every architecture supported by GHC. Stability ---------+ While the low level interfaces should remains the same, it's not unexpected that some high level interface would be grafted on top of the actual interfaces.
+ Tests.hs view
@@ -0,0 +1,101 @@+import Text.Printf+import Data.String+import Test.QuickCheck+import Test.Framework (defaultMain, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import qualified Data.ByteString as B+import Network.DBus.Type+import Network.DBus.Signature+import Network.DBus.Internal+import Network.DBus.Message+import Network.DBus.Wire+import Control.Monad+import Control.Applicative ((<$>))++-----------------------------------------------------------------------------------+genSimpleSig =+ [ return SigByte+ , return SigBool+ , return SigInt16+ , return SigUInt16+ , return SigInt32+ , return SigUInt32+ , return SigInt64+ , return SigUInt64+ , return SigDouble+ , return SigString+ , return SigObjectPath+ , return SigSignature+ , return SigVariant+ ]++genSigElem :: Int -> Gen Type+genSigElem 0 = oneof genSimpleSig+genSigElem n = oneof (genSimpleSig ++ [ liftM SigArray subSig, liftM SigStruct (replicateM 2 subSig) ])+ where+ subSig :: Gen Type+ subSig = genSigElem (n `div` 2)++genSig :: Gen Signature+genSig = replicateM 4 (resize 2 $ sized genSigElem)++-- just there to make it instance of arbitrary without having typesynonym+newtype DSignature = DSignature Signature+ deriving (Eq)++instance Show DSignature where+ show (DSignature x) = show x+instance Arbitrary DSignature where+ arbitrary = DSignature <$> genSig+-----------------------------------------------------------------------------------++instance Arbitrary PackedString where+ arbitrary = fromString <$> arbitrary++instance Arbitrary ObjectPath where+ arbitrary = ObjectPath <$> arbitrary++arbitraryBS = B.pack <$> (choose (1,580) >>= \l -> replicateM l arbitrary)++genBody :: Gen (Signature, [DBusValue])+genBody = genSig >>= \sig -> (mapM sigToBody sig >>= \body ->return (sig,body)) where+ sigToBody SigByte = DBusByte <$> arbitrary+ sigToBody SigBool = DBusBoolean <$> arbitrary+ sigToBody SigInt16 = DBusInt16 <$> arbitrary+ sigToBody SigUInt16 = DBusUInt16 <$> arbitrary+ sigToBody SigInt32 = DBusInt32 <$> arbitrary+ sigToBody SigUInt32 = DBusUInt32 <$> arbitrary+ sigToBody SigInt64 = DBusInt64 <$> arbitrary+ sigToBody SigUInt64 = DBusUInt64 <$> arbitrary+ sigToBody SigDouble = DBusDouble <$> arbitrary+ sigToBody SigObjectPath = DBusObjectPath <$> arbitrary+ sigToBody SigString = DBusString <$> arbitrary+ sigToBody SigVariant = sized genSigElem >>= sigToBody >>= return . DBusVariant+ sigToBody SigSignature = DBusSignature <$> genSig+ sigToBody (SigStruct sigs) = mapM sigToBody sigs >>= return . DBusStruct sigs+ sigToBody (SigArray sig)+ | sig == SigByte = DBusByteArray <$> arbitraryBS+ | otherwise = mapM sigToBody (replicate 3 sig) >>= return . DBusArray sig+ sigToBody _ = DBusString <$> arbitrary++data BodyContent = BodyContent Signature [DBusValue]+ deriving (Show,Eq)+instance Arbitrary BodyContent where+ arbitrary = genBody >>= \(sig, val) -> return $ BodyContent sig val+-----------------------------------------------------------------------------------++property_signature_marshalling (DSignature x) = (unserializeSignature $ serializeSignature x) == Right x+property_body_marshalling (BodyContent sig c) = (readBodyRaw LE sig $ writeBody c) `assertEq` c++assertEq :: (Show a, Eq a) => a -> a -> Bool+assertEq a b+ | a == b = True+ | otherwise = error ("equality failed:\ngot: " ++ show a ++ "\nexpected " ++ show b)++main = defaultMain tests where+ tests = [testMarshalling]+ testMarshalling = testGroup "Marshalling"+ [ testProperty "Signature" property_signature_marshalling+ , testProperty "Values" property_body_marshalling+ ]
udbus.cabal view
@@ -1,5 +1,5 @@ Name: udbus-Version: 0.1.1+Version: 0.2.0 Description: Small and flexible implementation of the dbus protocol. License: BSD3 License-file: LICENSE@@ -24,20 +24,26 @@ Library Build-Depends: base >= 3 && < 5- , cereal >= 0.3.0 , binary+ , cereal , bytestring+ , utf8-string , network+ , containers , mtl , unix , ghc-prim Exposed-modules: Network.DBus+ Network.DBus.Actions Other-modules: Network.DBus.IEEE754 Network.DBus.Message+ Network.DBus.MessageType+ Network.DBus.StdMessage+ Network.DBus.Internal Network.DBus.Signature Network.DBus.Type Network.DBus.Wire- ghc-options: -Wall+ ghc-options: -Wall -fno-warn-missing-signatures Executable dbus Main-is: DBus.hs@@ -46,6 +52,19 @@ Build-Depends: network else Buildable: False++executable Tests+ Main-is: Tests.hs+ if flag(test)+ Buildable: True+ Build-Depends: base >= 3 && < 5+ , QuickCheck >= 2+ , test-framework+ , test-framework-quickcheck2+ , bytestring+ else+ Buildable: False+ ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures source-repository head type: git