**This is an old revision of the document!**

Lightweight groupware solution using nginx+php-fpm+mysql+baïkal+roundcube+agendav

The goal of this document is to set up a web-based solution providing mail, calendars and contacts management, along with mobile devices & desktop synchronization using dav* protocols.

This obviously needs an already running full mail stack (I use qmail/postfix/dovecot), which will not be covered there. This document also assume that nginx, fpm and mysql are also already up and running.

nginx

 server {

        # put listen directives & tls stuff here #

        server_name sub.domain.tld;

        access_log /var/log/nginx/groupware-access.log;
        error_log /var/log/nginx/groupware-error.log;

        location / {
                index index.php;
                root /path/to/roundcube;

                dav_ext_methods PROPFIND OPTIONS;
                
                # support for csrf token
                rewrite "^/[a-zA-Z0-9]{16}/(.*)" /$1 break;
                
                # good ol' favicon (adjust path if you use Larry skin)
                rewrite ^/favicon.ico /skins/classic/images/favicon.ico last;

                # caldav/carddav stuff
                rewrite ^/addressbooks/(.*) /dav/card.php/addressbooks/$1 redirect;
                rewrite ^/calendars/(.*) /dav/cal.php/calendars/$1 redirect;
                rewrite ^/.well-known/carddav /dav/dav.php redirect;
                rewrite ^/.well-known/caldav /dav/dav.php redirect;


                # maximum upload size for mail attachments
                client_max_body_size 30M;

                location ~ .php {
                        include fastcgi_params;
                        fastcgi_param HTTPS on;
                        fastcgi_split_path_info (.+.php)(/.*)$;
                        fastcgi_pass unix:/path/to/php-fpm.sock;
                        fastcgi_param SCRIPT_FILENAME $request_filename;
                }
        }

        location /dav {
                root /path/to/baikal;
                index index.php;

                dav_methods     PUT DELETE MKCOL COPY MOVE;
                dav_ext_methods PROPFIND OPTIONS;

                charset utf-8;

                location ~ /(\.ht|Core|Specific) {
                        deny all;
                        return 404;
                }

                location ~ ^(.+\.php)(.*)$ {
                        try_files $fastcgi_script_name =404;
                        include fastcgi_params;
                        fastcgi_split_path_info  ^(.+\.php)(.*)$;
                        fastcgi_pass   unix:/path/to/php-fpm.sock;
                        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                        fastcgi_param  PATH_INFO        $fastcgi_path_info;
                }

        }
}

Baïkal

curl -L -O https://github.com/fruux/Baikal/releases/download/0.4.6/baikal-0.4.6.zip
unzip baikal-0.4.6.zip && rm -f baikal-0.4.6.zip
ln -s baikal-0.4.6 baikal

Roundcube

  • First, you need to create a database. Here we created a mysql database named roundcubemail, hosted on “dbhost” and owned by user roundcubemail.
  • Install roundecube:
curl -L -O https://github.com/roundcube/roundcubemail/releases/download/1.3.0/roundcubemail-1.3.0-complete.tar.gz
tar xvf roundcubemail-1.3.0-complete.tar.gz && rm -f roundcubemail-1.3.0-complete.tar.gz
ln -s roundcubemail-1.3.0 roundcube
cd roundcube
mysql -h dbhost -u roundcubemail -p roundcubemail < SQL/mysql.initial.sql

Agendav plugin

  • First, you need to create a database. Here we created a mysql database named agendav, hosted on “dbhost” and owned by user agendav.
  • Install agendav plugin:
cd roundcube/plugins
git clone https://github.com/stephanblanke/roundcube-agendav.git agendav
curl -L -O https://github.com/adobo/agendav/archive/1.2.6.2.tar.gz
tar xvf 1.2.6.2.tar.gz && rm 1.2.6.2.tar.gz
cp agendav-1.2.6.2.css agendav-1.2.6.2/web/public/css/agendav-1.2.6.2.css
cd agendav-1.2.6.2
mysql -h dbhost -u agendav -p agendav < sql/mysql.schema.sql
  • Adjust var in plugins/agendav/agendav-1.2.6.2/web/config/config.php
  • Adjust caldav parameters in plugins/agendav/agendav-1.2.6.2/web/config/caldav.php:
