2 {-# LANGUAGE OverloadedStrings #-}
4 -- generic AMQP publisher
5 import Control.Concurrent
6 import qualified Control.Exception as X
7 import qualified Data.ByteString.Lazy.Char8 as BL
8 #if MIN_VERSION_hinotify(0,3,10)
9 import qualified Data.ByteString.Char8 as BS
11 import Data.List (isSuffixOf)
13 import qualified Data.Text as T
15 import Data.Time.Clock.POSIX
16 import Data.Version (showVersion)
17 import Data.Word (Word64)
20 import Network.AMQP.Types
21 import Network.AMQP.Utils.Connection
22 import Network.AMQP.Utils.Helpers
23 import Network.AMQP.Utils.Options
24 import Paths_amqp_utils (version)
25 import System.Directory
26 import System.Environment
29 import qualified System.Posix.Files as F
35 args <- getArgs >>= parseargs 'a'
36 printparam "client version" ["amqp-utils", showVersion version]
37 printparam "routing key" $ rKey args
38 printparam "exchange" $ currentExchange args
40 if inputFile args == "-"
42 else F.getFileStatus (inputFile args) >>= return . F.isDirectory
45 printparam "hotfolder" (inputFile args) >>
46 printparam "initial scan" (initialScan args)
54 printparam "remove sent file" (removeSentFile args && isDir)
55 (conn, chan) <- connect args
56 addChannelExceptionHandler chan (X.throwTo tid)
57 printparam "confirm mode" $ confirm args
60 confirmSelect chan False
61 addConfirmationListener chan confirmCallback
63 let publishOneMsg = publishOneMsg' chan args {removeSentFile = removeSentFile args && isDir}
67 setCurrentDirectory (inputFile args)
69 then getDirectoryContents "." >>= mapM_ (\fn -> handleFile publishOneMsg (suffix args) fn)
71 inotify <- initINotify
77 (handleEvent publishOneMsg (suffix args))
78 hr $ "BEGIN watching " ++ (inputFile args)
79 sleepingBeauty >>= printparam "exception"
81 hr $ "END watching " ++ (inputFile args)
85 if inputFile args == "-"
87 else BL.readFile (inputFile args)
89 then mapM_ (publishOneMsg Nothing) (BL.lines messageFile)
90 else publishOneMsg (Just (inputFile args)) messageFile
93 -- all done. wait and close.
95 then waitForConfirms chan >>= printparam "confirmed"
97 X.catch (closeConnection conn) exceptionHandler
99 -- | A handler for clean exit
100 exceptionHandler :: AMQPException -> IO ()
101 exceptionHandler (ChannelClosedException Normal txt) =
102 printparam "exit" txt >> exitWith ExitSuccess
103 exceptionHandler (ConnectionClosedException Normal txt) =
104 printparam "exit" txt >> exitWith ExitSuccess
105 exceptionHandler x = printparam "exception" x >> exitWith (ExitFailure 1)
107 -- | The handler for publisher confirms
108 confirmCallback :: (Word64, Bool, AckType) -> IO ()
109 confirmCallback (deliveryTag, isAll, ackType) =
119 -- | Hotfolder event handler
121 (Maybe String -> BL.ByteString -> IO ())
125 -- just handle closewrite and movedin events
126 #if MIN_VERSION_hinotify(0,3,10)
127 handleEvent func suffixes (Closed False (Just fileName) True) =
128 handleFile func suffixes (BS.unpack fileName)
129 handleEvent func suffixes (MovedIn False fileName _) =
130 handleFile func suffixes (BS.unpack fileName)
132 handleEvent func suffixes (Closed False (Just fileName) True) = handleFile func suffixes fileName
133 handleEvent func suffixes (MovedIn False fileName _) = handleFile func suffixes fileName
135 handleEvent _ _ _ = return ()
137 -- | Hotfolder file handler
139 (Maybe String -> BL.ByteString -> IO ()) -> [String] -> FilePath -> IO ()
140 handleFile _ _ ('.':_) = return () -- ignore hidden files
141 handleFile func suffixes@(_:_) fileName =
142 if any (flip isSuffixOf fileName) suffixes
143 then handleFile func [] fileName
145 handleFile func [] fileName =
147 (BL.readFile fileName >>= func (Just fileName))
148 (\e -> printparam "exception in handleFile" (e :: X.IOException))
150 -- | Publish one message with our settings
151 publishOneMsg' :: Channel -> Args -> Maybe FilePath -> BL.ByteString -> IO ()
152 publishOneMsg' chan a fn content = do
153 printparam "sending" fn
154 (mtype, mencoding) <-
155 if (magic a) && isJust fn
157 m <- magicOpen [MagicMimeType]
159 t <- magicFile m (fromJust fn)
160 magicSetFlags m [MagicMimeEncoding]
161 e <- magicFile m (fromJust fn)
162 return (Just (T.pack t), Just (T.pack e))
163 else return ((contenttype a), (contentencoding a))
164 now <- getCurrentTime >>= return . floor . utcTimeToPOSIXSeconds
167 (T.pack $ currentExchange a)
171 , msgDeliveryMode = persistent a
172 , msgTimestamp = Just now
174 , msgType = msgtype a
175 , msgUserID = userid a
176 , msgApplicationID = appid a
177 , msgClusterID = clusterid a
178 , msgContentType = mtype
179 , msgContentEncoding = mencoding
180 , msgReplyTo = replyto a
181 , msgPriority = prio a
182 , msgCorrelationID = corrid a
183 , msgExpiration = msgexp a
184 , msgHeaders = substheader (fnheader a) fn $ msgheader a
187 removeSentFileIfRequested (removeSentFile a) fn
190 [String] -> Maybe String -> Maybe FieldTable -> Maybe FieldTable
191 substheader (s:r) (Just fname) old =
192 substheader r (Just fname) (addheader old (s ++ "=" ++ fname))
193 substheader _ _ old = old
194 removeSentFileIfRequested False _ = return ()
195 removeSentFileIfRequested True Nothing = return ()
196 removeSentFileIfRequested True (Just fname) = printparam "removing" fname >> removeFile fname