Table of Contents
From EGroupware 20.1 on supported IMAP servers can be configured to send push notifications to EGroupware's push server.
Currently only supported is Dovecot on premise and EGroupware GmbH's mail service for the hosting (mail.egroupware.org).
If you use our hosting, nothing special need to be done, everything is already preconfigured for you.
General steps for all mail-servers
- First you need to find out your automatic generate bearer-token for push:
docker exec -it egroupware-push cat /var/www/config.inc.php
<?php
$bearer_token = '<bearer-token>';
- If the bearer-token has URL special characters + or / in it, you need to change them first, eg. replace them with an X:
cd /etc/egroupware-docker
docker run --rm -it -vegroupware-docker_push-config:/mnt busybox vi /mnt/config.inc.php
docker-compose restart
- Add IMAP server name (or IP-addresse) used in EGroupware in Mail site configuration: Admin > Applications > Mail > Site configuration
Dovecot 2.2+
Dovecot 2.2 only supports notifications about new arriving mails in users INBOX AND only http URLs!
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 notify AND push_notification plugins for LMTP (or LDA if you use that)
protocol lmtp {
mail_plugins = $mail_plugins notify push_notification
}
# URL to call for new arriving mails in the INBOX
plugin {
push_notification_driver = ox:url=http://Bearer:<push-token>@<egroupware-domain>/egroupware/push user_from_metadata
}
Then restart Dovecot: systemctl restart dovecot
Dovecot documentation about push and required IMAP metadata
Dovecot 2.3.7+
Dovecot 2.3 allows to get more events (flag changes, delete or Sieve moved mails) via a custom LUA script and allows https URLs. 2.3.7+ is required to read metadata information / the push server tokens EGroupware places on the mailbox (metadata_get() method).
Install dovecot-lua
plus required Lua modules:
apt install dovecot-lua lua-socket lua-json
curl https://raw.githubusercontent.com/EGroupware/swoolepush/master/doc/dovecot-push.lua > /etc/dovecot/dovecot-push.lua
(For CentOS/RHEL use yum
instead of apt
and you need to install epel-release
package/repo first. Debian/Ubuntu has no dovecot-lua, it is include in dovecot-core already.)
Create the following file /etc/dovecot/conf.d/14-egroupware-push.conf (must be before 15-lda.conf and 20-lmtp.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
}
The above commands installed the following Lua script to /etc/dovecot/dovecot-push.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 or NonJunk FlagSet/Clear from TB
--
-- Needs lua-socket and lua-json packages plus dovecot-lua!
--
local http = require "socket.http"
local ltn12 = require "ltn12"
local json = require "json"
function table_get(t, k, d)
return t[k] or d
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
local msg = {
user = ctx.meta,
["imap-uidvalidity"] = event.uid_validity,
["imap-uid"] = event.uid,
folder = event.mailbox,
event = event.name,
flags = event.flags,
keywords = event.keywords
}
if (status ~= nil)
then
msg.unseen = status.unseen
end
if (event.name == "FlagsClear")
then
msg.flags_old = event.flags_old
msg.keywords_old = event.keywords_old
end
table.insert(ctx.messages, msg)
end
function arrayEqual(t1, t2)
if (#t1 ~= #t2)
then
return false
end
if (#t1 == 1)
then
return t1[1] == t2[1]
end
return json.encode(t1) == json.encode(t2)
end
function dovecot_lua_notify_event_flags_clear(ctx, event)
dovecot_lua_notify_event_flags_set(ctx, event)
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 allows to configure a script as external notification for it's notifyd. Let us know, if you're interested that we create such a script.
Language: |
- General information
- Distribution specific instructions
- Update recommendations and troubleshooting
- Tuning EGroupware for higher number of users
- Docker-compose installation: Linux, Windows, Mac, Synology, QNAP
- Configure IMAP push
- IMAP Push Notifications for Dovecot 2.2+
- Using EGroupware Mail server with ActiveDirectory
CTI / Computer Telephone Integration
Using SmallPART with a LMS (Moodle, OpenOLAT, ...)
Synchronisation between Untis / Webuntis and EGroupware
Development