diff --git a/.env b/.env
index 39ee1f9..a92624c 100644
--- a/.env
+++ b/.env
@@ -48,9 +48,10 @@ AUTH_REALM=SabreDAV
# "Basic", "IMAP", or "LDAP"
AUTH_METHOD=Basic
-# In case of IMAP Auth, you must specify the url of the mailbox in the following format {host[:port][/flag1/flag2...]}.
-# See https://www.php.net/manual/en/function.imap-open.php for more details
+# In case of IMAP Auth, you must specify the url of the mailbox in the following format host[:port].
IMAP_AUTH_URL=null
+IMAP_ENCRYPTION_METHOD=ssl
+IMAP_CERTIFICATE_VALIDATION=true
IMAP_AUTH_USER_AUTOCREATE=false
# In case of LDAP Auth, you must specify the url of the LDAP server
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3eb67fa..ee9efa5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,11 +27,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- - name: Install GD PHP extension
+ - name: Install GD / ZIP PHP extension
run: |
- apk add $PHPIZE_DEPS libpng-dev
+ apk add $PHPIZE_DEPS libpng-dev libzip-dev
docker-php-ext-configure gd
- docker-php-ext-install gd
+ docker-php-ext-configure zip
+ docker-php-ext-install gd zip
- name: Install Composer
run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- name: Validate Composer
@@ -70,16 +71,18 @@ jobs:
php:
- '8.2'
- '8.3'
+ - '8.4'
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4
- - name: Install MySQL / GD PHP extensions
+ - name: Install MySQL / GD / ZIP PHP extensions
run: |
- apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev
+ apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev libzip-dev
docker-php-ext-configure intl
docker-php-ext-configure gd
- docker-php-ext-install pdo pdo_mysql intl gd
+ docker-php-ext-configure zip
+ docker-php-ext-install pdo pdo_mysql intl gd zip
- name: Install Composer
run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
- name: Install dependencies with Composer
diff --git a/README.md b/README.md
index 386194b..aba1cde 100644
--- a/README.md
+++ b/README.md
@@ -239,16 +239,18 @@ Or use it directly in the Apache configuration
### Specific environment variables for IMAP and LDAP authentication methods
-In case you use the `IMAP` auth type, you must specify the auth url (_the "mailbox" url_) in `IMAP_AUTH_URL`. See https://www.php.net/manual/en/function.imap-open.php for more details.
+In case you use the `IMAP` auth type, you must specify the auth url (_the "mailbox" url_) in `IMAP_AUTH_URL` as `host:port`, the encryption method (SSL, TLS or None) and whether the certificate should be validated.
-You should also explicitely define whether you want new authenticated to be created upon login:
+You should also explicitely define whether you want new authenticated users to be created upon login:
```shell
-IMAP_AUTH_URL={imap.gmail.com:993/imap/ssl/novalidate-cert}
+IMAP_AUTH_URL=imap.mydomain.com:993
+IMAP_ENCRYPTION_METHOD=ssl # ssl, tls or false
+IMAP_CERTIFICATE_VALIDATION=true
IMAP_AUTH_USER_AUTOCREATE=true # false by default
```
-Same goes for LDAP, where you must specify the LDAP server url, the DN pattern, the Mail attribute, as well as whether you want new authenticated to be created upon login (_like for IMAP_):
+Same goes for LDAP, where you must specify the LDAP server url, the DN pattern, the Mail attribute, as well as whether you want new authenticated users to be created upon login (_like for IMAP_):
```shell
LDAP_AUTH_URL=ldap://127.0.0.1:3890 # default LDAP port
diff --git a/composer.json b/composer.json
index 7ce9ad1..e454ffa 100644
--- a/composer.json
+++ b/composer.json
@@ -8,6 +8,7 @@
"ext-ctype": "*",
"ext-gd": "*",
"ext-iconv": "*",
+ "ext-zip": "*",
"composer-runtime-api": "^2",
"dantsu/php-osm-static-api": "^0.6.4",
"doctrine/doctrine-bundle": "^2.15.1",
@@ -37,7 +38,8 @@
"symfony/twig-bundle": "^7.3",
"symfony/validator": "^7.3",
"symfony/web-link": "^7.3",
- "symfony/yaml": "^7.3"
+ "symfony/yaml": "^7.3",
+ "webklex/php-imap": "^6.2"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.5",
diff --git a/composer.lock b/composer.lock
index 50e83cd..796d029 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,77 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4f919c5e56d4687211f93f8211d01299",
+ "content-hash": "10757dc8d60ac836ef824e271e598ca8",
"packages": [
+ {
+ "name": "carbonphp/carbon-doctrine-types",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+ "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb",
+ "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/dbal": "<3.7.0 || >=4.0.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.7.0",
+ "nesbot/carbon": "^2.71.0 || ^3.0.0",
+ "phpunit/phpunit": "^10.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "KyleKatarn",
+ "email": "kylekatarnls@gmail.com"
+ }
+ ],
+ "description": "Types to use Carbon in Doctrine",
+ "keywords": [
+ "carbon",
+ "date",
+ "datetime",
+ "doctrine",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+ "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-11T17:09:12+00:00"
+ },
{
"name": "dantsu/php-image-editor",
"version": "1.4.5",
@@ -1514,6 +1583,334 @@
],
"time": "2025-03-06T22:45:56+00:00"
},
+ {
+ "name": "illuminate/collections",
+ "version": "v12.26.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/collections.git",
+ "reference": "a6d56e21c12040d49a5f5fec7316d990b515cbf4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/collections/zipball/a6d56e21c12040d49a5f5fec7316d990b515cbf4",
+ "reference": "a6d56e21c12040d49a5f5fec7316d990b515cbf4",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/conditionable": "^12.0",
+ "illuminate/contracts": "^12.0",
+ "illuminate/macroable": "^12.0",
+ "php": "^8.2",
+ "symfony/polyfill-php84": "^1.33",
+ "symfony/polyfill-php85": "^1.33"
+ },
+ "suggest": {
+ "illuminate/http": "Required to convert collections to API resources (^12.0).",
+ "symfony/var-dumper": "Required to use the dump method (^7.2)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "functions.php",
+ "helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Collections package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-08-29T13:30:10+00:00"
+ },
+ {
+ "name": "illuminate/conditionable",
+ "version": "v12.26.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/conditionable.git",
+ "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/conditionable/zipball/ec677967c1f2faf90b8428919124d2184a4c9b49",
+ "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Conditionable package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-05-13T15:08:45+00:00"
+ },
+ {
+ "name": "illuminate/contracts",
+ "version": "v12.26.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/contracts.git",
+ "reference": "f1c4cf02c9ab81a9ce47940cf261fa2386ed6c5d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/f1c4cf02c9ab81a9ce47940cf261fa2386ed6c5d",
+ "reference": "f1c4cf02c9ab81a9ce47940cf261fa2386ed6c5d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.2",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/simple-cache": "^1.0|^2.0|^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Contracts\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Contracts package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-08-27T18:34:41+00:00"
+ },
+ {
+ "name": "illuminate/macroable",
+ "version": "v12.26.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/macroable.git",
+ "reference": "e862e5648ee34004fa56046b746f490dfa86c613"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/macroable/zipball/e862e5648ee34004fa56046b746f490dfa86c613",
+ "reference": "e862e5648ee34004fa56046b746f490dfa86c613",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Macroable package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2024-07-23T16:31:01+00:00"
+ },
+ {
+ "name": "illuminate/pagination",
+ "version": "v12.26.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/pagination.git",
+ "reference": "5561f16aec3cee1d53f254105327a4ee8d237fb5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/pagination/zipball/5561f16aec3cee1d53f254105327a4ee8d237fb5",
+ "reference": "5561f16aec3cee1d53f254105327a4ee8d237fb5",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "illuminate/collections": "^12.0",
+ "illuminate/contracts": "^12.0",
+ "illuminate/support": "^12.0",
+ "php": "^8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Pagination\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Pagination package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-08-21T19:25:55+00:00"
+ },
+ {
+ "name": "illuminate/support",
+ "version": "v12.26.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/support.git",
+ "reference": "7952d50ab6a91a9c2f2e21eed41a00d1f53e5ae9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/support/zipball/7952d50ab6a91a9c2f2e21eed41a00d1f53e5ae9",
+ "reference": "7952d50ab6a91a9c2f2e21eed41a00d1f53e5ae9",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/inflector": "^2.0",
+ "ext-ctype": "*",
+ "ext-filter": "*",
+ "ext-mbstring": "*",
+ "illuminate/collections": "^12.0",
+ "illuminate/conditionable": "^12.0",
+ "illuminate/contracts": "^12.0",
+ "illuminate/macroable": "^12.0",
+ "nesbot/carbon": "^3.8.4",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.33",
+ "symfony/polyfill-php85": "^1.33",
+ "voku/portable-ascii": "^2.0.2"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "replace": {
+ "spatie/once": "*"
+ },
+ "suggest": {
+ "illuminate/filesystem": "Required to use the Composer class (^12.0).",
+ "laravel/serializable-closure": "Required to use the once function (^1.3|^2.0).",
+ "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.7).",
+ "league/uri": "Required to use the Uri class (^7.5.1).",
+ "ramsey/uuid": "Required to use Str::uuid() (^4.7).",
+ "symfony/process": "Required to use the Composer class (^7.2).",
+ "symfony/uid": "Required to use Str::ulid() (^7.2).",
+ "symfony/var-dumper": "Required to use the dd function (^7.2).",
+ "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "functions.php",
+ "helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Support package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2025-08-29T13:27:38+00:00"
+ },
{
"name": "monolog/monolog",
"version": "3.9.0",
@@ -1556,31 +1953,127 @@
"symfony/mailer": "^5.4 || ^6",
"symfony/mime": "^5.4 || ^6"
},
- "suggest": {
- "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
- "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
- "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
- "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
- "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
- "ext-mbstring": "Allow to work properly with unicode symbols",
- "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
- "ext-openssl": "Required to send log messages using SSL",
- "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
- "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
- "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
- "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
- "rollbar/rollbar": "Allow sending log messages to Rollbar",
- "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "ext-openssl": "Required to send log messages using SSL",
+ "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/3.9.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-03-24T10:02:05+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "3.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon.git",
+ "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24",
+ "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24",
+ "shasum": ""
+ },
+ "require": {
+ "carbonphp/carbon-doctrine-types": "<100.0",
+ "ext-json": "*",
+ "php": "^8.1",
+ "psr/clock": "^1.0",
+ "symfony/clock": "^6.3.12 || ^7.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6.3 || ^4.0",
+ "doctrine/orm": "^2.15.2 || ^3.0",
+ "friendsofphp/php-cs-fixer": "^3.75.0",
+ "kylekatarnls/multi-tester": "^2.5.3",
+ "phpmd/phpmd": "^2.15.0",
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^2.1.17",
+ "phpunit/phpunit": "^10.5.46",
+ "squizlabs/php_codesniffer": "^3.13.0"
},
+ "bin": [
+ "bin/carbon"
+ ],
"type": "library",
"extra": {
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
"branch-alias": {
- "dev-main": "3.x-dev"
+ "dev-2.x": "2.x-dev",
+ "dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
- "Monolog\\": "src/Monolog"
+ "Carbon\\": "src/Carbon/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1589,33 +2082,42 @@
],
"authors": [
{
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "https://seld.be"
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
}
],
- "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
- "homepage": "https://github.com/Seldaek/monolog",
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbon.nesbot.com",
"keywords": [
- "log",
- "logging",
- "psr-3"
+ "date",
+ "datetime",
+ "time"
],
"support": {
- "issues": "https://github.com/Seldaek/monolog/issues",
- "source": "https://github.com/Seldaek/monolog/tree/3.9.0"
+ "docs": "https://carbon.nesbot.com/docs",
+ "issues": "https://github.com/CarbonPHP/carbon/issues",
+ "source": "https://github.com/CarbonPHP/carbon"
},
"funding": [
{
- "url": "https://github.com/Seldaek",
+ "url": "https://github.com/sponsors/kylekatarnls",
"type": "github"
},
{
- "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "url": "https://opencollective.com/Carbon#sponsor",
+ "type": "opencollective"
+ },
+ {
+ "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
"type": "tidelift"
}
],
- "time": "2025-03-24T10:02:05+00:00"
+ "time": "2025-08-02T09:36:06+00:00"
},
{
"name": "psr/cache",
@@ -1923,6 +2425,57 @@
},
"time": "2024-09-11T13:17:53+00:00"
},
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
{
"name": "sabre/dav",
"version": "4.7.0",
@@ -5863,6 +6416,86 @@
],
"time": "2025-06-24T13:30:11+00:00"
},
+ {
+ "name": "symfony/polyfill-php85",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php85.git",
+ "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
+ "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php85\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-23T16:12:55+00:00"
+ },
{
"name": "symfony/process",
"version": "v7.3.3",
@@ -7931,6 +8564,161 @@
}
],
"time": "2025-05-03T07:21:55+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d",
+ "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lars Moelleken",
+ "homepage": "https://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
+ "keywords": [
+ "ascii",
+ "clean",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-21T01:49:47+00:00"
+ },
+ {
+ "name": "webklex/php-imap",
+ "version": "6.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Webklex/php-imap.git",
+ "reference": "6b8ef85d621bbbaf52741b00cca8e9237e2b2e05"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Webklex/php-imap/zipball/6b8ef85d621bbbaf52741b00cca8e9237e2b2e05",
+ "reference": "6b8ef85d621bbbaf52741b00cca8e9237e2b2e05",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "ext-iconv": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-zip": "*",
+ "illuminate/pagination": ">=5.0.0",
+ "nesbot/carbon": "^2.62.1|^3.2.4",
+ "php": "^8.0.2",
+ "symfony/http-foundation": ">=2.8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5.10"
+ },
+ "suggest": {
+ "symfony/mime": "Recomended for better extension support",
+ "symfony/var-dumper": "Usefull tool for debugging"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webklex\\PHPIMAP\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Malte Goldenbaum",
+ "email": "github@webklex.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP IMAP client",
+ "homepage": "https://github.com/webklex/php-imap",
+ "keywords": [
+ "imap",
+ "mail",
+ "php-imap",
+ "pop3",
+ "webklex"
+ ],
+ "support": {
+ "issues": "https://github.com/Webklex/php-imap/issues",
+ "source": "https://github.com/Webklex/php-imap/tree/6.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.buymeacoffee.com/webklex",
+ "type": "custom"
+ },
+ {
+ "url": "https://ko-fi.com/webklex",
+ "type": "ko_fi"
+ }
+ ],
+ "time": "2025-04-25T06:02:37+00:00"
}
],
"packages-dev": [
@@ -11482,6 +12270,7 @@
"ext-ctype": "*",
"ext-gd": "*",
"ext-iconv": "*",
+ "ext-zip": "*",
"composer-runtime-api": "^2"
},
"platform-dev": {},
diff --git a/config/services.yaml b/config/services.yaml
index c20afdf..c583016 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -33,6 +33,8 @@ services:
App\Services\IMAPAuth:
arguments:
$IMAPAuthUrl: "%env(IMAP_AUTH_URL)%"
+ $IMAPEncryptionMethod: "%env(IMAP_ENCRYPTION_METHOD)%"
+ $IMAPCertificateValidation: "%env(bool:IMAP_CERTIFICATE_VALIDATION)%"
$autoCreate: "%env(bool:IMAP_AUTH_USER_AUTOCREATE)%"
App\Services\LDAPAuth:
@@ -40,8 +42,8 @@ services:
$LDAPAuthUrl: "%env(LDAP_AUTH_URL)%"
$LDAPDnPattern: "%env(LDAP_DN_PATTERN)%"
$LDAPMailAttribute: "%env(LDAP_MAIL_ATTRIBUTE)%"
- $autoCreate: "%env(bool:LDAP_AUTH_USER_AUTOCREATE)%"
$LDAPCertificateCheckingStrategy: "%env(LDAP_CERTIFICATE_CHECKING_STRATEGY)%"
+ $autoCreate: "%env(bool:LDAP_AUTH_USER_AUTOCREATE)%"
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
diff --git a/docker/.env b/docker/.env
index bfec096..be359dc 100644
--- a/docker/.env
+++ b/docker/.env
@@ -32,7 +32,9 @@ AUTH_METHOD=Basic # Basic or IMAP or LDAP
AUTH_REALM=SabreDAV
# IMAP auth settings
-IMAP_AUTH_URL={imap.gmail.com:993/imap/ssl/novalidate-cert}
+IMAP_AUTH_URL=imap.mydomain.com:993
+IMAP_ENCRYPTION_METHOD=ssl
+IMAP_CERTIFICATE_VALIDATION=true
IMAP_AUTH_USER_AUTOCREATE=false
# LDAP auth settings
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 9f6028d..6882c6b 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -56,11 +56,11 @@ RUN apk --update --virtual build-deps-ldap add --no-cache openldap-dev \
&& apk del build-deps-ldap \
&& rm -rf /tmp/*
-# IMAP auth support
-RUN apk --update --virtual build-deps-imap add --no-cache imap-dev openssl-dev krb5-dev \
- && docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
- && docker-php-ext-install imap \
- && apk del build-deps-imap \
+# Zip lib for PHP-IMAP
+RUN apk --update --virtual build-deps-zip add --no-cache libzip-dev \
+ && docker-php-ext-configure zip \
+ && docker-php-ext-install zip \
+ && apk del build-deps-zip \
&& rm -rf /tmp/*
# OPCache
diff --git a/docker/Dockerfile-standalone b/docker/Dockerfile-standalone
index d769e67..b29b482 100644
--- a/docker/Dockerfile-standalone
+++ b/docker/Dockerfile-standalone
@@ -57,11 +57,11 @@ RUN apk --update --virtual build-deps-ldap add --no-cache openldap-dev \
&& apk del build-deps-ldap \
&& rm -rf /tmp/*
-# IMAP auth support
-RUN apk --update --virtual build-deps-imap add --no-cache imap-dev openssl-dev krb5-dev \
- && docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
- && docker-php-ext-install imap \
- && apk del build-deps-imap \
+# Zip lib for PHP-IMAP
+RUN apk --update --virtual build-deps-zip add --no-cache libzip-dev \
+ && docker-php-ext-configure zip \
+ && docker-php-ext-install zip \
+ && apk del build-deps-zip \
&& rm -rf /tmp/*
# OPCache
diff --git a/src/Services/BirthdayService.php b/src/Services/BirthdayService.php
index be14e53..020f38f 100644
--- a/src/Services/BirthdayService.php
+++ b/src/Services/BirthdayService.php
@@ -229,7 +229,7 @@ public function buildDataFromContact(string $cardData): ?VCalendar
$vEvent->{'TRANSP'} = 'TRANSPARENT';
// Set a reminder, if needed
- if (strtolower($this->birthdayReminderOffset) !== "false") {
+ if ('false' !== strtolower($this->birthdayReminderOffset)) {
$alarm = $vCal->createComponent('VALARM');
$alarm->add($vCal->createProperty('TRIGGER', $this->birthdayReminderOffset, ['VALUE' => 'DURATION']));
$alarm->add($vCal->createProperty('ACTION', 'DISPLAY'));
diff --git a/src/Services/IMAPAuth.php b/src/Services/IMAPAuth.php
index f462e94..6aa78cf 100644
--- a/src/Services/IMAPAuth.php
+++ b/src/Services/IMAPAuth.php
@@ -4,9 +4,11 @@
use App\Entity\User;
use Doctrine\Persistence\ManagerRegistry;
-use Sabre\DAV\Auth\Backend\IMAP;
+use Sabre\DAV\Auth\Backend\AbstractBasic;
+use Webklex\PHPIMAP\Client;
+use Webklex\PHPIMAP\ClientManager;
-final class IMAPAuth extends IMAP
+final class IMAPAuth extends AbstractBasic
{
/**
* Doctrine registry.
@@ -30,9 +32,65 @@ final class IMAPAuth extends IMAP
*/
private $autoCreate;
- public function __construct(ManagerRegistry $doctrine, Utils $utils, string $IMAPAuthUrl, bool $autoCreate)
+ /**
+ * IMAP server host.
+ *
+ * @var string
+ */
+ private $IMAPHost;
+
+ /**
+ * IMAP server port.
+ *
+ * @var int
+ */
+ private $IMAPPort;
+
+ /**
+ * IMAP encryption method. Could be ssl, tls or false.
+ *
+ * @var mixed (string or bool)
+ */
+ private $IMAPEncryptionMethod;
+
+ /**
+ * Should we validate the certificate?
+ *
+ * @var bool
+ */
+ private $IMAPCertificateValidation;
+
+ public function __construct(ManagerRegistry $doctrine, Utils $utils, string $IMAPAuthUrl, bool $autoCreate, string $IMAPEncryptionMethod, bool $IMAPCertificateValidation)
{
- parent::__construct($IMAPAuthUrl);
+ $components = parse_url($IMAPAuthUrl);
+
+ if (!$components) {
+ throw new Exception('IMAP Error (parsing IMAP url "'.$IMAPAuthUrl.'"): '.$e->getMessage());
+ }
+
+ $this->IMAPHost = $components['host'] ?? null;
+
+ // Trying to choose the best port if it was not provided,
+ // defaulting to 993 (secure)
+ if (isset($components['port'])) {
+ $this->IMAPPort = $components['port'];
+ } elseif (false === $this->IMAPEncryptionMethod) {
+ $this->IMAPPort = 143;
+ } else {
+ $this->IMAPPort = 993;
+ }
+
+ // We're making sure that only ssl, tls or 'false' are passed down to the IMAP client,
+ // defaulting to SSL
+ $IMAPEncryptionMethodCleaned = strtolower($IMAPEncryptionMethod);
+ if ('false' === $IMAPEncryptionMethodCleaned) {
+ $this->IMAPEncryptionMethod = false;
+ } elseif ('tls' === $IMAPEncryptionMethodCleaned) {
+ $this->IMAPEncryptionMethod = 'tls';
+ } else {
+ $this->IMAPEncryptionMethod = 'ssl';
+ }
+ $this->IMAPCertificateValidation = $IMAPCertificateValidation;
$this->autoCreate = $autoCreate;
@@ -42,16 +100,31 @@ public function __construct(ManagerRegistry $doctrine, Utils $utils, string $IMA
/**
* Connects to an IMAP server and tries to authenticate.
- * If the user does not exist, create it.
- *
- * @param string $username
- * @param string $password
- *
- * @return bool
+ * If the user does not exist, create it (depending on the autoCreate flag).
*/
- protected function imapOpen($username, $password)
+ protected function imapOpen(string $username, string $password): bool
{
- $success = parent::imapOpen($username, $password);
+ $cm = new ClientManager($options = []);
+
+ // Create a new instance of the IMAP client manually
+ $client = $cm->make([
+ 'host' => $this->IMAPHost,
+ 'port' => $this->IMAPPort,
+ 'encryption' => $this->IMAPEncryptionMethod,
+ 'validate_cert' => $this->IMAPCertificateValidation,
+ 'username' => $username,
+ 'password' => $password,
+ 'protocol' => 'imap',
+ ]);
+
+ try {
+ $client->connect();
+ $client->disconnect();
+ $success = true;
+ } catch (\Exception $e) {
+ error_log('IMAP Error (connection): '.$e->getMessage());
+ $success = false;
+ }
// Auto-create the user if it does not already exist in the database
if ($success && $this->autoCreate) {
@@ -73,4 +146,12 @@ protected function imapOpen($username, $password)
return $success;
}
+
+ /**
+ * Validates a username and password by trying to authenticate against IMAP.
+ */
+ protected function validateUserPass($username, $password): bool
+ {
+ return $this->imapOpen($username, $password);
+ }
}
diff --git a/templates/dashboard.html.twig b/templates/dashboard.html.twig
index 3b7cac7..3ec0e13 100644
--- a/templates/dashboard.html.twig
+++ b/templates/dashboard.html.twig
@@ -42,7 +42,7 @@
{{ "dashboard.version"|trans }} : {{ version }} (SabreDAV {{ sabredav_version }})
-
{{ authMethod }} ({{ "dashboard.auth_realm"|trans }}: {{ authRealm }}){{ authMethod }}{% if authMethod == 'Basic' %} ({{ "dashboard.auth_realm"|trans }}: {{ authRealm }}){% endif %}{{ invite_from_address|default('Not set') }}{{ timezone.actual_default }}