imapfilter convenience wrapper

common.lua 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. os = require "os"
  2. socket = require "socket"
  3. ------------------------------------------------------------------------------
  4. -- config and handling --
  5. ------------------------------------------------------------------------------
  6. function shortname()
  7. --
  8. -- Short hostname
  9. --
  10. local long = socket.dns.gethostname()
  11. return string.gmatch(long, '[^.]+')()
  12. end
  13. function file_exists(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. function init_host(session)
  26. --
  27. -- Do host-specific init, if exists
  28. --
  29. local init = session.dirs.host .. "/init.lua"
  30. if file_exists(init) then
  31. dofile(init)
  32. return
  33. end
  34. end
  35. function handle(session)
  36. --
  37. -- Look for action handler in session dirs; do first one that exists
  38. --
  39. local valid = {
  40. newmail = true,
  41. rewind = true,
  42. cleanup = true,
  43. migrate = true
  44. }
  45. if not valid[session.action] then
  46. error("invalid action: " .. session.action)
  47. return nil
  48. end
  49. init_host(session)
  50. for k,v in ipairs({session.dirs.host, session.dirs.default}) do
  51. attempt = v .. "/handlers/" .. session.action .. ".lua"
  52. if file_exists(attempt) then
  53. dofile(attempt)
  54. return
  55. end
  56. end
  57. end
  58. ------------------------------------------------------------------------------
  59. -- mail getters --
  60. ------------------------------------------------------------------------------
  61. function get_queue(acct, mbox)
  62. --
  63. -- Get queue from *mbox* from *acct* or nil (no messages)
  64. --
  65. -- If mbox is not specified, "FILTER_QUEUE" is used
  66. --
  67. mbox = mbox or "FILTER_QUEUE"
  68. local exist, unread, unseen, uidnext = acct[mbox]:check_status()
  69. if exist > 0 then
  70. return acct[mbox]:select_all()
  71. end
  72. return nil
  73. end
  74. function _notifirc1(subj, from, body)
  75. --
  76. -- notify about message using (pre-configured) notifirc
  77. --
  78. local fd = assert(io.popen('notifirc -c mail -f - "message preview:"', "w"))
  79. local fmt = "> %s\n> %s\n> BODY: %s"
  80. fd:write(fmt:format(subj, from, body))
  81. fd:close()
  82. end
  83. function notifirc_all(seq)
  84. --
  85. -- Send notifications about all messages in *seq*
  86. --
  87. for _, mesg in ipairs(seq) do
  88. mbox, uid = table.unpack(mesg)
  89. subj = mbox[uid]:fetch_field('Subject')
  90. from = mbox[uid]:fetch_field('From')
  91. body = mbox[uid]:fetch_body()
  92. _notifirc1(subj, from, body)
  93. end
  94. end
  95. function _partinf_compare(a, b)
  96. if not type(a) == type(b) then
  97. return false
  98. end
  99. if type(a) == 'number' then
  100. return a == b
  101. elseif type(a) == 'string' then
  102. return a:lower() == b:lower()
  103. end
  104. end
  105. function has_part_like(query, structure)
  106. --
  107. -- True if structure has MIME part matching *query*
  108. --
  109. if structure == nil then
  110. return false
  111. end
  112. for partid, partinf in pairs(structure) do
  113. local part_answer = true
  114. -- check all query parts
  115. for qkey, qvalue in pairs(query) do
  116. value = partinf[qkey]
  117. if not _partinf_compare(value, qvalue) then
  118. part_answer = false
  119. break
  120. end
  121. end
  122. if part_answer then
  123. return true
  124. end
  125. end
  126. return false
  127. end
  128. function save_header(mesg, name)
  129. --
  130. -- Append header from *mesg* to file named *name*
  131. --
  132. -- File will be placed under directory specified by IMAPDOMO_HEADERS
  133. -- environment variable.
  134. --
  135. local dest = os.getenv("IMAPDOMO_HEADERS") .. '/' .. name
  136. local cmd = ('cat >>"%q"'):format(dest)
  137. mbox, uid = table.unpack(mesg)
  138. header = mbox[uid]:fetch_header()
  139. if pipe_to(cmd, header) == 0 then
  140. return true
  141. else
  142. return false
  143. end
  144. end
  145. function filter_header_saved(seq, name)
  146. --
  147. -- Save headers from sequence
  148. --
  149. -- Append header of each message in sequence *seq* to file names
  150. -- *name* and return new sequence with those messages where save was
  151. -- successful.
  152. --
  153. result = Set {}
  154. for _, mesg in ipairs(seq) do
  155. if save_header(mesg, name) then
  156. table.insert(result, mesg)
  157. end
  158. end
  159. return result
  160. end
  161. function filter_part_like(query, seq)
  162. --
  163. -- Run MIME part query on *seq* sequence of messages
  164. --
  165. result = Set {}
  166. for _, mesg in ipairs(seq) do
  167. mbox, uid = table.unpack(mesg)
  168. structure = mbox[uid]:fetch_structure()
  169. if has_part_like(query, structure) then
  170. table.insert(result, mesg)
  171. end
  172. end
  173. return result
  174. end