Skip to content

Commit d9c8949

Browse files
sh1pt-actions-fleet[bot]ralyodioclaude
authored
dev2 webmail: calendar, tasks and contacts (CalDAV/CardDAV) (#231)
- webmail/Dockerfile -> profullstack/webmail:1.7.5-cal1 (built on dev2): stock Roundcube 1.7.5 + kolab/calendar + kolab/tasklist 3.6.1 + roundcube/carddav 5.1.5. pear/http_request2 2.8.0 aliased as 2.5.1 (libkolab pins ~2.5.0, CVE-2025-43717); SKIP_DB_INIT at build; libkolab sqlite schema's duplicate ix_contact_type renamed. - anyhost: startup hook picks calendar/tasklist drivers per session: Forward Email -> caldav.forwardemail.net, any other server -> Roundcube database calendars. - carddav preset: Forward Email address book auto-added for FE sessions only. Co-authored-by: Anthony Ettinger <anthony@profullstack.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent babfffe commit d9c8949

4 files changed

Lines changed: 69 additions & 3 deletions

File tree

‎dev2/sites.d/webmail.profullstack.com.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"service": "webmail.profullstack.com",
44
"repo": null,
55
"branch": null,
6-
"image": "roundcube/roundcubemail:1.7.5-apache",
6+
"image": "profullstack/webmail:1.7.5-cal1",
77
"start_command": "apache2-foreground",
88
"port_var": "80",
99
"domains": [
@@ -48,9 +48,9 @@
4848
"ROUNDCUBEMAIL_DEFAULT_PORT": "993",
4949
"ROUNDCUBEMAIL_SMTP_SERVER": "ssl://smtp.forwardemail.net",
5050
"ROUNDCUBEMAIL_SMTP_PORT": "465",
51-
"ROUNDCUBEMAIL_PLUGINS": "anyhost,archive,zipdownload",
51+
"ROUNDCUBEMAIL_PLUGINS": "anyhost,archive,zipdownload,libcalendaring,libkolab,calendar,tasklist,carddav",
5252
"ROUNDCUBEMAIL_SKIN": "elastic",
5353
"ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE": "25M"
5454
},
55-
"notes": "Not a Railway site: born on dev2 2026-11-02. Primary host webmail.profullstack.com (A -> dev2); webmail.pit.world kept as an alias. Cert by HTTP-01 (both names resolve to dev2). Roundcube webmail, stock image, no repo. Login form has a Server field: users sign in to ANY IMAP server (blank = Forward Email, imap.forwardemail.net). The anyhost plugin (cli-tools dev2/webmail/anyhost, copied to volumes/plugin-anyhost) turns a bare hostname into ssl://host:993, derives SMTP (imap.X -> smtp.X, else the same host; 465 then 587) and refuses private/loopback addresses so the form cannot probe dev2. volumes/config/zz-profullstack.inc.php holds des_key (minted on the box, never committed) and the imap_host override. mail.profullstack.com is NOT this site: it is the bbs box's Mailu MTA (PTR, SPF, cert)."
55+
"notes": "Not a Railway site: born on dev2 2026-11-02. Primary host webmail.profullstack.com (A -> dev2); webmail.pit.world kept as an alias. Cert by HTTP-01 (both names resolve to dev2). Roundcube webmail, stock image, no repo. Login form has a Server field: users sign in to ANY IMAP server (blank = Forward Email, imap.forwardemail.net). The anyhost plugin (cli-tools dev2/webmail/anyhost, copied to volumes/plugin-anyhost) turns a bare hostname into ssl://host:993, derives SMTP (imap.X -> smtp.X, else the same host; 465 then 587) and refuses private/loopback addresses so the form cannot probe dev2. volumes/config/zz-profullstack.inc.php holds des_key (minted on the box, never committed) and the imap_host override. mail.profullstack.com is NOT this site: it is the bbs box's Mailu MTA (PTR, SPF, cert). Since 2026-11-02 the image is profullstack/webmail:<tag>, built ON dev2 from cli-tools dev2/webmail/Dockerfile (docker build -t profullstack/webmail:<tag> dev2/webmail; not on any registry, deploy pull fails harmlessly): stock 1.7.5 + Kolab calendar/tasklist 3.6.1 + RCMCardDAV 5.1.5. FE sessions sync caldav.forwardemail.net / carddav.forwardemail.net; other servers get database calendars. Plugin schemas loaded once into volumes/db with bin/updatedb.sh."
5656
}

