OX6:MailNotify Bundle

From Open-Xchange
Revision as of 15:06, 18 February 2011 by Choeger (talk | contribs)

Open-Xchange Mail Push

Beginning with the 6.16 release, there's a new bundle handling external notifications of new mail to send push requests to mobile devices.

Description

This Bundle is currently only able to cooperate with the mail notification functionality of cyrus-imapd, but this will change in a way, that future versions will be configurable on what kind of notification protocol is required.

At the moment, this bundle listens on the address and port as configured in /opt/open-xchange/etc/groupware/push_mailnotify.properties accepting UDP datagrams following the format as used in the cyrus notifyd:

notifyd/notifyd.c:
method NUL class NUL priority NUL user NUL mailbox NUL nopt NUL N(option NUL) message NUL

and the only information which is currently being evaluated is the fourth one, the username.

Install on OX AppSuite

Debian GNU/Linux 11.0

Add the following entry to /etc/apt/sources.list.d/open-xchange.list if not already present:

deb https://software.open-xchange.com/products/stable/DebianBullseye/ /
# if you have a valid maintenance subscription, please uncomment the 
# following and add the ldb account data to the url so that the most recent
# packages get installed
# deb https://[CUSTOMERID:PASSWORD]@software.open-xchange.com/products/stable/updates/DebianBullseye/ /

and run

$ apt-get update
$ apt-get install open-xchange-push-mailnotify

Debian GNU/Linux 12.0

Add the following entry to /etc/apt/sources.list.d/open-xchange.list if not already present:

deb https://software.open-xchange.com/products/stable/DebianBookworm/ /
# if you have a valid maintenance subscription, please uncomment the 
# following and add the ldb account data to the url so that the most recent
# packages get installed
# deb https://[CUSTOMERID:PASSWORD]@software.open-xchange.com/products/stable/updates/DebianBookworm/ /

and run

$ apt-get update
$ apt-get install open-xchange-push-mailnotify


Configuration

On the server running cyrus, add the following to /etc/imapd.conf:

mailnotifier: log

In /etc/cyrus.conf take care, that cyrus own notifyd is NOT in use. If it is not commented, either do that or remove it completely.

Example:

 # this is only necessary if using notifications
 #  notify       cmd="notifyd" listen="/var/lib/imap/socket/notify" proto="udp" prefork=1

Install the program socat. Socat is a Multipurpose relay (see http://www.dest-unreach.org/socat/) which must be used to relay the udb datagrams from the unix domain socket to the open-xchange push bundle.

To redirect the notifications to the open-xchange server, socat must be started like this:

socat -u -4 UNIX-RECV:/var/lib/imap/socket/notify,unlink-early,perm-early=777,type=2 \
         UDP-SENDTO:<YOUROXIP>:<YOURPUSHPORT>

To debug whether cyrus sends messages, you might want to add debugging to socat:

socat -d -d -d -D -v -u -4 UNIX-RECV:/var/lib/imap/socket/notify,unlink-early,perm-early=777,type=2 \
         UDP-SENDTO:<YOUROXIP>:<YOURPUSHPORT>

Example Initscript for Debian

The following initscript can be used to start the socat tunnel on debian. A file /etc/default/ox-push can be created to define parameters such as DAEMON_ARGS.

After copying the script to /etc/init.d/ox-push do the following:

$ chmod 755 /etc/init.d/ox-push
$ update-rc.d ox-push defaults
#! /bin/sh
### BEGIN INIT INFO
# Provides:          ox-push
# Required-Start:    $local_fs $remote_fs open-xchange-groupware
# Required-Stop:     $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: UDP Tunnel for Open-Xchange mail push
# Description:       Tunnel UDP Datagrams from cyrus notify unix socket
#                    to Open-Xchange Mail Push bundle
### END INIT INFO

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Open-Xchange Push Tunnel"
NAME=socat
DAEMON=/usr/bin/$NAME
DAEMON_ARGS="-u -4 UNIX-RECV:/var/lib/imap/socket/notify,unlink-early,perm-early=777,type=2 UDP-SENDTO:localhost:23420"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
	# Return
	#   0 if daemon has been started
	#   1 if daemon was already running
	#   2 if daemon could not be started
	start-stop-daemon --start -b --quiet -m --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
		|| return 1
	start-stop-daemon --start -b --quiet -m --pidfile $PIDFILE --exec $DAEMON -- \
		$DAEMON_ARGS \
		|| return 2
	# Add code here, if necessary, that waits for the process to be ready
	# to handle requests from services started subsequently which depend
	# on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
	# Return
	#   0 if daemon has been stopped
	#   1 if daemon was already stopped
	#   2 if daemon could not be stopped
	#   other if a failure occurred
	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
	RETVAL="$?"
	[ "$RETVAL" = 2 ] && return 2
	# Many daemons don't delete their pidfiles when they exit.
	rm -f $PIDFILE
	return "$RETVAL"
}