$config['caldav_http_auth_method'] = CURLAUTH_BASIC;
$config['caldav_principal_url'] = 'https://sub.domain.tld/dav/cal.php/%u/default';
$config['caldav_calendar_url'] = 'https://sub.domain.tld/.well-known/caldav';
$config['public_caldav_url'] = 'https://sub.domain.tld/dav/cal.php/%s/';
  • Adjust vars in plugins/agendav/agendav-1.2.6.2/web/config/database.php:
$db['default']['hostname'] = 'dbhost';
$db['default']['username'] = 'agendav';
$db['default']['password'] = 'changeme';
$db['default']['database'] = 'agendav';
$db['default']['dbdriver'] = 'mysql';
  • For french translation, create plugins/agendav/localization/fr_FR.inc:
<?php
 
/*
 +-----------------------------------------------------------------------+
 | plugins/help/localization/<lang>.inc                                  |
 |                                                                       |
 | Localization file of the Roundcube Webmail Help plugin                |
 | Copyright (C) 2012-2013, The Roundcube Dev Team                       |
 |                                                                       |
 | Licensed under the GNU General Public License version 3 or            |
 | any later version with exceptions for skins & plugins.                |
 | See the README file for a full license statement.                     |
 |                                                                       |
 +-----------------------------------------------------------------------+
 
 For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-help/
*/
 
$labels = array();
$labels['agendav'] = 'Calendrier';
$labels['prefs'] = 'Préférences';
 