‎dev2/webmail/Dockerfile‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# webmail.profullstack.com: stock Roundcube + calendar/tasks (Kolab, CalDAV) + contacts (RCMCardDAV).
2+
# Built on dev2 as profullstack/webmail:<tag> (see README.md); the entrypoint copies
3+
# /usr/src/roundcubemail into /var/www/html on every fresh container.
4+
FROM roundcube/roundcubemail:1.7.5-apache
5+
6+
# libkolab pins pear/http_request2 ~2.5.0, which is under CVE-2025-43717 (XSS, fixed
7+
# 2.7.0); composer blocks it. Install the fixed 2.8.0 aliased as 2.5.1 instead of
8+
# ignoring the advisory.
9+
10+
# roundcube/plugin-installer runs each plugin's SQL on install; there is no database at
11+
# build time (SKIP_DB_INIT). The live schema is created on the box (README.md).
12+
RUN cd /usr/src/roundcubemail \
13+
&& SKIP_DB_INIT=1 SKIP_DB_UPDATE=1 COMPOSER_ALLOW_SUPERUSER=1 composer require --no-interaction --no-progress --update-no-dev --prefer-dist \
14+
"pear/http_request2:2.8.0 as 2.5.1" \
15+
kolab/calendar:3.6.1 kolab/tasklist:3.6.1 roundcube/carddav:5.1.5 \
16+
&& ls plugins
17+
18+
# libkolab 3.6.1 sqlite.initial.sql creates ix_contact_type twice (kolab_cache_contact and
19+
# kolab_cache_dav_contact); SQLite index names are per database, so init fails.
20+
RUN sed -i 's/^CREATE INDEX ix_contact_type ON kolab_cache_dav_contact/CREATE INDEX ix_dav_contact_type ON kolab_cache_dav_contact/' \
21+
/usr/src/roundcubemail/plugins/libkolab/SQL/sqlite.initial.sql \
22+
&& grep -c ix_dav_contact_type /usr/src/roundcubemail/plugins/libkolab/SQL/sqlite.initial.sql
23+
24+
COPY --chmod=644 carddav.config.inc.php /usr/src/roundcubemail/plugins/carddav/config.inc.php

‎dev2/webmail/anyhost/anyhost.php‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ public function init()
2525
$this->add_hook('authenticate', [$this, 'authenticate']);
2626
$this->add_hook('smtp_connect', [$this, 'smtp_connect']);
2727
$this->add_hook('template_object_loginform', [$this, 'loginform']);
28+
// calendar/tasklist load their own config.inc.php in init() (after this plugin) and
29+
// pick their driver in startup; our startup hook is registered first, so it wins.
30+
$this->add_hook('startup', function ($args) { $this->pick_dav_drivers(); return $args; });
31+
}
32+
33+
/**
34+
* Calendar + tasks backend for this session (anyhost loads before calendar/tasklist,
35+
* so this runs first): Forward Email users sync with caldav.forwardemail.net; every
36+
* other server gets Roundcube's own database calendar (Mailu has no CalDAV).
37+
*/
38+
private function pick_dav_drivers()
39+
{
40+
$config = rcmail::get_instance()->config;
41+
if ((string) ($_SESSION['storage_host'] ?? '') === 'imap.forwardemail.net') {
42+
$config->set('calendar_driver', 'caldav');
43+
$config->set('calendar_caldav_server', 'https://caldav.forwardemail.net');
44+
$config->set('tasklist_driver', 'caldav');
45+
$config->set('tasklist_caldav_server', 'https://caldav.forwardemail.net');
46+
} else {
47+
$config->set('calendar_driver', 'database');
48+
$config->set('tasklist_driver', 'database');
49+
}
2850
}
2951

3052
public function loginform($args)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
// RCMCardDAV presets for webmail.profullstack.com.
3+
// The login form takes any IMAP server, so the Forward Email address book is only
4+
// set up for sessions signed in to imap.forwardemail.net. Everyone else can still
5+
// add an address book by hand in Settings > CardDAV.
6+
$prefs['_GLOBAL']['pwstore_scheme'] = 'des_key';
7+
$prefs['_GLOBAL']['loglevel'] = \Psr\Log\LogLevel::WARNING;
8+
9+
if ((string) ($_SESSION['storage_host'] ?? '') === 'imap.forwardemail.net') {
10+
$prefs['forwardemail'] = [
11+
'accountname' => 'Forward Email',
12+
'discovery_url' => 'https://carddav.forwardemail.net/dav/',
13+
'username' => '%u',
14+
'password' => '%p',
15+
'fixed' => ['discovery_url', 'username', 'password'],
16+
'refresh_time' => '00:15:00',
17+
'active' => true,
18+
'readonly' => false,
19+
];
20+
}

0 commit comments

Comments
 (0)