case "$1" in
  start)
	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
	do_start
	case "$?" in
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
	esac
	;;
  stop)
	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
	do_stop
	case "$?" in
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
	esac
	;;
  restart|force-reload)
	#
	# If the "reload" option is implemented then remove the
	# 'force-reload' alias
	#
	log_daemon_msg "Restarting $DESC" "$NAME"
	do_stop
	case "$?" in
	  0|1)
		do_start
		case "$?" in
			0) log_end_msg 0 ;;
			1) log_end_msg 1 ;; # Old process is still running
			*) log_end_msg 1 ;; # Failed to start
		esac
		;;
	  *)
	  	# Failed to stop
		log_end_msg 1
		;;
	esac
	;;
  *)
	echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
	exit 3
	;;
esac

:

Example Initscript for Redhat/SUSE

After copying the script to /etc/init.d/ox-push do the following:

$ chmod 755 /etc/init.d/ox-push

and on Redhat

$ chkconfig --level 35 ox-push on

on SUSE

$ insserv ox-push
#!/bin/bash
#
#     LSB system startup script for service/daemon socat tunnel
#     Based on initscript skeleton.compat as shipped with [open]SUSE.
#          
#     This library is free software; you can redistribute it and/or modify it
#     under the terms of the GNU Lesser General Public License as published by
#     the Free Software Foundation; either version 2.1 of the License, or (at
#     your option) any later version.
#			      
#     This library is distributed in the hope that it will be useful, but
#     WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#      
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
#     USA.
#
# /etc/init.d/ox-push
# LSB compatible service control script; see http://www.linuxbase.org/spec/
# 
# Note: This template uses functions rc_XXX defined in /etc/rc.status on
# UnitedLinux/SUSE/Novell based Linux distributions. However, it will work
# on other distributions as well, by using the LSB (Linux Standard Base) 
# or RH functions or by open coding the needed functions.
# Read http://www.tldp.org/HOWTO/HighQuality-Apps-HOWTO/ if you prefer not 
# to use this template.
#
# chkconfig: 345 99 00
# description: Open Xchange Groupware Daemon
# 
### BEGIN INIT INFO
# Provides:          ox-push
# Required-Start:    $syslog $remote_fs open-xchange-groupware
# Required-Stop:     $syslog $remote_fs
# Default-Start:     3 4 5
# Default-Stop:      0 1 2 6
# Short-Description: Open Xchange Groupware Daemon
# Description:       UDP Tunnel for Open-Xchange mail push
### END INIT INFO


# Note: Special treatment of stop for LSB conformance
PIDFILE=ox-push.pid
SOCATBIN=/usr/bin/socat
SOCATARGS="-u -4 UNIX-RECV:/var/lib/imap/socket/notify,unlink-early,perm-early=777,type=2 UDP-SENDTO:localhost:23420"

test -x $SOCATBIN || { echo "$SOCATBIN not installed";
        if [ "$1" = "stop" ]; then exit 0;
        else exit 5; fi; }

