GrowlNotify-0.3: Network/GrowlNotify.hs
-- | A network notification utility to work with Growl <http://growl.info/>.
--
-- An application must register itself by sending a registrationPacket. Then, to send
-- a notification, send a notificationPacket. Any packet must have an md5sum tacked on
-- to the end before being sent.
module Network.GrowlNotify
( registrationPacket
, notificationPacket
, addMD5Sum
, sendMessage
)
where
import Data.Word
import Data.Binary.Put
import qualified Data.ByteString.Lazy as B
import qualified Char
import Data.Digest.MD5
import Network.Socket
import Network.BSD
growl_udp_port=9887
growl_protocol_version=1
growl_type_registration=0
growl_type_notification=1
makeBS s = B.pack $ map (fromIntegral . Char.ord) s
putString :: String -> Put
putString = do putLazyByteString . makeBS
-- | register this application and its notification with growl
-- | any notifications will be on by default
registrationPacket :: String -- ^ The name of this application
-> [String] -- ^ A list of notifications
-> B.ByteString -- ^ The packet
registrationPacket appName notiNames = runPut $ do
putWord8 growl_protocol_version
putWord8 growl_type_registration
putWord16be $ fromIntegral $ length appName
putWord8 $ fromIntegral $ lengthnames
putWord8 $ fromIntegral $ lengthnames
putString appName
mapM_ addNotification notiNames
mapM_ putWord8 [0..(fromIntegral lengthnames)-1]
where
addNotification :: String -> Put
addNotification s = do
putWord16be $ fromIntegral $ length s
putLazyByteString $ makeBS s
lengthnames = length notiNames
-- | Create a notification packet
notificationPacket :: Bool -- ^ True iff the notification should be sticky
-> String -- ^ The name of this application
-> String -- ^ The notification name
-> String -- ^ The title of this notification
-> String -- ^ The notification text
-> B.ByteString -- ^ The packet
notificationPacket sticky appName notification title description = runPut $ do
putWord8 growl_protocol_version
putWord8 growl_type_notification
putWord16be $ if sticky then 1 else 0
putLength notification
putLength title
putLength description
putLength appName
putString notification
putString title
putString description
putString appName
where
putLength = putWord16be . fromIntegral . length
-- | Tack on the md5sum to the end of the message, with a (possibly empty) password
addMD5Sum :: String -- ^ The password
-> B.ByteString -- ^ The packet (from notificationPacket or registrationPacket)
-> B.ByteString -- ^ The message with appended md5sum, ready for sendMessage
addMD5Sum password message = B.append message s
where
s = B.pack $ hash $ map fromIntegral $ B.unpack $ B.append message bspassword
bspassword = makeBS password
-- | Send the packet
sendMessage :: String -- ^ The server (an IP address or hostname)
-> B.ByteString -- ^ The message (with md5sum) to send
-> IO ()
sendMessage server m = do
server' <- getHostByName server
let d = map (Char.chr . fromIntegral) $ B.unpack m
s <- socket AF_INET Datagram 0
write (hostAddress server') s d
sClose s
write :: HostAddress -> Socket -> String -> IO ()
write addr sock s = do
sendTo sock s (SockAddrInet (fromIntegral growl_udp_port) addr)
return ()