In case someone is interested this is the final version of the script:
-- To use -- -- plugin { -- push_notification_driver = lua:file=/etc/dovecot/dovecot-push.lua -- push_lua_url = https://Bearer:<push-token>@<egroupware-domain>/egroupware/push -- } -- -- server is sent a PUT message with JSON body like push_notification_driver = ox:url=<push_lua_url> user_from_metadata -- plus additionally the events MessageAppend, MessageExpunge, FlagsSet and FlagsClear -- MessageTrash and MessageRead are ignored, so are empty FlagSet/Clear or FlagSet NonJunk (TB) -- local http = require "socket.http" local ltn12 = require "ltn12" -- luarocks install json-lua local json = require "JSON" function table_get(t, k, d) return t[k] or d end function script_init() return 0 end function dovecot_lua_notify_begin_txn(user) local meta = user:metadata_get("/private/vendor/vendor.dovecot/http-notify") if (meta == nil or meta:sub(1,5) ~= "user=") then meta = nil; else meta = meta:sub(6) end return {user=user, event=dovecot.event(), ep=user:plugin_getenv("push_lua_url"), messages={}, meta=meta} end function dovecot_lua_notify_event_message_new(ctx, event) -- check if there is a push token registered if (ctx.meta == nil) then return end -- get mailbox status local mbox = ctx.user:mailbox(event.mailbox) mbox:sync() local status = mbox:status(dovecot.storage.STATUS_RECENT, dovecot.storage.STATUS_UNSEEN, dovecot.storage.STATUS_MESSAGES) mbox:free() table.insert(ctx.messages, { user = ctx.meta, ["imap-uidvalidity"] = event.uid_validity, ["imap-uid"] = event.uid, folder = event.mailbox, event = event.name, from = event.from, subject = event.subject, snippet = event.snippet, unseen = status.unseen, messages = status.messages }) end function dovecot_lua_notify_event_message_append(ctx, event) dovecot_lua_notify_event_message_new(ctx, event) end -- ignored, as FlagSet flags=[\Seen] is sent anyway too -- function dovecot_lua_notify_event_message_read(ctx, event) -- dovecot_lua_notify_event_message_expunge(ctx, event) -- end -- ignored, as most MUA nowadays expunge immediatly -- function dovecot_lua_notify_event_message_trash(ctx, event) -- dovecot_lua_notify_event_message_expunge(ctx, event) -- end function dovecot_lua_notify_event_message_expunge(ctx, event) -- check if there is a push token registered if (ctx.meta == nil) then return end -- get mailbox status local mbox = ctx.user:mailbox(event.mailbox) mbox:sync() local status = mbox:status(dovecot.storage.STATUS_RECENT, dovecot.storage.STATUS_UNSEEN, dovecot.storage.STATUS_MESSAGES) mbox:free() -- agregate multiple Expunge (or Trash or Read) if (#ctx.messages == 1 and ctx.messages[1].user == ctx.meta and ctx.messages[1].folder == event.mailbox and ctx.messages[1]["imap-uidvalidity"] == event.uid_validity and ctx.messages[1].event == event.name) then if (type(ctx.messages[1]["imap-uid"]) ~= 'table') then ctx.messages[1]["imap-uid"] = {ctx.messages[1]["imap-uid"]} end table.insert(ctx.messages[1]["imap-uid"], event.uid) ctx.messages[1].unseen = status.unseen ctx.messages[1].messages = status.messages return; end table.insert(ctx.messages, { user = ctx.meta, ["imap-uidvalidity"] = event.uid_validity, ["imap-uid"] = event.uid, folder = event.mailbox, event = event.name, unseen = status.unseen, messages = status.messages }) end function dovecot_lua_notify_event_flags_set(ctx, event) -- check if there is a push token registered if (ctx.meta == nil or (#event.flags == 0 and #event.keywords == 0) or -- ignore TB sends it empty (#event.keywords == 1 and event.keywords[1] == "NonJunk")) -- ignore TB NonJunk then return end local status = nil; if (#event.flags == 1 and event.flags[1] == "\\Seen") then -- get mailbox status local mbox = ctx.user:mailbox(event.mailbox) mbox:sync() status = mbox:status(dovecot.storage.STATUS_RECENT, dovecot.storage.STATUS_UNSEEN, dovecot.storage.STATUS_MESSAGES) mbox:free() end -- agregate multiple FlagSet if (#ctx.messages == 1 and ctx.messages[1].user == ctx.meta and ctx.messages[1].folder == event.mailbox and ctx.messages[1]["imap-uidvalidity"] == event.uid_validity and ctx.messages[1].event == event.name and arrayEqual(ctx.messages[1].flags, event.flags) and arrayEqual(ctx.messages[1].keywords, event.keywords)) then if (type(ctx.messages[1]["imap-uid"]) ~= 'table') then ctx.messages[1]["imap-uid"] = {ctx.messages[1]["imap-uid"]} end table.insert(ctx.messages[1]["imap-uid"], event.uid) if (status ~= nil) then ctx.messages[1].unseen = status.unseen end return; end table.insert(ctx.messages, { user = ctx.meta, ["imap-uidvalidity"] = event.uid_validity, ["imap-uid"] = event.uid, folder = event.mailbox, event = event.name, flags = event.flags, keywords = event.keywords, unseen = status.unseen }) end function arrayEqual(t1, t2) if (#t1 ~= #t2) then return false end if (#t1 == 1 and t1[1] == t2[1]) then return true end return json:encode(t1) == json:encode(t2) end function dovecot_lua_notify_event_flags_clear(ctx, event) -- check if there is a push token registered AND something to clear (TB sends it empty) if (ctx.meta == nil or (#event.flags == 0 and #event.keywords == 0)) then return end table.insert(ctx.messages, { user = ctx.meta, ["imap-uidvalidity"] = event.uid_validity, ["imap-uid"] = event.uid, folder = event.mailbox, event = event.name, flags = event.flags, ["flags-old"] = event.flags_old, keywords = event.keywords, ["keywords-old"] = event.keywords_old }) end function dovecot_lua_notify_end_txn(ctx) -- report all states for i,msg in ipairs(ctx.messages) do local e = dovecot.event(ctx.event) e:set_name("lua_notify_mail_finished") reqbody = json:encode(msg) e:log_debug(ctx.ep .. " - sending " .. reqbody) res, code = http.request({ method = "PUT", url = ctx.ep, source = ltn12.source.string(reqbody), headers={ ["content-type"] = "application/json; charset=utf-8", ["content-length"] = tostring(#reqbody) } }) e:add_int("result_code", code) e:log_info("Mail notify status " .. tostring(code)) end end
It's also in our Github repo: https://raw.githubusercontent.com/EGroupware/swoolepush/master/doc/dovecot-push.lua
Ralf
-- Ralf Becker EGroupware GmbH [www.egroupware.org] Handelsregister HRB Kaiserslautern 3587 Geschäftsführer Birgit und Ralf Becker Leibnizstr. 17, 67663 Kaiserslautern, Germany Telefon +49 631 31657-0