imapfilter convenience wrapper

imapdomo.lua 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. local pkg = {}
  2. ------------------------------------------------------------------------------
  3. -- config and handling --
  4. ------------------------------------------------------------------------------
  5. pkg.load_mailboxes = function()
  6. --
  7. -- Load mailboxes from $IMAPDOMO_CFGDIR/mailboxes.lua
  8. --
  9. local fname = os.getenv("IMAPDOMO_CFGDIR") .. '/mailboxes.lua'
  10. assert(pkg.file_exists(fname))
  11. return dofile(fname)
  12. end
  13. pkg.file_exists = function(name)
  14. --
  15. -- True if file *name* exists
  16. --
  17. local f = io.open(name,"r")
  18. if f ~= nil then
  19. io.close(f)
  20. return true
  21. else
  22. return false
  23. end
  24. end
  25. pkg.do_if_exists = function (filename)
  26. --
  27. -- Do file from home, if exists
  28. --
  29. local file = os.getenv("IMAPFILTER_HOME") .. "/" .. filename
  30. if pkg.file_exists(file) then
  31. dofile(file)
  32. return
  33. end
  34. end
  35. pkg.handle = function()
  36. --
  37. -- Handle action if it's valid
  38. --
  39. local action = os.getenv("IMAPDOMO_ACTION")
  40. local file = os.getenv("IMAPFILTER_HOME") .. "/handlers/" .. action .. ".lua"
  41. if pkg.file_exists(file) then
  42. pkg.do_if_exists("init.lua")
  43. dofile(file)
  44. else
  45. error("no handler for action: " .. action)
  46. return nil
  47. end
  48. end
  49. ------------------------------------------------------------------------------
  50. -- mail getters --
  51. ------------------------------------------------------------------------------
  52. pkg.get_queue = function(acct, mbox, size)
  53. --
  54. -- Get queue from *mbox* from *acct* or nil (no messages)
  55. --
  56. -- If mbox is not specified, "FILTER_QUEUE" is used
  57. --
  58. mbox = mbox or "FILTER_QUEUE"
  59. size = size or 512
  60. local exist = acct[mbox]:check_status()
  61. if exist > 0 then
  62. return pkg.head(size, acct[mbox]:select_all())
  63. end
  64. return nil
  65. end
  66. pkg._notifirc1 = function(subj, from, body)
  67. --
  68. -- notify about message using (pre-configured) notifirc
  69. --
  70. local fd = assert(io.popen('notifirc -c mail -f - "message preview:"', "w"))
  71. local fmt = "> %s\n> %s\n> BODY: %s"
  72. fd:write(fmt:format(subj, from, body))
  73. fd:close()
  74. end
  75. pkg.notifirc_all = function(seq)
  76. --
  77. -- Send notifications about all messages in *seq*
  78. --
  79. for _, mesg in ipairs(seq) do
  80. local mbox, uid, subj, from, body
  81. mbox, uid = table.unpack(mesg)
  82. subj = mbox[uid]:fetch_field('Subject')
  83. from = mbox[uid]:fetch_field('From')
  84. body = mbox[uid]:fetch_body()
  85. pkg._notifirc1(subj, from, body)
  86. end
  87. end
  88. pkg.hook_all = function(seq, hname, ...)
  89. --
  90. -- Call hook hname for all messages in seq
  91. --
  92. local hfile = os.getenv("IMAPFILTER_HOME") .. "/hooks/" .. hname
  93. local hargs = {...}
  94. local hcmd = hfile
  95. local fmt = ' %q'
  96. if pkg.file_exists(hfile) then
  97. for _, harg in pairs(hargs) do
  98. hcmd = hcmd .. fmt:format(harg)
  99. end
  100. for _, mesg in ipairs(seq) do
  101. local mbox, uid, subj, body, from, to, date
  102. mbox, uid = table.unpack(mesg)
  103. subj = mbox[uid]:fetch_field('Subject')
  104. from = mbox[uid]:fetch_field('From')
  105. to = mbox[uid]:fetch_field('To')
  106. date = mbox[uid]:fetch_field('Date')
  107. body = mbox[uid]:fetch_body()
  108. pkg._hook1(hcmd, subj, from, to, date, body)
  109. end
  110. else
  111. error("no such hook: " .. hname)
  112. return nil
  113. end
  114. end
  115. pkg._hook1 = function(hcmd, subj, from, to, date, body)
  116. --
  117. -- push message through hook script
  118. --
  119. local fd = assert(io.popen(hcmd, "w"))
  120. local fmt = "subj=%s\nfrom=%s\nto=%s\ndate=%s\n%s"
  121. fd:write(fmt:format(subj, from, to, date, body))
  122. fd:close()
  123. end
  124. pkg._partinf_compare = function(a, b)
  125. if not type(a) == type(b) then
  126. return false
  127. end
  128. if type(a) == 'number' then
  129. return a == b
  130. elseif type(a) == 'string' then
  131. return a:lower() == b:lower()
  132. end
  133. end
  134. pkg.has_part_like = function(query, structure)
  135. --
  136. -- True if structure has MIME part matching *query*
  137. --
  138. if structure == nil then
  139. return false
  140. end
  141. for partid, partinf in pairs(structure) do
  142. local part_answer = true
  143. -- check all query parts
  144. for qkey, qvalue in pairs(query) do
  145. local value = partinf[qkey]
  146. if not pkg._partinf_compare(value, qvalue) then
  147. part_answer = false
  148. break
  149. end
  150. end
  151. if part_answer then
  152. return true
  153. end
  154. end
  155. return false
  156. end
  157. pkg.save_header = function(mesg, name)
  158. --
  159. -- Append header from *mesg* to file named *name*
  160. --
  161. -- File will be placed under directory specified by IMAPDOMO_HEADERS
  162. -- environment variable.
  163. --
  164. local dest = os.getenv("IMAPDOMO_HEADERS") .. '/' .. name
  165. local cmd = ('cat >>"%q"'):format(dest)
  166. local mbox, uid = table.unpack(mesg)
  167. local header = mbox[uid]:fetch_header()
  168. if pkg.pipe_to(cmd, header) == 0 then
  169. return true
  170. else
  171. return false
  172. end
  173. end
  174. pkg.filter_header_saved = function(seq, name)
  175. --
  176. -- Save headers from sequence
  177. --
  178. -- Append header of each message in sequence *seq* to file names
  179. -- *name* and return new sequence with those messages where save was
  180. -- successful.
  181. --
  182. local result = {}
  183. for _, mesg in ipairs(seq) do
  184. if pkg.save_header(mesg, name) then
  185. table.insert(result, mesg)
  186. end
  187. end
  188. return result
  189. end
  190. pkg.head = function(num, seq)
  191. --
  192. -- Return first *num* elements from sequence
  193. --
  194. if not seq then return seq end
  195. local result = seq:is_smaller(0) -- HACK to generate empty sequence
  196. for idx, value in ipairs(seq) do
  197. if idx > num then break end
  198. table.insert(result, value)
  199. end
  200. return result
  201. end
  202. pkg.filter_part_like = function(query, seq)
  203. --
  204. -- Run MIME part query on *seq* sequence of messages
  205. --
  206. local result = {}
  207. for _, mesg in ipairs(seq) do
  208. local mbox, uid = table.unpack(mesg)
  209. local structure = mbox[uid]:fetch_structure()
  210. if pkg.has_part_like(query, structure) then
  211. table.insert(result, mesg)
  212. end
  213. end
  214. return result
  215. end
  216. return pkg