?>
  • If using classic skin, replace the strange “interrogation mark” button by a prettier one: <code bash> mv plugins/agendav/skins/classic/agendav.gif plugins/agendav/skins/classic/agendav.old.gif curl -o plugins/agendav/skins/classic/agendav.gif https://git.kolab.org/file/data/kamnlpzz2fmtbgygywso/PHID-FILE-2ozl7pm2enflqcndiowh/calendar.gif </code>

    PHP7

    If using php7, you need to patch agendav 1.2.6.2
    • plugins/agendav/agendav-1.2.6.2/web/system/database/drivers/mysql/mysqldriver.php <code diff> — agendav-1.2.6.2.old/web/system/database/drivers/mysql/mysqldriver.php 2012-10-15 09:54:01.000000000 +0200 +++ agendav-1.2.6.2/web/system/database/drivers/mysql/mysqldriver.php 2017-07-09 16:18:32.673770000 +0200 @@ -88,7 +88,7 @@ $this→hostname .= ':'.$this→port; } - return @mysqlpconnect($this→hostname, $this→username, $this→password);
  • return @mysqliconnect($this→hostname, $this→username, $this→password, $this→database); } ——————————————————————– @@ -120,7 +120,8 @@ / function dbselect() { - return @mysqlselectdb($this→database, $this→connid); + return @mysqliselectdb($this→database, $this→connid); + return @mysqliconnect($this→hostname, $this→username, $this→password, $this→database); } ——————————————————————– @@ -138,16 +139,16 @@ if ( ! isset($this→usesetnames)) { mysqlsetcharset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback - $this→usesetnames = (versioncompare(PHPVERSION, '5.2.3', '>=') && versioncompare(mysqlgetserverinfo(), '5.0.7', '>=')) ? FALSE : TRUE; + $this→usesetnames = (versioncompare(PHPVERSION, '5.2.3', '>=') && versioncompare(mysqligetserverinfo($this→connid), '5.0.7', '>=')) ? FALSE : TRUE; } if ($this→usesetnames === TRUE) { - return @mysqlquery(“SET NAMES '”.$this→escapestr($charset).“' COLLATE '”.$this→escapestr($collation).“'”, $this→connid); + return @mysqliquery($this→connid, “SET NAMES '”.$this→escapestr($charset).“' COLLATE '”.$this→escapestr($collation).“'”); } else { - return @mysqlsetcharset($charset, $this→connid); + return @mysqlisetcharset($charset, $this→connid); } } @@ -176,7 +177,7 @@ function _execute($sql) { $sql = $this→prepquery($sql); - return @mysqlquery($sql, $this→connid); + return @mysqliquery($this→connid, $sql); } ——————————————————————– @@ -342,7 +343,7 @@ / function affectedrows() { - return @mysqlaffectedrows($this→connid); + return @mysqliaffectedrows($this→connid); } ——————————————————————– @@ -355,7 +356,7 @@ / function insertid() { - return @mysqlinsertid($this→connid); + return @mysqliinsertid($this→connid); } ——————————————————————– @@ -454,7 +455,7 @@ / function errormessage() { - return mysqlerror($this→connid); + return mysqlierror($this→connid); } ——————————————————————– @@ -467,7 +468,7 @@ / function errornumber() { - return mysqlerrno($this→connid); + return mysqlierrno($this→connid); } + return @mysqliconnect($this→hostname, $this→username, $this→password, $this→database); } ——————————————————————– @@ -138,16 +139,16 @@ if ( ! isset($this→usesetnames)) { mysqlsetcharset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback - $this→usesetnames = (versioncompare(PHPVERSION, '5.2.3', '>=') && versioncompare(mysqlgetserverinfo(), '5.0.7', '>=')) ? FALSE : TRUE; + $this→usesetnames = (versioncompare(PHPVERSION, '5.2.3', '>=') && versioncompare(mysqligetserverinfo($this→connid), '5.0.7', '>=')) ? FALSE : TRUE; } if ($this→usesetnames === TRUE) { - return @mysqlquery(“SET NAMES '”.$this→escapestr($charset).“' COLLATE '”.$this→escapestr($collation).“'”, $this→connid); + return @mysqliquery($this→connid, “SET NAMES '”.$this→escapestr($charset).“' COLLATE '”.$this→escapestr($collation).“'”); } else { - return @mysqlsetcharset($charset, $this→connid); + return @mysqlisetcharset($charset, $this→connid); } } @@ -176,7 +177,7 @@ function execute($sql) { $sql = $this→prepquery($sql); - return @mysqlquery($sql, $this→connid); + return @mysqliquery($this→connid, $sql); } ——————————————————————– @@ -342,7 +343,7 @@ / function affectedrows() { - return @mysqlaffectedrows($this→connid); + return @mysqliaffectedrows($this→connid); } ——————————————————————– @@ -355,7 +356,7 @@ / function insertid() { - return @mysqlinsertid($this→connid); + return @mysqliinsertid($this→connid); } ——————————————————————– @@ -454,7 +455,7 @@ / function errormessage() { - return mysqlerror($this→connid); + return mysqlierror($this→connid); } ——————————————————————– @@ -467,7 +468,7 @@ / function errornumber() { - return mysqlerrno($this→connid); + return mysqlierrno($this→connid); } ——————————————————————– @@ -769,11 +770,11 @@ / function close($connid) { - @mysqlclose($connid); + @mysqliclose($connid); } } / End of file mysqldriver.php / -/ Location: ./system/database/drivers/mysql/mysqldriver.php / \ No newline at end of file +/ Location: ./system/database/drivers/mysql/mysqldriver.php / </code> * plugins/agendav/agendav-1.2.6.2/web/system/database/drivers/mysql/mysqlresult.php <code diff> — agendav-1.2.6.2.old/web/system/database/drivers/mysql/mysqlresult.php 2012-10-15 09:54:01.000000000 +0200 +++ agendav-1.2.6.2/web/system/database/drivers/mysql/mysqlresult.php 2017-07-09 18:06:06.572461000 +0200 @@ -34,7 +34,7 @@ / function numrows() { - return @mysqlnumrows($this→resultid); + return @mysqlinumrows($this→resultid); } ——————————————————————– @@ -134,7 +134,7 @@ / function dataseek($n = 0) { - return mysqldataseek($this→resultid, $n); + return mysqlidataseek($this→resultid, $n); } ——————————————————————– @@ -149,7 +149,7 @@ / function fetchassoc() { - return mysqlfetchassoc($this→resultid); + return mysqlifetchassoc($this→resultid); } ——————————————————————– @@ -164,11 +164,11 @@ / function fetchobject() { - return mysqlfetchobject($this→resultid); + return mysqlifetchobject($this→resultid); } } / End of file mysqlresult.php / -/ Location: ./system/database/drivers/mysql/mysqlresult.php / \ No newline at end of file +/ Location: ./system/database/drivers/mysql/mysqlresult.php */ </code> ===== Carddav plugin ===== <code bash> cd roundcube curl -s http://getcomposer.org/installer | php php composer.phar require roundcube/carddav </code> ===== Mobile plugin ===== <note warning>At the time of writing (2017-07-09), this plugin is incompatible with Roundcube 1.3.0.</note> <code bash> cd roundcube git clone https://github.com/messagerie-melanie2/Roundcube-Skin-Melanie2-Larry-Mobile.git skins/melanie2larry_mobile php composer.phar require melanie2/mobile:dev-master </code>