diff --git a/dev2/sites.d/webmail.profullstack.com.json b/dev2/sites.d/webmail.profullstack.com.json index e4abb29..fc74a33 100644 --- a/dev2/sites.d/webmail.profullstack.com.json +++ b/dev2/sites.d/webmail.profullstack.com.json @@ -3,7 +3,7 @@ "service": "webmail.profullstack.com", "repo": null, "branch": null, - "image": "profullstack/webmail:1.7.5-cal3", + "image": "profullstack/webmail:1.7.5-cal5", "start_command": "apache2-foreground", "port_var": "80", "domains": [ diff --git a/dev2/webmail/Dockerfile b/dev2/webmail/Dockerfile index 73c4811..448078d 100644 --- a/dev2/webmail/Dockerfile +++ b/dev2/webmail/Dockerfile @@ -34,5 +34,9 @@ RUN f=/usr/src/roundcubemail/plugins/calendar/lib/calendar_ui.php \ && sed -i "s#html::div('col-sm-8', \$select->show(1))#html::div('col-sm-8', \$select->show(0))#" $f \ && grep -c "select->show(0))" $f +# Exact-match source patches (fail the build if upstream moves): see patches.php. +COPY patches.php /tmp/patches.php +RUN php /tmp/patches.php && php -l /usr/src/roundcubemail/plugins/libcalendaring/lib/libcalendaring_recurrence.php && rm /tmp/patches.php + COPY --chmod=755 googlecal /usr/src/roundcubemail/plugins/googlecal COPY --chmod=644 carddav.config.inc.php /usr/src/roundcubemail/plugins/carddav/config.inc.php diff --git a/dev2/webmail/googlecal/googlecal.php b/dev2/webmail/googlecal/googlecal.php index b563177..4b1db05 100644 --- a/dev2/webmail/googlecal/googlecal.php +++ b/dev2/webmail/googlecal/googlecal.php @@ -119,6 +119,14 @@ public function callback() $this->rc->output->redirect($back); return; } + // Google's consent screen shows calendar access as its own checkbox; without it + // every CalDAV call is 403 insufficientPermissions. Refuse instead of half-connecting. + if (strpos((string) ($r['scope'] ?? ''), 'https://www.googleapis.com/auth/calendar') === false) { + $this->post(self::REVOKE_URL, ['token' => $r['refresh_token']]); + $this->rc->output->show_message('Google did not grant calendar access. Click Connect again and tick "See, edit, share, and permanently delete all the calendars" on Google\'s screen.', 'error', null, true, 30); + $this->rc->output->redirect($back); + return; + } $email = ''; if (!empty($r['id_token']) && count($parts = explode('.', $r['id_token'])) === 3) { $claims = json_decode(base64_decode(strtr($parts[1], '-_', '+/')), true); diff --git a/dev2/webmail/patches.php b/dev2/webmail/patches.php new file mode 100644 index 0000000..f9aa2cb --- /dev/null +++ b/dev2/webmail/patches.php @@ -0,0 +1,58 @@ + [[ + <<<'OLD' + if ($date instanceof DateTimeInterface) { + $date = libcalendaring_datetime::createFromFormat( + 'Y-m-d\\TH:i:s', + $date->format('Y-m-d\\TH:i:s'), + // Sabre will loose timezone on all-day events, use the event start's timezone + $this->start->getTimezone() + ); + } + + $date->_dateonly = $this->dateonly; +OLD, + <<<'NEW' + if ($date instanceof DateTimeInterface) { + $src = $date; + $date = libcalendaring_datetime::createFromFormat( + 'Y-m-d\\TH:i:s', + $date->format('Y-m-d\\TH:i:s'), + // Sabre will loose timezone on all-day events, use the event start's timezone + $this->start->getTimezone() + ); + if (!$date) { + $date = new libcalendaring_datetime('@' . $src->getTimestamp()); + $date->setTimezone($this->start->getTimezone()); + } + } + + if (!is_object($date)) { + return false; + } + + $date->_dateonly = $this->dateonly; +NEW, + ]], +]; + +foreach ($patches as $file => $list) { + $src = file_get_contents($file); + foreach ($list as [$old, $new]) { + if (substr_count($src, $old) !== 1) { + fwrite(STDERR, "patch does not apply to $file\n"); + exit(1); + } + $src = str_replace($old, $new, $src); + } + file_put_contents($file, $src); + echo "patched $file\n"; +}