====== 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.3/roundcubemail-1.3.3-complete.tar.gz tar xvf roundcubemail-1.3.3-complete.tar.gz && rm -f roundcubemail-1.3.3-complete.tar.gz ln -s roundcubemail-1.3.3 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: .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: 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 ==== 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/mysql_driver.php --- agendav-1.2.6.2.old/web/system/database/drivers/mysql/mysql_driver.php 2012-10-15 09:54:01.000000000 +0200 +++ agendav-1.2.6.2/web/system/database/drivers/mysql/mysql_driver.php 2017-07-09 16:18:32.673770000 +0200 @@ -88,7 +88,7 @@ $this->hostname .= ':'.$this->port; } - return @mysql_pconnect($this->hostname, $this->username, $this->password); + return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database); } // -------------------------------------------------------------------- @@ -120,7 +120,8 @@ */ function db_select() { - return @mysql_select_db($this->database, $this->conn_id); + //return @mysqli_select_db($this->database, $this->conn_id); + return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database); } // -------------------------------------------------------------------- @@ -138,16 +139,16 @@ if ( ! isset($this->use_set_names)) { // mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback - $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE; + $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE; } if ($this->use_set_names === TRUE) { - return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); + return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); } else { - return @mysql_set_charset($charset, $this->conn_id); + return @mysqli_set_charset($charset, $this->conn_id); } } @@ -176,7 +177,7 @@ function _execute($sql) { $sql = $this->_prep_query($sql); - return @mysql_query($sql, $this->conn_id); + return @mysqli_query($this->conn_id, $sql); } // -------------------------------------------------------------------- @@ -342,7 +343,7 @@ */ function affected_rows() { - return @mysql_affected_rows($this->conn_id); + return @mysqli_affected_rows($this->conn_id); } // -------------------------------------------------------------------- @@ -355,7 +356,7 @@ */ function insert_id() { - return @mysql_insert_id($this->conn_id); + return @mysqli_insert_id($this->conn_id); } // -------------------------------------------------------------------- @@ -454,7 +455,7 @@ */ function _error_message() { - return mysql_error($this->conn_id); + return mysqli_error($this->conn_id); } // -------------------------------------------------------------------- @@ -467,7 +468,7 @@ */ function _error_number() { - return mysql_errno($this->conn_id); + return mysqli_errno($this->conn_id); } + return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database); } // -------------------------------------------------------------------- @@ -138,16 +139,16 @@ if ( ! isset($this->use_set_names)) { // mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback - $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE; + $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE; } if ($this->use_set_names === TRUE) { - return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); + return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); } else { - return @mysql_set_charset($charset, $this->conn_id); + return @mysqli_set_charset($charset, $this->conn_id); } } @@ -176,7 +177,7 @@ function _execute($sql) { $sql = $this->_prep_query($sql); - return @mysql_query($sql, $this->conn_id); + return @mysqli_query($this->conn_id, $sql); } // -------------------------------------------------------------------- @@ -342,7 +343,7 @@ */ function affected_rows() { - return @mysql_affected_rows($this->conn_id); + return @mysqli_affected_rows($this->conn_id); } // -------------------------------------------------------------------- @@ -355,7 +356,7 @@ */ function insert_id() { - return @mysql_insert_id($this->conn_id); + return @mysqli_insert_id($this->conn_id); } // -------------------------------------------------------------------- @@ -454,7 +455,7 @@ */ function _error_message() { - return mysql_error($this->conn_id); + return mysqli_error($this->conn_id); } // -------------------------------------------------------------------- @@ -467,7 +468,7 @@ */ function _error_number() { - return mysql_errno($this->conn_id); + return mysqli_errno($this->conn_id); } // -------------------------------------------------------------------- @@ -769,11 +770,11 @@ */ function _close($conn_id) { - @mysql_close($conn_id); + @mysqli_close($conn_id); } } /* End of file mysql_driver.php */ -/* Location: ./system/database/drivers/mysql/mysql_driver.php */ \ No newline at end of file +/* Location: ./system/database/drivers/mysql/mysql_driver.php */ * plugins/agendav/agendav-1.2.6.2/web/system/database/drivers/mysql/mysql_result.php --- agendav-1.2.6.2.old/web/system/database/drivers/mysql/mysql_result.php 2012-10-15 09:54:01.000000000 +0200 +++ agendav-1.2.6.2/web/system/database/drivers/mysql/mysql_result.php 2017-07-09 18:06:06.572461000 +0200 @@ -34,7 +34,7 @@ */ function num_rows() { - return @mysql_num_rows($this->result_id); + return @mysqli_num_rows($this->result_id); } // -------------------------------------------------------------------- @@ -134,7 +134,7 @@ */ function _data_seek($n = 0) { - return mysql_data_seek($this->result_id, $n); + return mysqli_data_seek($this->result_id, $n); } // -------------------------------------------------------------------- @@ -149,7 +149,7 @@ */ function _fetch_assoc() { - return mysql_fetch_assoc($this->result_id); + return mysqli_fetch_assoc($this->result_id); } // -------------------------------------------------------------------- @@ -164,11 +164,11 @@ */ function _fetch_object() { - return mysql_fetch_object($this->result_id); + return mysqli_fetch_object($this->result_id); } } /* End of file mysql_result.php */ -/* Location: ./system/database/drivers/mysql/mysql_result.php */ \ No newline at end of file +/* Location: ./system/database/drivers/mysql/mysql_result.php */ ===== Carddav plugin ===== cd roundcube curl -s http://getcomposer.org/installer | php php composer.phar require roundcube/carddav ===== Mobile plugin ===== At the time of writing (2017-07-09), this plugin is [[https://github.com/messagerie-melanie2/Roundcube-Plugin-Mobile/issues/25|incompatible with Roundcube 1.3.0]]. cd roundcube git clone https://github.com/messagerie-melanie2/Roundcube-Skin-Melanie2-Larry-Mobile.git skins/melanie2_larry_mobile php composer.phar require melanie2/mobile:dev-master