# Source LSB init functions
# providing start_daemon, killproc, pidofproc, 
# log_success_msg, log_failure_msg and log_warning_msg.
# This is currently not used by UnitedLinux based distributions and
# not needed for init scripts for UnitedLinux only. If it is used,
# the functions from rc.status should not be sourced or used.
#. /lib/lsb/init-functions

# Shell functions sourced from /etc/rc.status:
#      rc_check         check and set local and overall rc status
#      rc_status        check and set local and overall rc status
#      rc_status -v     be verbose in local rc status and clear it afterwards
#      rc_status -v -r  ditto and clear both the local and overall rc status
#      rc_status -s     display "skipped" and exit with status 3
#      rc_status -u     display "unused" and exit with status 3
#      rc_failed        set local and overall rc status to failed
#      rc_failed <num>  set local and overall rc status to <num>
#      rc_reset         clear both the local and overall rc status
#      rc_exit          exit appropriate to overall rc status
#      rc_active        checks whether a service is activated by symlinks

# Use the SUSE rc_ init script functions;
# emulate them on LSB, RH and other systems

# Default: Assume sysvinit binaries exist
start_daemon() { /sbin/start_daemon ${1+"$@"}; }
killproc()     { /sbin/killproc     ${1+"$@"}; }
pidofproc()    { /sbin/pidofproc    ${1+"$@"}; }
checkproc()    { /sbin/checkproc    ${1+"$@"}; }
if test -e /etc/rc.status; then
    # SUSE rc script library
    . /etc/rc.status
else
    export LC_ALL=POSIX
    _cmd=$1
    declare -a _SMSG
    if test "${_cmd}" = "status"; then
	_SMSG=(running dead dead unused unknown reserved)
	_RC_UNUSED=3
    else
	_SMSG=(done failed failed missed failed skipped unused failed failed reserved)
	_RC_UNUSED=6
    fi
    if test -e /lib/lsb/init-functions; then
	# LSB    
    	. /lib/lsb/init-functions
	echo_rc()
	{
	    if test ${_RC_RV} = 0; then
		log_success_msg "  [${_SMSG[${_RC_RV}]}] "
	    else
		log_failure_msg "  [${_SMSG[${_RC_RV}]}] "
	    fi
	}
	# TODO: Add checking for lockfiles
	checkproc() { return pidofproc ${1+"$@"} >/dev/null 2>&1; }
    elif test -e /etc/init.d/functions; then
	# RHAT
	. /etc/init.d/functions
	echo_rc()
	{
	    #echo -n "  [${_SMSG[${_RC_RV}]}] "
	    if test ${_RC_RV} = 0; then
		success "  [${_SMSG[${_RC_RV}]}] "
	    else
		failure "  [${_SMSG[${_RC_RV}]}] "
	    fi
	}
	checkproc() { return status ${1+"$@"}; }
	start_daemon() { return daemon ${1+"$@"}; }
    else
	# emulate it
	echo_rc() { echo "  [${_SMSG[${_RC_RV}]}] "; }
    fi
    rc_reset() { _RC_RV=0; }
    rc_failed()
    {
	if test -z "$1"; then 
	    _RC_RV=1;
	elif test "$1" != "0"; then 
	    _RC_RV=$1; 
    	fi
	return ${_RC_RV}
    }
    rc_check()
    {
	return rc_failed $?
    }	
    rc_status()
    {
	rc_failed $?
	if test "$1" = "-r"; then _RC_RV=0; shift; fi
	if test "$1" = "-s"; then rc_failed 5; echo_rc; rc_failed 3; shift; fi
	if test "$1" = "-u"; then rc_failed ${_RC_UNUSED}; echo_rc; rc_failed 3; shift; fi
	if test "$1" = "-v"; then echo_rc; shift; fi
	if test "$1" = "-r"; then _RC_RV=0; shift; fi
	return ${_RC_RV}
    }
    rc_exit() { exit ${_RC_RV}; }
    rc_active() 
    {
	if test -z "$RUNLEVEL"; then read RUNLEVEL REST < <(/sbin/runlevel); fi
	if test -e /etc/init.d/S[0-9][0-9]${1}; then return 0; fi
	return 1
    }
