diff --git a/README.md b/README.md index eee11c3..ced7130 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ AgenDAV for YunoHost -------------------- +-------------------- + +**This is a work-in-progress AgenDAV package upgrade.** [AgenDAV](http://agendav.org/) is a CalDAV web client which features an AJAX interface to allow users to manage their own calendars and shared ones. -**Shipped version:** 1.2.6.2 +**Shipped version:** 2.0.0-beta2 -## Known issues +![](http://agendav.org/img/screenshots/2.0.0-beta1/001_month_view.png) - * The calendar events are not retrieved when using the last Baïkal version as - backend, even if creating new ones work. +## TODO + + * Update the `upgrade`, `backup` and `restore` scripts + * Test the upgrade from the 1.x to the 2.x ## Links -**AgenDAV**: http://agendav.org/ - -**YunoHost**: https://yunohost.org/ + * Report a bug: https://dev.yunohost.org/projects/apps/issues + * Nextcloud website: http://agendav.org/ + * YunoHost website: https://yunohost.org/ diff --git a/conf/nginx.conf b/conf/nginx.conf index 55b8a86..abaac35 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,13 +1,18 @@ -location {LOCATION} { - alias {DESTDIR}/web/public; +location {PATH}/ { + alias {DESTDIR}/web/public/; + index index.php; + if ($scheme = http) { rewrite ^ https://$server_name$request_uri? permanent; } - index index.php; - try_files $uri $uri/ index.php; - location ~ [^/]\.php(/|$) { + + # The seemingly weird syntax is due to a long-standing bug in nginx, + # see: https://trac.nginx.org/nginx/ticket/97 + try_files $uri $uri/ {PATH}/{PATH}/index.php$is_args$args; + + location ~ ^{PATH}/index\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; - fastcgi_pass unix:/var/run/php5-fpm.sock; + fastcgi_pass unix:/var/run/php5-fpm-agendav.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param REMOTE_USER $remote_user; @@ -15,6 +20,13 @@ location {LOCATION} { fastcgi_param SCRIPT_FILENAME $request_filename; } + location ~ \.php$ { + return 404; + } + # Include SSOWAT user panel. include conf.d/yunohost_panel.conf.inc; } + +# append trailing slash in case of a subpath +location = {LOCATION} { return 302 {PATH}/; } diff --git a/conf/php-fpm.conf b/conf/php-fpm.conf new file mode 100644 index 0000000..e249310 --- /dev/null +++ b/conf/php-fpm.conf @@ -0,0 +1,68 @@ +[{POOLNAME}] +; The address on which to accept FastCGI requests. +listen = /var/run/php5-fpm-{POOLNAME}.sock + +; Set permissions for unix socket, if one is used. +listen.owner = www-data +listen.group = www-data +listen.mode = 0600 + +; Unix user/group of processes. +user = www-data +group = www-data + +; Choose how the process manager will control the number of child processes. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes to be created when pm is set to 'dynamic'. +pm.max_children = 6 + +; The number of child processes created on startup. +pm.start_servers = 3 + +; The desired minimum number of idle server processes. +pm.min_spare_servers = 3 + +; The desired maximum number of idle server processes. +pm.max_spare_servers = 5 + +; The number of requests each child process should execute before respawning. +pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. +pm.status_path = /fpm-status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. +ping.path = /ping + +; The timeout for serving a single request after which the worker process will +; be killed. +request_terminate_timeout = 1d + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +request_slowlog_timeout = 5s + +; The log file for slow requests. +slowlog = /var/log/nginx/{POOLNAME}.slow.log + +; Set open file descriptor rlimit. +rlimit_files = 4096 + +; Set max core size rlimit. +rlimit_core = 0 + +; Chdir to this directory at the start. +chdir = {DESTDIR} + +; Redirect worker stdout and stderr into main error log. +catch_workers_output = yes + +; Do not clear environment in FPM workers. +clear_env = no + +; Additional php.ini defines, specific to this pool of workers. +; ... diff --git a/conf/settings.php b/conf/settings.php new file mode 100644 index 0000000..3398903 --- /dev/null +++ b/conf/settings.php @@ -0,0 +1,126 @@ + '{DBNAME}', + 'user' => '{DBUSER}', + 'password' => '{DBPASS}', + 'host' => 'localhost', + 'driver' => 'pdo_mysql' +]; + +// CSRF secret +$app['csrf.secret'] = '{ENCRYPTKEY}'; + +// Log path +$app['log.path'] = '{LOGDIR}/'; + +// Base URL +$app['caldav.baseurl'] = '{CALDAV_BASEURL}'; + +// Authentication method required by CalDAV server (basic or digest) +$app['caldav.authmethod'] = 'basic'; + +// Whether to show public CalDAV urls +$app['caldav.publicurls'] = true; + +// Whether to show public CalDAV urls +$app['caldav.baseurl.public'] = 'https://{CALDAV_DOMAIN}'; + +// Email attribute name +$app['principal.email.attribute'] = '{DAV:}email'; + +// Calendar sharing +$app['calendar.sharing'] = false; + +// Calendar sharing permissions. In case of doubt, do not modify them +// These defaults are only useful for DAViCal (http://wiki.davical.org/index.php/Permissions) +$app['calendar.sharing.permissions'] = [ + 'owner' => ['{DAV:}all'], + 'read-only' => ['{DAV:}read', '{urn:ietf:params:xml:ns:caldav}read-free-busy'], + 'read-write' => ['{DAV:}read', '{DAV:}write', '{urn:ietf:params:xml:ns:caldav}read-free-busy'], + 'default' => ['{urn:ietf:params:xml:ns:caldav}read-free-busy'] +]; + +// Default timezone +$app['defaults.timezone'] = '{TIMEZONE}'; + +// Default languajge +$app['defaults.language'] = '{LANGUAGE}'; + +// Default time format. Options: '12' / '24' +$app['defaults.time_format'] = '24'; + +/* + * Default date format. Options: + * + * - ymd: YYYY-mm-dd + * - dmy: dd-mm-YYYY + * - mdy: mm-dd-YYYY + */ +$app['defaults.date_format'] = 'ymd'; + +// Default first day of week. Options: 0 (Sunday), 1 (Monday) +$app['defaults.weekstart'] = 0; + +// Logout redirection. Optional +$main_domain = exec('cat /etc/yunohost/current_host'); +$app['logout.redirection'] = 'https://' . $main_domain . '/yunohost/sso/?action=logout'; + +// Calendar colors +$app['calendar.colors'] = [ + '03A9F4', // Light blue + '3F51B5', // Indigo + 'F44336', // Red + 'E91E63', // Pink + '9C27B0', // Purple + '673AB7', // Deep purple + + 'B3E5FC', // Pale light blue + 'C5CAE9', // Pale Indigo + 'FFCDD2', // Pale red + 'F8BBD0', // Pale pink + 'E1BEE7', // Pale purple + 'D1C4E9', // Pale deep purple + + '4CAF50', // Green + 'FFC107', // Yellow + 'CDDC39', // Lime + 'FF9800', // Orange + '795548', // Brown + '9E9E9E', // Gray + + 'C8E6C9', // Pale green + 'FFF9C4', // Pale yellow + 'F0F4C3', // Pale lime + 'FFE0B2', // Pale orange + 'D7CCC8', // Pale brown + 'F5F5F5', // Pale gray +]; + +/** + * Local configuration + */ + +$local_config = __DIR__ . '/local.php'; +if (file_exists($local_config)) { + require $local_config; +} diff --git a/manifest.json b/manifest.json index 2b35c56..777b247 100644 --- a/manifest.json +++ b/manifest.json @@ -8,7 +8,7 @@ }, "url": "http://agendav.org/", "license": "GPL-3", - "version": "1.2.6.2", + "version": "2.0.0-beta2", "maintainer": { "name": "julien", "email": "julien.malik@paraiso.me" @@ -42,6 +42,17 @@ }, "example": "/agendav", "default": "/agendav" + }, + { + "name": "language", + "ask": { + "en": "Default language to be used in AgenDAV", + "fr": "Langue par défaut à utiliser dans AgenDAV" + }, + "choices": [ + "de", "en", "es", "fr", "it", "nl" + ], + "default": "en" } ] } diff --git a/patches/00-add-http-auth.patch b/patches/00-add-http-auth.patch new file mode 100644 index 0000000..74153c9 --- /dev/null +++ b/patches/00-add-http-auth.patch @@ -0,0 +1,56 @@ +--- a/web/app/controllers.php ++++ b/web/app/controllers.php +@@ -71,13 +71,20 @@ $controllers->before(function(Request $request, Silex\Application $app) { + // processing the request + if ($app['session']->has('username')) { + $username = $app['session']->get('username'); +- $preferences = $app['preferences.repository']->userPreferences($username); +- $app['user.preferences'] = $preferences; +- $app['user.timezone'] = $preferences->get('timezone'); + +- // Set application language +- $app['locale'] = $preferences->get('language'); +- return; ++ // Clear user session if HTTP authentication changed ++ if (isset($_SERVER['PHP_AUTH_USER']) ++ && $username != $_SERVER['PHP_AUTH_USER']) { ++ $app['session']->clear(); ++ } else { ++ $preferences = $app['preferences.repository']->userPreferences($username); ++ $app['user.preferences'] = $preferences; ++ $app['user.timezone'] = $preferences->get('timezone'); ++ ++ // Set application language ++ $app['locale'] = $preferences->get('language'); ++ return; ++ } + } + + if ($request->isXmlHttpRequest()) { +--- a/web/src/Controller/Authentication.php ++++ b/web/src/Controller/Authentication.php +@@ -33,7 +33,7 @@ class Authentication + { + $template_vars = []; + +- if ($request->isMethod('POST')) { ++ if ($request->isMethod('POST') || isset($_SERVER['PHP_AUTH_USER'])) { + $result = $this->processLogin($request, $app); + + if ($result === true) { +@@ -62,8 +62,13 @@ class Authentication + + protected function processLogin(Request $request, Application $app) + { +- $user = $request->request->get('user'); +- $password = $request->request->get('password'); ++ if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { ++ $user = $_SERVER['PHP_AUTH_USER']; ++ $password = $_SERVER['PHP_AUTH_PW']; ++ } else { ++ $user = $request->request->get('user'); ++ $password = $request->request->get('password'); ++ } + + if (empty($user) || empty($password)) { + return $app['translator']->trans('messages.error_empty_fields'); diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..b1e9e3c --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,54 @@ +# +# Common variables +# + +# AgenDAV version +VERSION="2.0.0-beta2" + +# Source tarball checksum +SOURCE_SHA256="5cbb76c08ef1c669e5735473a963ca62b835fd43955d31cc1b6d8d32672bc54d" + +# Source tarball URL +SOURCE_URL="https://github.com/adobo/agendav/releases/download/${VERSION}/agendav-${VERSION}.tar.gz" + +# App package root directory should be the parent folder +PKGDIR=$(cd ../; pwd) + +# Associative array of languages +declare -A LANGUAGES=( + [nl]=nl_NL + [en]=en + [fr]=fr_FR + [de]=de_DE + [it]=it_IT + [es]=es_ES +) + +# +# Common helpers +# + +# Source app helpers +source /usr/share/yunohost/helpers + +# Download and extract AgenDAV sources to the given directory +# usage: extract_agendav DESTDIR +extract_agendav() { + local DESTDIR=$1 + + # retrieve and extract tarball + tarball_path="/tmp/agendav.tar.gz" + rm -f "$tarball_path" + wget -q -O "$tarball_path" "$SOURCE_URL" \ + || ynh_die "Unable to download AgenDAV archive" + echo "$SOURCE_SHA256 $tarball_path" | sha256sum -c >/dev/null \ + || ynh_die "Invalid checksum of downloaded archive" + tar xf "$tarball_path" -C "$DESTDIR" --strip-components 1 \ + || ynh_die "Unable to extract AgenDAV archive" + rm -rf "$tarball_path" + + # apply patches + (cd "$DESTDIR" \ + && for p in ${PKGDIR}/patches/*.patch; do patch -p1 < $p; done) \ + || die "Unable to apply patches to AgenDAV" +} diff --git a/scripts/install b/scripts/install index ecea94b..a4c3b7b 100644 --- a/scripts/install +++ b/scripts/install @@ -1,13 +1,14 @@ #!/bin/bash -set -e +set -eu # Retrieve arguments domain=$1 path=${2%/} +language=$3 -# Source app helpers -. /usr/share/yunohost/helpers +# Source common variables and helpers +source ./_common.sh # Set app specific variables app="agendav" @@ -18,6 +19,10 @@ dbuser=$app sudo yunohost app checkurl "${domain}${path}" -a "$app" \ || exit 1 +# Set and store language +language=${LANGUAGES[$3]} +ynh_app_setting_set "$app" language "$language" + # Check destination directory DESTDIR="/var/www/${app}" [[ -d "$DESTDIR" ]] && ynh_die \ @@ -27,12 +32,10 @@ DESTDIR="/var/www/${app}" # Check whether Baïkal or Radicale is installed if sudo yunohost app list --installed -f baikal | grep -q id ; then caldav_app="baikal" - caldav_principal_path="/cal.php/%u/" - caldav_calendar_path="/cal.php/calendars/%s/" + caldav_baseurl="/cal.php/" elif sudo yunohost app list --installed -f radicale | grep -q id ; then caldav_app="radicale" - caldav_principal_path="/%u/" - caldav_calendar_path="/%s/" + caldav_baseurl="/" else ynh_die "You must install Baïkal or Radicale before" fi @@ -41,64 +44,65 @@ fi ynh_package_is_installed "php5-cli" \ || ynh_package_install "php5-cli" +# Create tmp directory and fetch app inside +TMPDIR=$(ynh_mkdir_tmp) +extract_agendav "$TMPDIR" + # Generate random password and encryption key dbpass=$(ynh_string_random) encryptkey=$(ynh_string_random 24) +ynh_app_setting_set "$app" encryptkey "$encryptkey" +ynh_app_setting_set "$app" mysqlpwd "$dbpass" -# Initialize database -ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass" -ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" \ - < "../sources/sql/mysql.schema.sql" +# Create log directory +LOGDIR=/var/log/agendav +sudo install -m 750 -o www-data -d "$LOGDIR" -# Copy files to the right place -sudo cp -r ../sources "$DESTDIR" +# Copy and set AgenDAV configuration +conf_path="${TMPDIR}/web/config/settings.php" +cp ../conf/settings.php "$conf_path" +sed -i "s/{DBUSER}/${dbuser}/g" "$conf_path" +sed -i "s/{DBPASS}/${dbpass}/g" "$conf_path" +sed -i "s/{DBNAME}/${dbname}/g" "$conf_path" +sed -i "s/{ENCRYPTKEY}/${encryptkey}/g" "$conf_path" +sed -i "s@{LOGDIR}@${LOGDIR}@g" "$conf_path" +sed -i "s@{TIMEZONE}@$(cat /etc/timezone)@g" "$conf_path" +sed -i "s@{LANGUAGE}@${language}@g" "$conf_path" # CalDAV config caldav_domain=$(ynh_app_setting_get "$caldav_app" domain) caldav_path=$(ynh_app_setting_get "$caldav_app" path) caldav_url="https://${caldav_domain}${caldav_path%/}" -sed -i "s@{PRINCIPAL_URL}@${caldav_url}${caldav_principal_path}@g" \ - ../conf/caldav.php -sed -i "s@{CALENDAR_URL}@${caldav_url}${caldav_calendar_path}@g" \ - ../conf/caldav.php +sed -i "s@{CALDAV_BASEURL}@${caldav_url}${caldav_baseurl}@g" "$conf_path" +sed -i "s@{CALDAV_DOMAIN}@${caldav_domain}@g" "$conf_path" -# Database config -sed -i "s/{DBUSER}/${dbuser}/g" ../conf/database.php -sed -i "s/{DBPASS}/${dbpass}/g" ../conf/database.php -sed -i "s/{DBNAME}/${dbname}/g" ../conf/database.php - -# Main config -LOGDIR=/var/log/agendav -lang=${LANG/.*/} -sed -i "s@{DOMAIN}@${domain}@g" ../conf/config.php -sed -i "s@{PATH}@${path}@g" ../conf/config.php -sed -i "s@{LOGDIR}@${LOGDIR}@g" ../conf/config.php -sed -i "s/{ENCRYPTKEY}/${encryptkey}/g" ../conf/config.php -sed -i "s@{COOKIE_PREFIX}@${path#/}@g" ../conf/config.php -sed -i "s@{COOKIE_DOMAIN}@${domain}@g" ../conf/config.php -sed -i "s@{LANG}@${lang:-en_EN}@g" ../conf/config.php -sed -i "s@{TIMEZONE}@$(cat /etc/timezone)@g" ../conf/config.php - -# Copy config files and set permissions -sudo cp ../conf/{config,database,caldav}.php "${DESTDIR}/web/config/" -(cd "$DESTDIR/web/application" && sudo ln -s ../config config) +# Install files and set permissions +sudo mv "$TMPDIR" "$DESTDIR" sudo chown -hR root: "$DESTDIR" +sudo chown -hR www-data: "${DESTDIR}/web" +sudo chmod -R 750 "${DESTDIR}/web/var" -# Create log directory -sudo install -m 750 -o www-data -d "$LOGDIR" - -# Execute database shema update -sudo /var/www/agendav/bin/agendavcli dbupdate - -# Save app settings -ynh_app_setting_set "$app" encryptkey "$encryptkey" -ynh_app_setting_set "$app" mysqlpwd "$dbpass" +# Initialize database +ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass" +(cd "$DESTDIR" && sudo sudo -u www-data \ + php agendavcli migrations:migrate --no-interaction) \ + || ynh_die "Unable to create AgenDAV tables" # Copy and set nginx configuration nginx_conf="/etc/nginx/conf.d/${domain}.d/${app}.conf" +sed -i "s@{PATH}@${path}@g" ../conf/nginx.conf sed -i "s@{LOCATION}@${path:-/}@g" ../conf/nginx.conf sed -i "s@{DESTDIR}@${DESTDIR}@g" ../conf/nginx.conf +# comment redirection in case of an installation at root +[[ -n "$path" ]] || sed -i '$s/^/#/' ../conf/nginx.conf sudo cp ../conf/nginx.conf "$nginx_conf" +# Copy and set php-fpm configuration +phpfpm_conf="/etc/php5/fpm/pool.d/${app}.conf" +sed -i "s@{POOLNAME}@${app}@g" ../conf/php-fpm.conf +sed -i "s@{DESTDIR}@${DESTDIR}/@g" ../conf/php-fpm.conf +sudo cp ../conf/php-fpm.conf "$phpfpm_conf" + # Reload services +sudo service php5-fpm restart sudo service nginx reload diff --git a/scripts/remove b/scripts/remove index df199b3..110cf1d 100644 --- a/scripts/remove +++ b/scripts/remove @@ -5,8 +5,8 @@ app="agendav" dbname=$app dbuser=$app -# Source app helpers -. /usr/share/yunohost/helpers +# Source YunoHost helpers +source /usr/share/yunohost/helpers # Drop MySQL database and user ynh_mysql_drop_db "$dbname" || true @@ -17,7 +17,9 @@ domain=$(ynh_app_setting_get "$app" domain) # Delete app directory and configurations sudo rm -rf "/var/www/${app}" "/var/log/${app}" +sudo rm -f "/etc/php5/fpm/pool.d/${app}.conf" [[ -n $domain ]] && sudo rm -f "/etc/nginx/conf.d/${domain}.d/${app}.conf" # Reload services +sudo service php5-fpm restart || true sudo service nginx reload || true