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 System (getArgs) 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 -> [String] -> B.ByteString 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 -- make a notification notificationPacket :: String -> String -> String -> String -> B.ByteString notificationPacket appName notification title description = runPut $ do putWord8 growl_protocol_version putWord8 growl_type_notification putWord16be 0 putLength notification putLength title putLength description putLength appName putString notification putString title putString description putString appName where putLength = putWord16be . fromIntegral . length addMD5Sum :: String -> B.ByteString -> B.ByteString addMD5Sum password message = B.append message s where s = B.pack $ hash $ map fromIntegral $ B.unpack $ B.append message bspassword bspassword = makeBS password main = do args <- getArgs (server,title, message) <- case args of server:title:message -> return $ (server, title, message) _ -> error "Usage: server title message" let b = registrationPacket "growlnotify" ["Command-Line Growl Notification"] let m = addMD5Sum "None" b let b2 = notificationPacket "growlnotify" "Command-Line Growl Notification" title (unwords message) let m2 = addMD5Sum "None" b2 doit server m doit server m2 where port = 9887 doit server m = do let d = map (Char.chr . fromIntegral) $ B.unpack m s <- socket AF_INET Datagram 0 write server s d sClose s write :: String -> Socket -> String -> IO () write server sock s = do addr <- inet_addr server sendTo sock s (SockAddrInet (fromIntegral port) addr) return ()