mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-08-10 16:57:54 +02:00
Notifications via Lua script for Dovecot 2.3+
@ -48,7 +48,238 @@ Then restart Dovecot: ```systemctl restart dovecot```
|
|||||||
|
|
||||||
## Dovecot 2.3
|
## Dovecot 2.3
|
||||||
|
|
||||||
> Use Dovecot 2.2 documentation for now. We will soon publish how to get other events (flag changes, delete or Sieve moved mails) available from Dovecot 2.3 via a LUA script.
|
> Use Dovecot 2.2 documentation for current 20.1 packages/container. We will soon publish a new version to get other events (flag changes, delete or Sieve moved mails) available from Dovecot 2.3 via a LUA script.
|
||||||
|
|
||||||
|
Install ```dovecot-lua``` plus required Lua modules:
|
||||||
|
```
|
||||||
|
apt install dovecot-lua lua-socket luarocks
|
||||||
|
luarocks install json-lua
|
||||||
|
ln -s ln -s /usr/share/lua/5.1/JSON.lua /usr/share/lua/5.2/
|
||||||
|
```
|
||||||
|
Create the following file /etc/dovecot/conf.d/99-egroupware-push.conf
|
||||||
|
```
|
||||||
|
# Store METADATA information in a file dovecot-metadata in user's home
|
||||||
|
mail_attribute_dict = file:%h/dovecot-metadata
|
||||||
|
|
||||||
|
# enable metadata
|
||||||
|
protocol imap {
|
||||||
|
imap_metadata = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
# add necessary plugins for Lua push notifications
|
||||||
|
mail_plugins = $mail_plugins mail_lua notify push_notification push_notification_lua
|
||||||
|
|
||||||
|
# Lua notification script and URL of EGroupware push server
|
||||||
|
plugin {
|
||||||
|
push_notification_driver = lua:file=/etc/dovecot/dovecot-push.lua
|
||||||
|
push_lua_url = https://Bearer:<push-token>@<egroupware-domain>/egroupware/push
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Copy the following Lua script to /etc/dovecot/dovecot-push.lua
|
||||||
|
```lua
|
||||||
|
-- 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
|
||||||
|
```
|
||||||
|
Then restart Dovecot: ```systemctl restart dovecot```
|
||||||
|
|
||||||
## Cyrus
|
## Cyrus
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user