fi

# Reset status of this service
rc_reset

# Return values acc. to LSB for all commands but status:
# 0	  - success
# 1       - generic or unspecified error
# 2       - invalid or excess argument(s)
# 3       - unimplemented feature (e.g. "reload")
# 4       - user had insufficient privileges
# 5       - program is not installed
# 6       - program is not configured
# 7       - program is not running
# 8--199  - reserved (8--99 LSB, 100--149 distrib, 150--199 appl)
# 
# Note that starting an already running service, stopping
# or restarting a not-running service as well as the restart
# with force-reload (in case signaling is not supported) are
# considered a success.

tstatus() {
    if [ ! -f /var/run/$PIDFILE ]; then
        return 3
    else
        read PID < /var/run/$PIDFILE
        test -z "$PID" && { echo "unable to read pid"; return 3; }
        if ! ps $PID > /dev/null; then
                return 3
        fi
    fi
    return 0
}

case "$1" in
    start)
	echo -n "Starting ox push "
	## Start daemon with startproc(8). If this fails
	## the return value is set appropriately by startproc.
	$SOCATBIN $SOCATARGS 2>&1 & echo $! > /var/run/$PIDFILE

	# Remember status and be verbose
	rc_status -v
	;;
    stop)
	echo -n "Shutting down ox push "
	## Stop daemon with killproc(8) and if this fails
	## killproc sets the return value according to LSB.

	if [ ! -f /var/run/$PIDFILE ]; then
		echo "service not running"
	else
		read PID < /var/run/$PIDFILE
		test -z "$PID" && { echo "unable to read pid"; exit 1; }
		if ! ps $PID > /dev/null; then
			echo "service not running"
		else
			kill -TERM $PID
		fi
	fi

	# Remember status and be verbose
	rc_status -v
	;;
    try-restart|condrestart)
	## Do a restart only if the service was active before.
	## Note: try-restart is now part of LSB (as of 1.9).
	## RH has a similar command named condrestart.
	if test "$1" = "condrestart"; then
		echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}"
	fi
	$0 status
	if test $? = 0; then
		$0 restart
	else
		rc_reset	# Not running is not a failure.
	fi
	# Remember status and be quiet
	rc_status
	;;
    restart)
	## Stop the service and regardless of whether it was
	## running or not, start it again.
	$0 stop
	$0 start

	# Remember status and be quiet
	rc_status
	;;
    force-reload)
	## Signal the daemon to reload its config. Most daemons
	## do this on signal 1 (SIGHUP).
	## If it does not support it, restart the service if it
	## is running.

	echo -n "Reload ox push "
	## if it supports it:
	#killproc -p $PIDFILE -HUP $OX_BIN
	#touch /var/run/FOO.pid
	#rc_status -v

	## Otherwise:
	$0 try-restart
	rc_status
	;;
    reload)
	## Like force-reload, but if daemon does not support
	## signaling, do nothing (!)

	# If it supports signaling:
	echo -n "Reload ox push "
	#killproc -p $PIDFILE -HUP $OX_BIN
	#touch /var/run/FOO.pid
	#rc_status -v
	
	## Otherwise if it does not support reload:
	rc_failed 3
	rc_status -v
	;;
    status)
	echo -n "Checking for ox push "
	## Check status with checkproc(8), if process is running
	## checkproc will return with exit status 0.

	# Return value is slightly different for the status command:
	# 0 - service up and running
	# 1 - service dead, but /var/run/  pid  file exists
	# 2 - service dead, but /var/lock/ lock file exists
	# 3 - service not running (unused)
	# 4 - service status unknown :-(
	# 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.)
	
	# NOTE: checkproc returns LSB compliant status values.
	tstatus
	# NOTE: rc_status knows that we called this init script with
	# "status" option and adapts its messages accordingly.
	rc_status -v
	;;
    *)
	echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|}"
	exit 1
	;;
esac
rc_exit