diff --git a/composer.json b/composer.json index b1a9f6e8c..4df7e16ee 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,7 @@ "ext-openssl": "*", "ext-session": "*", "ezyang/htmlpurifier": "^4.17", + "garethp/php-ews": "dev-master", "henrique-borba/php-sieve-manager": "^1.0", "league/commonmark": "^2.4", "paragonie/random_compat": "^2.0.18", diff --git a/composer.lock b/composer.lock index 963936305..d9357206d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e11d399af68ed7416340c63ab7a213d6", + "content-hash": "6a75aa856405dde3a37887d21821e90a", "packages": [ { "name": "bacon/bacon-qr-code", @@ -121,23 +121,23 @@ }, { "name": "dasprid/enum", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", "shasum": "" }, "require": { "php": ">=7.1 <9.0" }, "require-dev": { - "phpunit/phpunit": "^7 | ^8 | ^9", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", "squizlabs/php_codesniffer": "*" }, "type": "library", @@ -165,22 +165,22 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" }, - "time": "2023-08-25T16:18:39+00:00" + "time": "2024-08-09T14:30:48+00:00" }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -240,26 +240,26 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "ezyang/htmlpurifier", - "version": "v4.17.0", + "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c" + "reference": "cb56001e54359df7ae76dc522d08845dc741621b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { "cerdic/css-tidy": "^1.7 || ^2.0", @@ -301,22 +301,335 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" + }, + "time": "2024-11-01T03:51:45+00:00" + }, + { + "name": "garethp/http-playback", + "version": "v2.0", + "source": { + "type": "git", + "url": "https://github.com/Garethp/HttpPlayback.git", + "reference": "50fd7b75c3d08ac0548df6d834a60ac65fc365df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Garethp/HttpPlayback/zipball/50fd7b75c3d08ac0548df6d834a60ac65fc365df", + "reference": "50fd7b75c3d08ac0548df6d834a60ac65fc365df", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~9.5", + "squizlabs/php_codesniffer": "~3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "garethp\\HttpPlayback\\": "src/", + "garethp\\HttpPlayback\\Test\\": "tests/src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "support": { + "issues": "https://github.com/Garethp/HttpPlayback/issues", + "source": "https://github.com/Garethp/HttpPlayback/tree/v2.0" + }, + "time": "2021-05-10T01:13:55+00:00" + }, + { + "name": "garethp/php-ews", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Garethp/php-ews.git", + "reference": "3d0d9e23e1e3f9077403266c256fb3c1219eef16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Garethp/php-ews/zipball/3d0d9e23e1e3f9077403266c256fb3c1219eef16", + "reference": "3d0d9e23e1e3f9077403266c256fb3c1219eef16", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-dom": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "ext-soap": "*", + "garethp/http-playback": "^2.0" + }, + "require-dev": { + "goetas/xsd-reader": "^2.0-dev", + "goetas/xsd2php": "^2.1", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^9.4 || ^8.5", + "squizlabs/php_codesniffer": "~3.6.0" + }, + "default-branch": true, + "type": "library", + "autoload": { + "files": [ + "src/Utilities/ensureIsArray.php", + "src/Utilities/ensureIsDateTime.php", + "src/Utilities/cloneValue.php", + "src/Utilities/getFolderIds.php" + ], + "psr-4": { + "garethp\\ews\\": "src/", + "garethp\\ews\\Test\\": "tests/src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "A PHP Library to interact with the Exchange SOAP service", + "keywords": [ + "Microsoft Exchange", + "NTLM", + "calendar", + "contacts", + "email", + "ews", + "exchange web services", + "php", + "push notification", + "soap" + ], + "support": { + "issues": "https://github.com/Garethp/php-ews/issues", + "source": "https://github.com/Garethp/php-ews/tree/v0.10.2" + }, + "time": "2024-12-14T14:36:26+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, - "time": "2023-11-17T15:01:25+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { @@ -331,8 +644,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -403,7 +716,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -419,7 +732,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "henrique-borba/php-sieve-manager", @@ -492,16 +805,16 @@ }, { "name": "league/commonmark", - "version": "2.4.2", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" + "reference": "d150f911e0079e90ae3c106734c93137c184f932" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d150f911e0079e90ae3c106734c93137c184f932", + "reference": "d150f911e0079e90ae3c106734c93137c184f932", "shasum": "" }, "require": { @@ -514,8 +827,8 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.3", - "commonmark/commonmark.js": "0.30.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", "erusev/parsedown": "^1.0", @@ -526,8 +839,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 || ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0" }, @@ -537,7 +851,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "2.7-dev" } }, "autoload": { @@ -594,7 +908,7 @@ "type": "tidelift" } ], - "time": "2024-02-02T11:59:32+00:00" + "time": "2024-12-07T15:34:16+00:00" }, { "name": "league/config", @@ -680,31 +994,31 @@ }, { "name": "nette/schema", - "version": "v1.2.5", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", - "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", "shasum": "" }, "require": { - "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": "7.1 - 8.3" + "nette/utils": "^4.0", + "php": "8.1 - 8.4" }, "require-dev": { - "nette/tester": "^2.3 || ^2.4", + "nette/tester": "^2.5.2", "phpstan/phpstan-nette": "^1.0", - "tracy/tracy": "^2.7" + "tracy/tracy": "^2.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -736,35 +1050,36 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.5" + "source": "https://github.com/nette/schema/tree/v1.3.2" }, - "time": "2023-10-05T20:37:59+00:00" + "time": "2024-10-06T23:10:23+00:00" }, { "name": "nette/utils", - "version": "v3.2.10", + "version": "v4.0.5", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "a4175c62652f2300c8017fb7e640f9ccb11648d2" + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/a4175c62652f2300c8017fb7e640f9ccb11648d2", - "reference": "a4175c62652f2300c8017fb7e640f9ccb11648d2", + "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", "shasum": "" }, "require": { - "php": ">=7.2 <8.4" + "php": "8.0 - 8.4" }, "conflict": { - "nette/di": "<3.0.6" + "nette/finder": "<3", + "nette/schema": "<1.2.2" }, "require-dev": { "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "~2.0", + "nette/tester": "^2.5", "phpstan/phpstan": "^1.0", - "tracy/tracy": "^2.3" + "tracy/tracy": "^2.9" }, "suggest": { "ext-gd": "to use Image", @@ -772,13 +1087,12 @@ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", "ext-json": "to use Nette\\Utils\\Json", "ext-mbstring": "to use Strings::lower() etc...", - "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -822,9 +1136,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v3.2.10" + "source": "https://github.com/nette/utils/tree/v4.0.5" }, - "time": "2023-07-30T15:38:18+00:00" + "time": "2024-08-07T15:39:19+00:00" }, { "name": "paragonie/random_compat", @@ -1036,22 +1350,74 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, { "name": "psr/http-factory", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", + "php": ">=7.1", "psr/http-message": "^1.0 || ^2.0" }, "type": "library", @@ -1075,7 +1441,7 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -1087,9 +1453,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2023-04-10T20:10:41+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", @@ -1190,25 +1556,25 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.3", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "80d075412b557d41002320b96a096ca65aa2c98d" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", - "reference": "80d075412b557d41002320b96a096ca65aa2c98d", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1237,7 +1603,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -1253,7 +1619,7 @@ "type": "tidelift" } ], - "time": "2023-01-24T14:02:46+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/dotenv", @@ -1328,20 +1694,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -1352,8 +1718,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1387,7 +1753,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -1403,24 +1769,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f" + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f", - "reference": "cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/48becf00c920479ca2e910c22a5a39e5d47ca956", + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-iconv": "*" @@ -1467,7 +1833,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.31.0" }, "funding": [ { @@ -1483,24 +1849,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1511,8 +1877,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1547,7 +1913,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1563,30 +1929,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1627,7 +1993,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -1643,20 +2009,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9" + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", "shasum": "" }, "require": { @@ -1699,7 +2065,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.8" + "source": "https://github.com/symfony/yaml/tree/v6.4.13" }, "funding": [ { @@ -1715,7 +2081,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "thomaspark/bootswatch", @@ -2115,16 +2481,16 @@ "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -2132,11 +2498,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -2162,7 +2529,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -2170,30 +2537,31 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { + "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -2225,9 +2593,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", @@ -2349,32 +2717,32 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.14", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -2386,7 +2754,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -2415,7 +2783,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -2423,7 +2791,7 @@ "type": "github" } ], - "time": "2024-03-12T15:33:41+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2670,16 +3038,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.20", + "version": "10.5.39", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3" + "reference": "4e89eff200b801db58f3d580ad7426431949eaa9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3", - "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4e89eff200b801db58f3d580ad7426431949eaa9", + "reference": "4e89eff200b801db58f3d580ad7426431949eaa9", "shasum": "" }, "require": { @@ -2689,26 +3057,26 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.3", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -2751,7 +3119,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.39" }, "funding": [ { @@ -2767,7 +3135,7 @@ "type": "tidelift" } ], - "time": "2024-04-24T06:32:35+00:00" + "time": "2024-12-11T10:51:07+00:00" }, { "name": "sebastian/cli-parser", @@ -2939,16 +3307,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { @@ -2959,7 +3327,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -3004,7 +3372,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { @@ -3012,7 +3380,7 @@ "type": "github" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { "name": "sebastian/complexity", @@ -3738,7 +4106,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "garethp/php-ews": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -3756,5 +4126,5 @@ "platform-overrides": { "php": "8.1" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/lib/servers.php b/lib/servers.php index 1bd5fbf15..9e37fdb96 100644 --- a/lib/servers.php +++ b/lib/servers.php @@ -179,8 +179,8 @@ trait Hm_Server_List { Hm_Repository::get as repo_get; } - public static function init($name, $user_config) { - self::initRepo($name, $user_config, self::$server_list); + public static function init($name, $user_config, $session) { + self::initRepo($name, $user_config, $session, self::$server_list); } /** diff --git a/modules/advanced_search/modules.php b/modules/advanced_search/modules.php index eaf230250..752ffc9ef 100644 --- a/modules/advanced_search/modules.php +++ b/modules/advanced_search/modules.php @@ -73,13 +73,12 @@ public function process() { $charset = $this->request->post['charset']; } - $cache = Hm_IMAP_List::get_cache($this->cache, $this->imap_id); - $imap = Hm_IMAP_List::connect($this->imap_id, $cache); - if (!imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($this->imap_id, $this->cache); + if (! $mailbox || ! $mailbox->authed()) { return; } if ($charset) { - $imap->search_charset = $charset; + $mailbox->set_search_charset($charset); } $params = array( array('SENTBEFORE', date('j-M-Y', strtotime($form['adv_end']))), @@ -95,59 +94,62 @@ public function process() { $searchInSpecialFolders = $this->request->post['all_special_folders'] ?? false; $includeSubfolders = $this->request->post['include_subfolders'] ?? false; if ($searchInAllFolders) { - $msg_list = $this->all_folders_search($imap, $flags, $params, $limit); + $msg_list = $this->all_folders_search($mailbox, $flags, $params, $limit); } elseif ($searchInSpecialFolders) { - $msg_list = $this->special_folders_search($imap, $flags, $params, $limit); + $msg_list = $this->special_folders_search($mailbox, $flags, $params, $limit); } else if ($includeSubfolders) { - $msg_list = $this->all_folders_search($imap, $flags, $params, $limit, $this->folder); - } else if (!$imap->select_mailbox($this->folder)) { + $msg_list = $this->all_folders_search($mailbox, $flags, $params, $limit, $this->folder); + } else if (! $mailbox->select_mailbox($this->folder)) { return; } else { - $msg_list = $this->imap_search($flags, $imap, $params, $limit); + $msg_list = $this->imap_search($flags, $mailbox, $params, $limit); } $this->out('imap_search_results', $msg_list); - $this->out('folder_status', $imap->folder_state); + $this->out('folder_status', $mailbox->get_folder_state()); $this->out('imap_server_ids', array($this->imap_id)); } - private function all_folders_search($imap, $flags, $params, $limit, $parent = '') { - $folders = $imap->get_mailbox_list(mailbox:$parent); + private function all_folders_search($mailbox, $flags, $params, $limit, $parent = '') { + if ($parent) { + $folders = $mailbox->get_subfolders($parent); + } else { + $folders = $mailbox->get_folders(); + } $msg_list = array(); foreach ($folders as $folder) { $this->folder = $folder['name']; - $imap->select_mailbox($this->folder); - $msgs = $this->imap_search($flags, $imap, $params, $limit); + $msgs = $this->imap_search($flags, $mailbox, $params, $limit); $msg_list = array_merge($msg_list, $msgs); } return $msg_list; } - private function special_folders_search($imap, $flags, $params, $limit) { + private function special_folders_search($mailbox, $flags, $params, $limit) { $specials = $this->user_config->get('special_imap_folders', array()); $folders = $specials[$this->imap_id] ?? []; $msg_list = array(); foreach ($folders as $folder) { $this->folder = $folder; - $imap->select_mailbox($this->folder); - $msgs = $this->imap_search($flags, $imap, $params, $limit); + $mailbox->select_mailbox($this->folder); + $msgs = $this->imap_search($flags, $mailbox, $params, $limit); $msg_list = array_merge($msg_list, $msgs); } return $msg_list; } - private function imap_search($flags, $imap, $params, $limit) { + private function imap_search($flags, $mailbox, $params, $limit) { $msg_list = array(); $exclude_deleted = true; if (in_array('deleted', $flags, true)) { $exclude_deleted = false; } - $msgs = $imap->search($flags, false, $params, array(), $exclude_deleted); + $msgs = $mailbox->search($this->folder, $flags, false, $params, array(), $exclude_deleted); if (!$msgs) { return $msg_list; } $server_details = Hm_IMAP_List::dump($this->imap_id); - foreach ($imap->get_message_list($msgs) as $msg) { + foreach ($mailbox->get_message_list($this->folder, $msgs) as $msg) { if (array_key_exists('content-type', $msg) && mb_stristr($msg['content-type'], 'multipart/mixed')) { $msg['flags'] .= ' \Attachment'; } diff --git a/modules/core/handler_modules.php b/modules/core/handler_modules.php index 17e837a03..a42115653 100644 --- a/modules/core/handler_modules.php +++ b/modules/core/handler_modules.php @@ -60,8 +60,8 @@ public function process() { $current['pass'] = $form['password']; unset($current['nopass']); Hm_IMAP_List::edit($server['id'], $current); - $imap = Hm_IMAP_List::connect($server['id'], false); - if ($imap->get_state() == 'authenticated') { + $mailbox = Hm_IMAP_List::connect($server['id'], false); + if ($mailbox && $mailbox->authed()) { Hm_Msgs::add('Password Updated'); $this->out('connect_status', true); } @@ -1086,7 +1086,7 @@ public function process() { Hm_Msgs::add("ERRSMTP module is not enabled"); return; } - $this->smtp_server_id = connect_to_smtp_server($smtpAddress, $profileName, $smtpPort, $email, $password, $smtpTls, $smtpServerId); + $this->smtp_server_id = connect_to_smtp_server($smtpAddress, $profileName, $smtpPort, $email, $password, $smtpTls, 'smtp', $smtpServerId); if(!isset($this->smtp_server_id)){ Hm_Msgs::add("ERRCould not save server"); return; @@ -1133,7 +1133,7 @@ public function process() { return; } - add_profile($profileName, $profileSignature, $profileReplyTo, $profileIsDefault, $email, $imapAddress, $this->smtp_server_id, $this->imap_server_id, $this); + add_profile($profileName, $profileSignature, $profileReplyTo, $profileIsDefault, $email, $imapAddress, $email, $this->smtp_server_id, $this->imap_server_id, $this); } if ($this->module_is_supported('imap_folders')) { diff --git a/modules/core/hm-mailbox.php b/modules/core/hm-mailbox.php new file mode 100644 index 000000000..1a5a9ea63 --- /dev/null +++ b/modules/core/hm-mailbox.php @@ -0,0 +1,589 @@ +server_id = $server_id; + $this->user_config = $user_config; + $this->session = $session; + } + + public function connect(array $config) { + if (array_key_exists('type', $config) && $config['type'] == 'smtp') { + $this->type = self::TYPE_SMTP; + $this->connection = new Hm_SMTP($config); + return $this->connection->connect(); + } elseif (array_key_exists('type', $config) && $config['type'] == 'jmap') { + $this->type = self::TYPE_JMAP; + $this->connection = new Hm_JMAP(); + return $this->connection->connect($config); + } elseif (array_key_exists('type', $config) && $config['type'] == 'ews') { + $this->type = self::TYPE_EWS; + $this->connection = new Hm_EWS(); + return $this->connection->connect($config); + } else { + $this->type = self::TYPE_IMAP; + $this->connection = new Hm_IMAP(); + return $this->connection->connect($config); + } + } + + public function get_connection() { + return $this->connection; + } + + public function is_imap() { + return $this->type === self::TYPE_IMAP || $this->type === self::TYPE_JMAP; + } + + public function is_smtp() { + return $this->type === self::TYPE_SMTP; + } + + public function server_type() { + switch ($this->type) { + case self::TYPE_IMAP: + return 'IMAP'; + case self::TYPE_JMAP: + return 'JMAP'; + case self::TYPE_EWS: + return 'EWS'; + } + } + + public function authed() { + if ($this->is_imap()) { + return $this->connection->get_state() == 'authenticated' || $this->connection->get_state() == 'selected'; + } elseif ($this->is_smtp()) { + return $this->connection->state == 'authed'; + } else { + return $this->connection->authed(); + } + } + + public function state() { + if ($this->is_imap()) { + return $this->connection->get_state(); + } elseif ($this->is_smtp()) { + return $this->connection->state; + } else { + return null; + } + } + + public function get_folder_status($folder) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + return $this->connection->get_mailbox_status($folder); + } else { + return $this->connection->get_folder_status($folder); + } + } + + public function get_folder_name($folder) { + if ($this->is_imap()) { + return $folder; + } else { + $result = $this->connection->get_folder_status($folder); + return $result['name']; + } + } + + public function create_folder($folder, $parent = null) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + $new_folder = prep_folder_name($this->connection, $folder, false, $parent); + if ($this->connection->create_mailbox($new_folder)) { + return $new_folder; + } else { + return false; + } + } else { + return $this->connection->create_folder($folder, $parent); + } + } + + public function rename_folder($folder, $new_name, $parent = null) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + $old_folder = prep_folder_name($this->connection, $folder, true); + $new_folder = prep_folder_name($this->connection, $new_name, false, $parent); + return $this->connection->rename_mailbox($old_folder, $new_folder); + } else { + $folder = decode_folder_str($folder); + if ($parent) { + $parent = decode_folder_str($parent); + } + return $this->connection->rename_folder($folder, $new_name, $parent); + } + } + + public function delete_folder($folder) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + $del_folder = prep_folder_name($this->connection, $folder, true); + return $this->connection->delete_mailbox($del_folder); + } else { + $del_folder = decode_folder_str($folder); + return $this->connection->delete_folder($del_folder); + } + } + + public function prep_folder_name($folder) { + if ($this->is_imap()) { + return prep_folder_name($this->connection, $folder, true); + } else { + if (substr_count($folder, '_') >= 2) { + return decode_folder_str($folder); + } else { + return $folder; + } + } + } + + public function folder_subscription($folder, $action) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + return $this->connection->mailbox_subscription($folder, $action); + } else { + // emulate folder subscription via settings + $config = $this->user_config->get('unsubscribed_folders'); + if (! isset($config[$this->server_id])) { + $config[$this->server_id] = []; + } + if ($action) { + $index = array_search($folder, $config[$this->server_id]); + if ($index !== false) { + unset($config[$this->server_id][$index]); + } + } else { + if (! in_array($folder, $config[$this->server_id])) { + $config[$this->server_id][] = $folder; + } + } + $this->user_config->set('unsubscribed_folders', $config); + $this->session->set('user_data', $this->user_config->dump()); + $this->session->record_unsaved('Folder subscription updated'); + return true; + } + } + + public function get_folders($only_subscribed = false) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + return $this->connection->get_mailbox_list($only_subscribed); + } else { + return $this->connection->get_folders(null, $only_subscribed, $this->user_config->get('unsubscribed_folders')[$this->server_id] ?? []); + } + } + + public function get_subfolders($folder, $only_subscribed = false, $with_input = false) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + return $this->connection->get_folder_list_by_level($folder, $only_subscribed, $with_input); + } else { + return $this->connection->get_folders($folder, $only_subscribed, $this->user_config->get('unsubscribed_folders')[$this->server_id] ?? [], $with_input); + } + } + + public function get_folder_state() { + if ($this->is_imap()) { + return $this->connection->folder_state; + } else { + return $this->folder_state; + } + } + + public function get_selected_folder() { + if ($this->is_imap()) { + return $this->connection->selected_mailbox; + } else { + return $this->selected_folder; + } + } + + public function get_special_use_mailboxes($folder = false) { + if (! $this->authed()) { + return; + } + if ($this->is_imap()) { + return $this->connection->get_special_use_mailboxes($folder); + } else { + return $this->connection->get_special_use_folders($folder); + } + } + + /** + * Get messages in a folder applying filters, sorting and pagination + * @return array - [total results found, results for a single page] + */ + public function get_messages($folder, $sort, $reverse, $flag_filter, $offset=0, $limit=50, $keyword=false, $trusted_senders=[], $include_preview = false) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + $messages = $this->connection->get_mailbox_page($folder, $sort, $reverse, $flag_filter, $offset, $limit, $keyword, $trusted_senders, $include_preview); + } else { + $messages = $this->connection->get_messages($folder, $sort, $reverse, $flag_filter, $offset, $limit, $keyword, $trusted_senders, $include_preview); + $folder = $this->selected_folder['id']; + } + foreach ($messages[1] as &$msg) { + $msg['folder'] = bin2hex($folder); + } + return $messages; + } + + public function get_message_headers($folder, $msg_id) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + return $this->connection->get_message_headers($msg_id); + } else { + return $this->connection->get_message_headers($msg_id); + } + + } + + public function get_message_content($folder, $msg_id, $part = 0) { + if (! $this->authed()) { + return; + } + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + return $this->connection->get_message_content($msg_id, $part); + } else { + return $this->connection->get_message_content($msg_id, $part); + } + } + + public function get_structured_message($folder, $msg_id, $part, $text_only) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + $msg_struct = $this->connection->get_message_structure($msg_id); + if ($part !== false) { + if ($part == 0) { + $max = 500000; + } + else { + $max = false; + } + $struct = $this->connection->search_bodystructure($msg_struct, array('imap_part_number' => $part)); + $msg_struct_current = array_shift($struct); + $msg_text = $this->connection->get_message_content($msg_id, $part, $max, $msg_struct_current); + } + else { + if (! $text_only) { + list($part, $msg_text) = $this->connection->get_first_message_part($msg_id, 'text', 'html', $msg_struct); + if (!$part) { + list($part, $msg_text) = $this->connection->get_first_message_part($msg_id, 'text', false, $msg_struct); + } + } + else { + list($part, $msg_text) = $this->connection->get_first_message_part($msg_id, 'text', false, $msg_struct); + } + $struct = $this->connection->search_bodystructure($msg_struct, array('imap_part_number' => $part)); + $msg_struct_current = array_shift($struct); + if (! trim($msg_text)) { + if (is_array($msg_struct_current) && array_key_exists('subtype', $msg_struct_current)) { + if ($msg_struct_current['subtype'] == 'plain') { + $subtype = 'html'; + } + else { + $subtype = 'plain'; + } + list($part, $msg_text) = $this->connection->get_first_message_part($msg_id, 'text', $subtype, $msg_struct); + $struct = $this->connection->search_bodystructure($msg_struct, array('imap_part_number' => $part)); + $msg_struct_current = array_shift($struct); + } + } + } + if (isset($msg_struct_current['subtype']) && mb_strtolower($msg_struct_current['subtype'] == 'html')) { + $msg_text = add_attached_images($msg_text, $msg_id, $msg_struct, $this->connection); + } + return [$msg_struct, $msg_struct_current, $msg_text, $part]; + } else { + return $this->connection->get_structured_message($msg_id, $part, $text_only); + } + } + + public function store_message($folder, $msg, $seen = true, $draft = false) { + if (! $this->authed()) { + return false; + } + if ($this->is_imap()) { + if ($this->connection->append_start($folder, mb_strlen($msg), $seen, $draft)) { + $this->connection->append_feed($msg."\r\n"); + return $this->connection->append_end(); + } + } else { + return $this->connection->store_message($folder, $msg, $seen, $draft); + } + return false; + } + + public function delete_message($folder, $msg_id, $trash_folder) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap() && $trash_folder && $trash_folder != $folder) { + if ($this->connection->message_action('MOVE', [$msg_id], $trash_folder)['status']) { + return true; + } + } + else { + if ($this->connection->message_action('DELETE', array($msg_id))['status']) { + $this->connection->message_action('EXPUNGE', array($msg_id)); + return true; + } + } + return false; + } + + public function message_action($folder, $action, $uids, $mailbox=false, $keyword=false) { + if (! $this->select_folder($folder)) { + return ['status' => false, 'responses' => []]; + } + return $this->connection->message_action($action, $uids, $mailbox, $keyword); + } + + public function stream_message_part($folder, $msg_id, $part_id, $start_cb) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + $msg_struct = $this->connection->get_message_structure($msg_id); + $struct = $this->connection->search_bodystructure($msg_struct, array('imap_part_number' => $part_id)); + if (! empty($struct)) { + $part_struct = array_shift($struct); + $encoding = false; + if (array_key_exists('encoding', $part_struct)) { + $encoding = trim(mb_strtolower($part_struct['encoding'])); + } + $stream_size = $this->connection->start_message_stream($msg_id, $part_id); + if ($stream_size > 0) { + $part_name = get_imap_part_name($part_struct, $msg_id, $part_id); + $charset = ''; + if (array_key_exists('attributes', $part_struct)) { + if (is_array($part_struct['attributes']) && array_key_exists('charset', $part_struct['attributes'])) { + $charset = '; charset='.$part_struct['attributes']['charset']; + } + } + $start_cb($part_struct['type'] . '/' . $part_struct['subtype'] . $charset, $part_name); + $output_line = ''; + while($line = $this->connection->read_stream_line()) { + if ($encoding == 'quoted-printable') { + $line = quoted_printable_decode($line); + } + elseif ($encoding == 'base64') { + $line = base64_decode($line); + } + echo $output_line; + $output_line = $line; + } + if ($part_struct['type'] == 'text') { + $output_line = preg_replace("/\)(\r\n)$/m", '$1', $output_line); + } + echo $output_line; + } + } + } else { + return $this->connection->stream_message_part($msg_id, $part_id, $start_cb); + } + } + + public function remove_attachment($folder, $msg_id, $part_id) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + $msg = $this->connection->get_message_content($msg_id, 0, false, false); + if ($msg) { + $struct = $this->connection->get_message_structure($msg_id); + $attachment_id = get_attachment_id_for_mail_parser($struct, $part_id); + if ($attachment_id !== false) { + $msg = remove_attachment($attachment_id, $msg); + if ($this->connection->append_start($folder, mb_strlen($msg))) { + $this->connection->append_feed($msg."\r\n"); + if ($this->connection->append_end()) { + if ($this->connection->message_action('DELETE', array($uid))['status']) { + $this->connection->message_action('EXPUNGE', array($uid)); + return true; + } + } + } + } + } + } else { + $message = $this->connection->get_mime_message_by_id($msg_id); + $result = $this->connection->get_structured_message($msg_id, false, false); + $struct = $result[0]; + $attachment_id = get_attachment_id_for_mail_parser($struct, $part_id); + if ($attachment_id !== false) { + $message->removeAttachmentPart($attachment_id); + if ($this->connection->store_message($folder, (string) $message)) { + $this->connection->message_action('HARDDELETE', [$msg_id]); + return true; + } + } + } + } + + public function get_quota($folder, $root = false) { + if ($this->is_imap()) { + if ($root) { + return $this->connection->get_quota_root($folder); + } else { + return $this->connection->get_quota($folder); + } + } else { + // not supported by EWS + return []; + } + } + + public function get_debug() { + if ($this->is_imap()) { + return $this->connection->show_debug(true, true, true); + } else { + return []; + } + } + + public function use_cache() { + if ($this->is_imap()) { + return $this->connection->use_cache; + } else { + return false; + } + } + + public function dump_cache($type = 'string') { + if ($this->is_imap()) { + return $this->connection->dump_cache($type); + } else { + return null; + } + } + + public function get_state() { + if ($this->is_imap()) { + return $this->connection->get_state(); + } else { + return $this->authed() ? 'authenticated' : 'disconnected'; + } + } + + public function get_capability() { + return $this->connection->get_capability(); + } + + public function set_read_only($read_only) { + if ($this->is_imap()) { + $this->connection->read_only = $read_only; + } + } + + public function set_search_charset($charset) { + if ($this->is_imap()) { + $this->connection->search_charset = $charset; + } + } + + public function search($folder, $target='ALL', $uids=false, $terms=array(), $esearch=array(), $exclude_deleted=true, $exclude_auto_bcc=true, $only_auto_bcc=false) { + if (! $this->select_folder($folder)) { + return; + } + if ($this->is_imap()) { + return $this->connection->search($target, $uids, $terms, $esearch, $exclude_deleted, $exclude_auto_bcc, $only_auto_bcc); + } else { + // deleted flag, auto-bcc feature - not supported by EWS + list($total, $itemIds) = $this->connection->search($folder, false, false, $target, 0, 9999, $terms, []); + return $itemIds; + } + } + + public function get_message_list($folder, $msg_ids) { + if (! $this->select_folder($folder)) { + return []; + } + if ($this->is_imap()) { + return $this->connection->get_message_list($msg_ids); + } else { + return $this->connection->get_message_list($msg_ids); + } + } + + public function send_message($from, $recipients, $message, $delivery_receipt = false) { + if ($this->is_smtp()) { + if ($delivery_receipt) { + $from_params = 'RET=HDRS'; + $recipients_params = 'NOTIFY=SUCCESS,FAILURE'; + } else { + $from_params = ''; + $recipients_params = ''; + } + return $this->connection->send_message($from, $recipients, $message, $from_params, $recipients_params); + } else { + return $this->connection->send_message($from, $recipients, $message, $delivery_receipt); + } + } + + protected function select_folder($folder) { + if ($this->is_imap()) { + if (isset($this->connection->selected_mailbox['name']) && $this->connection->selected_mailbox['name'] == $folder) { + return true; + } + if (! $this->connection->select_mailbox($folder)) { + return false; + } + } else { + $this->folder_state = $this->get_folder_status($folder); + if (! $this->folder_state) { + return false; + } + $this->selected_folder = ['id' => $folder, 'name' => $this->folder_state['name'], 'detail' => []]; + } + return true; + } +} diff --git a/modules/core/message_list_functions.php b/modules/core/message_list_functions.php index 793bc398d..02c2d61de 100644 --- a/modules/core/message_list_functions.php +++ b/modules/core/message_list_functions.php @@ -167,10 +167,10 @@ function human_readable_interval($date_str) { $t['month'] = $t['day']*30; $t['year'] = $t['week']*52; - if ($interval < 0) { + if ($interval < -300) { return 'From the future!'; } - elseif ($interval == 0) { + elseif ($interval <= 0) { return 'Just now'; } foreach (array_reverse($t) as $name => $val) { @@ -458,17 +458,17 @@ function list_sources($sources, $output_mod) { if (array_key_exists('nodisplay', $src) && $src['nodisplay']) { continue; } - if ($src['type'] == 'imap' && !array_key_exists('folder', $src)) { - $folder = '_INBOX'; + if ($src['type'] == 'imap' && !array_key_exists('folder_name', $src)) { + $folder = 'INBOX'; } - elseif (!array_key_exists('folder', $src)) { + elseif (!array_key_exists('folder_name', $src)) { $folder = ''; } else { - $folder = '_'.hex2bin($src['folder']); + $folder = $src['folder_name']; } $res .= '
'.$output_mod->html_safe($src['type']).' '.$output_mod->html_safe($src['name']); - $res .= ' '.$output_mod->html_safe(str_replace('_', '', $folder)); + $res .= ' '.$output_mod->html_safe($folder); $res .= '
'; } $res .= ''; diff --git a/modules/core/modules.php b/modules/core/modules.php index dd980474e..8f520565d 100644 --- a/modules/core/modules.php +++ b/modules/core/modules.php @@ -17,3 +17,4 @@ require APP_PATH.'modules/core/message_list_functions.php'; require APP_PATH.'modules/core/handler_modules.php'; require APP_PATH.'modules/core/output_modules.php'; +require_once APP_PATH.'modules/core/hm-mailbox.php'; diff --git a/modules/core/output_modules.php b/modules/core/output_modules.php index 4225ea1c2..a22d43c8b 100644 --- a/modules/core/output_modules.php +++ b/modules/core/output_modules.php @@ -2226,7 +2226,7 @@ protected function output() { $hasJmapActivated = in_array('jmap', $this->get('router_module_list'), true); if($hasImapActivated){ - $imap_servers_count = count(array_filter($this->get('imap_servers', array()), function($v) { return !array_key_exists('type', $v) || $v['type'] != 'jmap'; })); + $imap_servers_count = count(array_filter($this->get('imap_servers', array()), function($v) { return !array_key_exists('type', $v) || $v['type'] == 'imap'; })); $accordionTitle .= 'IMAP'; $configuredText .= ' ' . $imap_servers_count .' IMAP'; $hasEssentialModuleActivated = true; diff --git a/modules/core/site.js b/modules/core/site.js index cb42a5cac..58cd6e638 100644 --- a/modules/core/site.js +++ b/modules/core/site.js @@ -2001,9 +2001,9 @@ function resetQuickSetupForm() { function handleCreateProfileCheckboxChange(checkbox) { if(checkbox.checked) { - $('#srv_setup_stepper_profile_bloc').show(); + $(checkbox).closest('.form-check').next().show(); }else{ - $('#srv_setup_stepper_profile_bloc').hide(); + $(checkbox).closest('.form-check').next().hide(); } } diff --git a/modules/imap/functions.php b/modules/imap/functions.php index 03349bf3a..524a58501 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -29,13 +29,13 @@ function imap_sources($mod, $folder = 'sent') { } $folders = get_special_folders($mod, $index); if (array_key_exists($folder, $folders) && $folders[$folder]) { - $sources[] = array('folder' => bin2hex($folders[$folder]), 'type' => 'imap', 'name' => $vals['name'], 'id' => $index); + $sources[] = array('folder' => bin2hex($folders[$folder]), 'folder_name' => $folders[$folder], 'type' => $vals['type'] ?? 'imap', 'name' => $vals['name'], 'id' => $index); } elseif ($inbox) { - $sources[] = array('folder' => bin2hex('INBOX'), 'type' => 'imap', 'name' => $vals['name'], 'id' => $index); + $sources[] = array('folder' => bin2hex('INBOX'), 'folder_name' => 'INBOX', 'type' => $vals['type'] ?? 'imap', 'name' => $vals['name'], 'id' => $index); } else { - $sources[] = array('folder' => bin2hex('SPECIAL_USE_CHECK'), 'nodisplay' => true, 'type' => 'imap', 'name' => $vals['name'], 'id' => $index); + $sources[] = array('folder' => bin2hex('SPECIAL_USE_CHECK'), 'folder_name' => 'SPECIAL_USE_CHECK', 'nodisplay' => true, 'type' => $vals['type'] ?? 'imap', 'name' => $vals['name'], 'id' => $index); } } return $sources; @@ -57,7 +57,7 @@ function imap_data_sources($custom=array()) { if (!array_key_exists('user', $vals)) { continue; } - $sources[] = array('folder' => bin2hex('INBOX'), 'type' => 'imap', 'name' => $vals['name'], 'id' => $index); + $sources[] = array('folder' => bin2hex('INBOX'), 'folder_name' => 'INBOX', 'type' => $vals['type'] ?? 'imap', 'name' => $vals['name'], 'id' => $index); } foreach ($custom as $path => $type) { $parts = explode('_', $path, 3); @@ -66,7 +66,14 @@ function imap_data_sources($custom=array()) { if ($type == 'add') { $details = Hm_IMAP_List::dump($parts[1]); if ($details) { - $sources[] = array('folder' => $parts[2], 'type' => 'imap', 'name' => $details['name'], 'id' => $parts[1]); + $folder_name = $parts[2]; + if (! empty($details['type']) && $details['type'] == 'ews') { + $mailbox = Hm_IMAP_List::get_connected_mailbox($details['id']); + if ($mailbox && $mailbox->authed()) { + $folder_name = $mailbox->get_folder_name(hex2bin($folder_name)); + } + } + $sources[] = array('folder' => $parts[2], 'folder_name' => $folder_name, 'type' => $details['type'] ?? 'imap', 'name' => $details['name'], 'id' => $parts[1]); } } elseif ($type == 'remove') { @@ -134,10 +141,10 @@ function format_imap_folder_section($folders, $id, $output_mod, $with_input = fa '" href="?page=message_list&list_path='. urlencode('imap_'.$id.'_'.$output_mod->html_safe($folder_name)).'"'; } - if (mb_strlen($output_mod->html_safe($folder['basename']))>15) { + if (mb_strlen($folder['basename'])>15) { $results .= ''.mb_substr($output_mod->html_safe($folder['basename']),0,15).'...'; + '">'.$output_mod->html_safe(mb_substr($folder['basename'],0,15)).'...'; } else { $results .= ''.$output_mod->html_safe($folder['basename']).''; @@ -508,20 +515,18 @@ function remove_attachment($att_id, $msg) { /* * ZBateson\MailMimeParser uses 0-based index for attachments * Which mixes embedded images and attachments - * @param Hm_IMAP $imap Imap object - * @param int $msg message id - * @param string $msg_id message part + * @param array $struct Imap structure + * @param string $part_id message part * @return int */ if (!hm_exists('get_attachment_id_for_mail_parser')) { - function get_attachment_id_for_mail_parser($imap, $uid, $msg_id) { + function get_attachment_id_for_mail_parser($struct, $part_id) { $count = -1; $id = false; - $struct = $imap->get_message_structure($uid); foreach ($struct[0]['subs'] as $key => $sub) { if (! empty($sub['file_attributes'])) { $count++; - if ($key == $msg_id && isset($sub['file_attributes']['attachment'])) { + if ($key == $part_id && isset($sub['file_attributes']['attachment'])) { $id = $count; break; } @@ -738,13 +743,12 @@ function merge_imap_search_results($ids, $search_type, $session, $hm_cache, $fol $sent_results = array(); $status = array(); foreach($ids as $index => $id) { - $cache = Hm_IMAP_List::get_cache($hm_cache, $id); - $imap = Hm_IMAP_List::connect($id, $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($id, $hm_cache); + if ($mailbox && $mailbox->authed()) { $server_details = Hm_IMAP_List::dump($id); $folder = $folders[$index]; if ($sent) { - $sent_folder = $imap->get_special_use_mailboxes('sent'); + $sent_folder = $mailbox->get_special_use_mailboxes('sent'); if (array_key_exists('sent', $sent_folder)) { list($sent_status, $sent_results) = merge_imap_search_results($ids, $search_type, $session, $hm_cache, array($sent_folder['sent']), $limit, $terms, false); $status = array_merge($status, $sent_status); @@ -753,44 +757,42 @@ function merge_imap_search_results($ids, $search_type, $session, $hm_cache, $fol continue; } } - if ($imap->select_mailbox($folder)) { - $status['imap_'.$id.'_'.bin2hex($folder)] = $imap->folder_state; - if (!empty($terms)) { - foreach ($terms as $term) { - if (preg_match('/(?:[^\x00-\x7F])/', $term[1]) === 1) { - $imap->search_charset = 'UTF-8'; - break; - } - } - if ($sent) { - $msgs = $imap->search($search_type, false, $terms, array(), true, false, true); - } - else { - $msgs = $imap->search($search_type, false, $terms); + if (!empty($terms)) { + foreach ($terms as $term) { + if (preg_match('/(?:[^\x00-\x7F])/', $term[1]) === 1) { + $mailbox->set_search_charset('UTF-8'); + break; } } + if ($sent) { + $msgs = $mailbox->search($folder, $search_type, false, $terms, array(), true, false, true); + } else { - $msgs = $imap->search($search_type); + $msgs = $mailbox->search($folder, $search_type, false, $terms); + } + } + else { + $msgs = $mailbox->search($folder, $search_type); + } + if ($msgs) { + if ($limit) { + rsort($msgs); + $msgs = array_slice($msgs, 0, $limit); } - if ($msgs) { - if ($limit) { - rsort($msgs); - $msgs = array_slice($msgs, 0, $limit); + foreach ($mailbox->get_message_list($folder, $msgs) as $msg) { + if (array_key_exists('content-type', $msg) && mb_stristr($msg['content-type'], 'multipart/mixed')) { + $msg['flags'] .= ' \Attachment'; } - foreach ($imap->get_message_list($msgs) as $msg) { - if (array_key_exists('content-type', $msg) && mb_stristr($msg['content-type'], 'multipart/mixed')) { - $msg['flags'] .= ' \Attachment'; - } - if (mb_stristr($msg['flags'], 'deleted')) { - continue; - } - $msg['server_id'] = $id; - $msg['folder'] = bin2hex($folder); - $msg['server_name'] = $server_details['name']; - $msg_list[] = $msg; + if (mb_stristr($msg['flags'], 'deleted')) { + continue; } + $msg['server_id'] = $id; + $msg['folder'] = bin2hex($folder); + $msg['server_name'] = $server_details['name']; + $msg_list[] = $msg; } } + $status['imap_'.$id.'_'.bin2hex($folder)] = $mailbox->get_folder_state(); } else { $connection_failed = true; @@ -881,17 +883,16 @@ function imap_move_same_server($ids, $action, $hm_cache, $dest_path, $screen_ema $responses = []; $keys = array_keys($ids); $server_id = array_pop($keys); - $cache = Hm_IMAP_List::get_cache($hm_cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - foreach ($ids[$server_id] as $folder => $msgs) { - if (imap_authed($imap) && $imap->select_mailbox(hex2bin($folder))) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $hm_cache); + if ($mailbox && $mailbox->authed()) { + foreach ($ids[$server_id] as $folder => $msgs) { if ($screen_emails) { foreach ($msgs as $msg) { $moved[] = sprintf('imap_%s_%s_%s', $server_id, $msg, $folder); - $email = current(array_column(process_address_fld($imap->get_message_headers($msg)['From']), "email")); - $uids = $imap->search('ALL', false, array(array('FROM', $email))); + $email = current(array_column(process_address_fld($mailbox->get_message_headers(hex2bin($folder), $msg)['From']), "email")); + $uids = $mailbox->search(hex2bin($folder), 'ALL', false, array(array('FROM', $email))); foreach ($uids as $uid) { - $result = $imap->message_action(mb_strtoupper($action), $uid, hex2bin($dest_path[2])); + $result = $mailbox->message_action(hex2bin($folder), mb_strtoupper($action), $uid, hex2bin($dest_path[2])); if ($result['status']) { $response = $result['responses'][0]; $responses[] = [ @@ -906,7 +907,7 @@ function imap_move_same_server($ids, $action, $hm_cache, $dest_path, $screen_ema } } } else { - $result = $imap->message_action(mb_strtoupper($action), $msgs, hex2bin($dest_path[2])); + $result = $mailbox->message_action(hex2bin($folder), mb_strtoupper($action), $msgs, hex2bin($dest_path[2])); if ($result['status']) { foreach ($msgs as $index => $msg) { $response = $result['responses'][$index]; @@ -940,16 +941,14 @@ function imap_move_same_server($ids, $action, $hm_cache, $dest_path, $screen_ema function imap_move_different_server($ids, $action, $dest_path, $hm_cache) { $moved = []; $responses = []; - $cache = Hm_IMAP_List::get_cache($hm_cache, $dest_path[1]); - $dest_imap = Hm_IMAP_List::connect($dest_path[1], $cache); - if ($dest_imap) { + $dest_mailbox = Hm_IMAP_List::get_connected_mailbox($dest_path[1], $hm_cache); + if ($dest_mailbox && $dest_mailbox->authed()) { foreach ($ids as $server_id => $folders) { - $cache = Hm_IMAP_List::get_cache($hm_cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - foreach ($folders as $folder => $msg_ids) { - if (imap_authed($imap) && $imap->select_mailbox(hex2bin($folder))) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $hm_cache); + if ($mailbox && $mailbox->authed()) { + foreach ($folders as $folder => $msg_ids) { foreach ($msg_ids as $msg_id) { - $detail = $imap->get_message_list(array($msg_id)); + $detail = $mailbox->get_message_list(hex2bin($folder), array($msg_id)); if (array_key_exists($msg_id, $detail)) { if (mb_stristr($detail[$msg_id]['flags'], 'seen')) { $seen = true; @@ -958,33 +957,29 @@ function imap_move_different_server($ids, $action, $dest_path, $hm_cache) { $seen = false; } } - $msg = $imap->get_message_content($msg_id, 0); + $msg = $mailbox->get_message_content(hex2bin($folder), $msg_id); $msg = str_replace("\r\n", "\n", $msg); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; if (!$seen) { - $imap->message_action('UNREAD', array($msg_id)); + $mailbox->message_action(hex2bin($folder), 'UNREAD', array($msg_id)); } - if ($dest_imap->append_start(hex2bin($dest_path[2]), mb_strlen($msg), $seen)) { - $dest_imap->append_feed($msg."\r\n"); - $uid = $dest_imap->append_end(); - if ($uid) { - if ($action == 'move') { - $deleteResult = $imap->message_action('DELETE', array($msg_id)); - if ($deleteResult['status']) { - $imap->message_action('EXPUNGE', array($msg_id)); - } + if ($uid = $dest_mailbox->store_message(hex2bin($dest_path[2]), $msg, $seen)) { + if ($action == 'move') { + $deleteResult = $mailbox->message_action(hex2bin($folder), 'DELETE', array($msg_id)); + if ($deleteResult['status']) { + $mailbox->message_action(hex2bin($folder), 'EXPUNGE', array($msg_id)); } - $moved[] = sprintf('imap_%s_%s_%s', $server_id, $msg_id, $folder); - $responses[] = [ - 'oldUid' => $msg_id, - 'newUid' => $uid, - 'oldFolder' => hex2bin($folder), - 'newFolder' => hex2bin($dest_path[2]), - 'oldServer' => $server_id, - 'newServer' => $dest_path[1], - ]; } + $moved[] = sprintf('imap_%s_%s_%s', $server_id, $msg_id, $folder); + $responses[] = [ + 'oldUid' => $msg_id, + 'newUid' => $uid, + 'oldFolder' => hex2bin($folder), + 'newFolder' => hex2bin($dest_path[2]), + 'oldServer' => $server_id, + 'newServer' => $dest_path[1], + ]; } } } @@ -1118,16 +1113,6 @@ function clear_existing_reply_details($session) { } }} -/** - * @subpackage imap/functions - * @param object $imap imap library object - * @return bool - */ -if (!hm_exists('imap_authed')) { -function imap_authed($imap) { - return is_object($imap) && ($imap->get_state() == 'authenticated' || $imap->get_state() == 'selected'); -}} - /** * @subpackage imap/functions */ @@ -1331,14 +1316,11 @@ function get_request_params($request) { }} if (!hm_exists('snooze_message')) { -function snooze_message($imap, $msg_id, $folder, $snooze_tag) { - if (!$imap->select_mailbox($folder)) { - return false; - } +function snooze_message($mailbox, $msg_id, $folder, $snooze_tag) { if (!$snooze_tag) { - $imap->message_action('UNREAD', array($msg_id)); + $mailbox->message_action($folder, 'UNREAD', array($msg_id)); } - $msg = $imap->get_message_content($msg_id, 0); + $msg = $mailbox->get_message_content($folder, $msg_id); preg_match("/^X-Snoozed:.*(\r?\n[ \t]+.*)*\r?\n?/im", $msg, $matches); if (count($matches)) { $msg = str_replace($matches[0], '', $msg); @@ -1355,34 +1337,27 @@ function snooze_message($imap, $msg_id, $folder, $snooze_tag) { $res = false; $snooze_folder = 'Snoozed'; if ($snooze_tag) { - if (!count($imap->get_mailbox_status($snooze_folder))) { - $imap->create_mailbox($snooze_folder); - } - if ($imap->select_mailbox($snooze_folder) && $imap->append_start($snooze_folder, mb_strlen($msg))) { - $imap->append_feed($msg."\r\n"); - if ($imap->append_end()) { - if ($imap->select_mailbox($folder)) { - $deleteResult = $imap->message_action('DELETE', array($msg_id)); - if ($deleteResult['status']) { - $imap->message_action('EXPUNGE', array($msg_id)); - $res = true; - } - } + $status = $mailbox->get_folder_status($snooze_folder); + if (! count($status)) { + $snooze_folder = $mailbox->create_folder($snooze_folder); + } else { + $snooze_folder = $status['id']; + } + if ($mailbox->store_message($snooze_folder, $msg)) { + $deleteResult = $mailbox->message_action($folder, 'DELETE', array($msg_id)); + if ($deleteResult['status']) { + $mailbox->message_action($folder, 'EXPUNGE', array($msg_id)); + $res = true; } } } else { $snooze_headers = parse_snooze_header($matches[0]); $original_folder = $snooze_headers['from']; - if ($imap->select_mailbox($original_folder) && $imap->append_start($original_folder, mb_strlen($msg))) { - $imap->append_feed($msg."\r\n"); - if ($imap->append_end()) { - if ($imap->select_mailbox($snooze_folder)) { - $deleteResult = $imap->message_action('DELETE', array($msg_id)); - if ($deleteResult['status']) { - $imap->message_action('EXPUNGE', array($msg_id)); - $res = true; - } - } + if ($mailbox->store_message($original_folder, $msg)) { + $deleteResult = $mailbox->message_action($snooze_folder, 'DELETE', array($msg_id)); + if ($deleteResult['status']) { + $mailbox->message_action($snooze_folder, 'EXPUNGE', array($msg_id)); + $res = true; } } } @@ -1539,6 +1514,7 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima $imap_list = array( 'name' => $name, 'server' => $address, + 'type' => $type, 'hide' => $hidden, 'port' => $port, 'user' => $user, @@ -1549,8 +1525,6 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima } if ($type === 'jmap') { - $imap_list['type'] = 'jmap'; - $imap_list['hide'] = $hidden; $imap_list['port'] = false; $imap_list['tls'] = false; } @@ -1567,7 +1541,7 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima } } else { $imap_server_id = Hm_IMAP_List::add($imap_list); - if (! can_save_last_added_server('Hm_IMAP_List', $user)) { + if ($type != 'ews' && ! can_save_last_added_server('Hm_IMAP_List', $user)) { return; } } @@ -1598,9 +1572,9 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima } } - $imap = Hm_IMAP_List::connect($imap_server_id, false); + $mailbox = Hm_IMAP_List::connect($imap_server_id, false); - if (imap_authed($imap)) { + if ($mailbox->authed()) { return $imap_server_id; } else { Hm_IMAP_List::del($imap_server_id); diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index 658c1c290..a44ebfd46 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -37,15 +37,11 @@ public function process() { if (!$filepath) { return; } - $cache = Hm_IMAP_List::get_cache($this->cache, $path[1]); - $imap = Hm_IMAP_List::connect($path[1], $cache); - if (!imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1], $this->cache); + if (! $mailbox) { return; } - if (!$imap->select_mailbox(hex2bin($path[2]))) { - return; - } - $content = $imap->get_message_content($uid, 0); + $content = $mailbox->get_message_content(hex2bin($path[2]), $uid); if (!$content) { return; } @@ -71,10 +67,9 @@ class Hm_Handler_imap_folder_status extends Hm_Handler_Module { public function process() { list($success, $form) = $this->process_form(array('imap_server_id', 'folder')); if ($success) { - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (imap_authed($imap)) { - $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->get_mailbox_status(hex2bin($form['folder'])))); + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_status(hex2bin($form['folder'])))); } } } @@ -255,16 +250,17 @@ public function process() { $screen = false; $parts = explode("_", $this->request->get['list_path']); $imap_server_id = $parts[1] ?? ''; - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); - $imap = Hm_IMAP_List::connect($imap_server_id, $cache); if ($form['imap_move_action'] == "screen_mail") { - $form['imap_move_action'] = "move"; - $screen = true; - $screen_folder = 'Screen emails'; - if (!count($imap->get_mailbox_status($screen_folder))) { - $imap->create_mailbox($screen_folder); + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { + $form['imap_move_action'] = "move"; + $screen = true; + $screen_folder = 'Screen emails'; + if (! count($mailbox->get_folder_status($screen_folder))) { + $mailbox->create_folder($screen_folder); + } + $form['imap_move_to'] = $parts[0] ."_". $parts[1] ."_".bin2hex($screen_folder); } - $form['imap_move_to'] = $parts[0] ."_". $parts[1] ."_".bin2hex($screen_folder); } list($msg_ids, $dest_path, $same_server_ids, $other_server_ids) = process_move_to_arguments($form); @@ -332,18 +328,17 @@ public function process() { $msg = str_replace("\r\n", "\n", $msg); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_id); - $imap = Hm_IMAP_List::connect($imap_id, $cache); $imap_details = Hm_IMAP_List::dump($imap_id); $sent_folder = false; - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_id, $this->cache); + if ($mailbox && $mailbox->authed()) { $specials = get_special_folders($this, $imap_id); if (array_key_exists('sent', $specials) && $specials['sent']) { $sent_folder = $specials['sent']; } if (!$sent_folder) { - $auto_sent = $imap->get_special_use_mailboxes('sent'); + $auto_sent = $mailbox->get_special_use_mailboxes('sent'); if (!array_key_exists('sent', $auto_sent)) { return; } @@ -354,16 +349,13 @@ public function process() { } if ($sent_folder) { Hm_Debug::add(sprintf("Attempting to save sent message for IMAP server %s in folder %s", $imap_details['server'], $sent_folder)); - if ($imap->append_start($sent_folder, mb_strlen($msg), true)) { - $imap->append_feed($msg."\r\n"); - if (!$imap->append_end()) { - Hm_Msgs::add('ERRAn error occurred saving the sent message'); - } + if (! $mailbox->store_message($sent_folder, $msg)) { + Hm_Msgs::add('ERRAn error occurred saving the sent message'); } $uid = null; - $mailbox_page = $imap->get_mailbox_page($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); + $mailbox_page = $mailbox->get_messages($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); foreach ($mailbox_page[1] as $mail) { - $msg_header = $imap->get_message_headers($mail['uid']); + $msg_header = $mailbox->get_message_headers($sent_folder, $mail['uid']); if ($msg_header['Message-Id'] === $mime->get_headers()['Message-Id']) { $uid = $mail['uid']; break; @@ -388,10 +380,9 @@ public function process() { if ($success) { $path = explode('_', $form['compose_msg_path']); if (count($path) == 3 && $path[0] == 'imap') { - $cache = Hm_IMAP_List::get_cache($this->cache, $path[1]); - $imap = Hm_IMAP_List::connect($path[1], $cache); - if (imap_authed($imap) && $imap->select_mailbox(hex2bin($path[2]))) { - $imap->message_action('UNFLAG', array($form['compose_msg_uid'])); + $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1], $this->cache); + if ($mailbox && $mailbox->authed()) { + $mailbox->message_action(hex2bin($path[2]), 'UNFLAG', array($form['compose_msg_uid'])); } } } @@ -410,11 +401,10 @@ public function process() { if ($success) { $path = explode('_', $form['compose_msg_path']); if (count($path) == 3 && $path[0] == 'imap') { - $cache = Hm_IMAP_List::get_cache($this->cache, $path[1]); - $imap = Hm_IMAP_List::connect($path[1], $cache); - if (imap_authed($imap) && $imap->select_mailbox(hex2bin($path[2]))) { - $this->out('folder_status', array('imap_'.$path[1].'_'.$path[2] => $imap->folder_state)); - $imap->message_action('ANSWERED', array($form['compose_msg_uid'])); + $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1], $this->cache); + if ($mailbox && $mailbox->authed()) { + $this->out('folder_status', array('imap_'.$path[1].'_'.$path[2] => $mailbox->get_folder_state())); + $mailbox->message_action(hex2bin($path[2]), 'ANSWERED', array($form['compose_msg_uid'])); } } } @@ -433,11 +423,10 @@ class Hm_Handler_imap_mark_as_read extends Hm_Handler_Module { public function process() { list($success, $form) = $this->process_form(array('imap_server_id', 'imap_msg_uid', 'folder')); if ($success) { - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (imap_authed($imap) && $imap->select_mailbox(hex2bin($form['folder']))) { - $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state)); - $imap->message_action('READ', array($form['imap_msg_uid'])); + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state())); + $mailbox->message_action(hex2bin($form['folder']), 'READ', array($form['imap_msg_uid'])); } } } @@ -494,49 +483,14 @@ public function process() { $msg_id = preg_replace("/^0.{1}/", '', $this->request->get['imap_msg_part']); } if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) { - $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - if (imap_authed($imap)) { - if ($imap->select_mailbox($folder)) { - $msg_struct = $imap->get_message_structure($uid); - $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $msg_id)); - if (!empty($struct)) { - $part_struct = array_shift($struct); - $encoding = false; - if (array_key_exists('encoding', $part_struct)) { - $encoding = trim(mb_strtolower($part_struct['encoding'])); - } - $stream_size = $imap->start_message_stream($uid, $msg_id); - if ($stream_size > 0) { - $charset = ''; - if (array_key_exists('attributes', $part_struct)) { - if (is_array($part_struct['attributes']) && array_key_exists('charset', $part_struct['attributes'])) { - $charset = '; charset='.$part_struct['attributes']['charset']; - } - } - header('Content-Type: '.$part_struct['type'].'/'.$part_struct['subtype'].$charset); - header('Content-Transfer-Encoding: binary'); - ob_end_clean(); - $output_line = ''; - while($line = $imap->read_stream_line()) { - if ($encoding == 'quoted-printable') { - $line = quoted_printable_decode($line); - } - elseif ($encoding == 'base64') { - $line = base64_decode($line); - } - echo $output_line; - $output_line = $line; - - } - if ($part_struct['type'] == 'text') { - $output_line = preg_replace("/\)(\r\n)$/m", '$1', $output_line); - } - echo $output_line; - Hm_Functions::cease(); - } - } - } + $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { + $mailbox->stream_message_part($folder, $uid, $msg_id, function ($content_type) { + header('Content-Type: ' . $content_type); + header('Content-Transfer-Encoding: binary'); + ob_end_clean(); + }); + Hm_Functions::cease(); } } Hm_Msgs::add('ERRAn Error occurred trying to download the message'); @@ -557,50 +511,15 @@ public function process() { list($server_id, $uid, $folder, $msg_id) = get_request_params($this->request->get); if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) { - $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - if (imap_authed($imap)) { - if ($imap->select_mailbox($folder)) { - $msg_struct = $imap->get_message_structure($uid); - $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $msg_id)); - if (!empty($struct)) { - $part_struct = array_shift($struct); - $encoding = false; - if (array_key_exists('encoding', $part_struct)) { - $encoding = trim(mb_strtolower($part_struct['encoding'])); - } - $stream_size = $imap->start_message_stream($uid, $msg_id); - if ($stream_size > 0) { - $name = get_imap_part_name($part_struct, $uid, $msg_id); - header('Content-Disposition: attachment; filename="'.$name.'"'); - $charset = ''; - if (array_key_exists('attributes', $part_struct)) { - if (is_array($part_struct['attributes']) && array_key_exists('charset', $part_struct['attributes'])) { - $charset = '; charset='.$part_struct['attributes']['charset']; - } - } - header('Content-Type: '.$part_struct['type'].'/'.$part_struct['subtype'].$charset); - header('Content-Transfer-Encoding: binary'); - ob_end_clean(); - $output_line = ''; - while($line = $imap->read_stream_line()) { - if ($encoding == 'quoted-printable') { - $line = quoted_printable_decode($line); - } - elseif ($encoding == 'base64') { - $line = base64_decode($line); - } - echo $output_line; - $output_line = $line; - } - if ($part_struct['type'] == 'text') { - $output_line = preg_replace("/\)(\r\n)$/m", '$1', $output_line); - } - echo $output_line; - Hm_Functions::cease(); - } - } - } + $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { + $mailbox->stream_message_part($folder, $uid, $msg_id, function ($content_type, $part_name) { + header('Content-Disposition: attachment; filename="' . $part_name . '"'); + header('Content-Type: ' . $content_type); + header('Content-Transfer-Encoding: binary'); + ob_end_clean(); + }); + Hm_Functions::cease(); } } Hm_Msgs::add('ERRAn Error occurred trying to download the message'); @@ -651,7 +570,13 @@ public function process() { else { $folder = hex2bin($parts[2]); } - $title = array('IMAP', $details['name'], $folder); + $mailbox = Hm_IMAP_List::get_connected_mailbox($details['id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + $label = $mailbox->get_folder_name($folder); + } else { + $label = $folder; + } + $title = array(strtoupper($details['type'] ?? 'IMAP'), $details['name'], $label); if ($this->get('list_page', 0)) { $title[] = sprintf('Page %d', $this->get('list_page', 0)); } @@ -695,29 +620,12 @@ public function process() { if (array_key_exists('imap_remove_attachment', $this->request->get) && $this->request->get['imap_remove_attachment']) { list($server_id, $uid, $folder, $msg_id) = get_request_params($this->request->get); if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) { - $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - if (imap_authed($imap)) { - if ($imap->select_mailbox($folder)) { - $msg = $imap->get_message_content($uid, 0, false, false); - if ($msg) { - $attachment_id = get_attachment_id_for_mail_parser($imap, $uid, $this->request->get['imap_msg_part']); - if ($attachment_id !== false) { - $msg = remove_attachment($attachment_id, $msg); - if ($imap->append_start($folder, mb_strlen($msg))) { - $imap->append_feed($msg."\r\n"); - if ($imap->append_end()) { - if ($imap->message_action('DELETE', array($uid))['status']) { - $imap->message_action('EXPUNGE', array($uid)); - Hm_Msgs::add('Attachment deleted'); - $this->out('redirect_url', '?page=message_list&list_path='.$this->request->get['list_path']); - return; - } - } - } - } - - } + $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { + if ($mailbox->remove_attachment($folder, $uid, $this->request->get['imap_msg_part'])) { + Hm_Msgs::add('Attachment deleted'); + $this->out('redirect_url', '?page=message_list&list_path=' . $this->request->get['list_path']); + return; } } } @@ -750,13 +658,12 @@ public function process() { $this->session->set('imap_prefetched_ids', array_unique($prefetched, SORT_STRING)); } $with_subscription = isset($this->request->post['subscription_state']) && $this->request->post['subscription_state']; - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - $this->out('can_share_folders', stripos($imap->get_capability(), 'ACL') !== false); - if (imap_authed($imap)) { - $quota_root = $imap->get_quota_root($folder ? $folder : 'INBOX'); + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + $this->out('can_share_folders', stripos($mailbox->get_capability(), 'ACL') !== false); + $quota_root = $mailbox->get_quota($folder ? $folder : 'INBOX', true); if ($quota_root && isset($quota_root[0]['name'])) { - $quota = $imap->get_quota($quota_root[0]['name']); + $quota = $mailbox->get_quota($quota_root[0]['name'], false); if ($quota) { $current = floatval($quota[0]['current']); $max = floatval($quota[0]['max']); @@ -775,12 +682,12 @@ public function process() { $this->out('folder', $folder); return; } - if (imap_authed($imap)) { + if ($mailbox && $mailbox->authed()) { $only_subscribed = $this->user_config->get('only_subscribed_folders_setting', false); if ($with_subscription) { $only_subscribed = false; } - $msgs = $imap->get_folder_list_by_level(hex2bin($folder), $only_subscribed, $with_subscription); + $msgs = $mailbox->get_subfolders(hex2bin($folder), $only_subscribed, $with_subscription); if (isset($msgs[$folder])) { unset($msgs[$folder]); } @@ -792,7 +699,7 @@ public function process() { $this->out('folder', $folder); } else { - Hm_Msgs::add(sprintf('ERRCould not authenticate to the selected %s server (%s)', $imap->server_type, $this->user_config->get('imap_servers')[$form['imap_server_id']]['user'])); + Hm_Msgs::add(sprintf('ERRCould not authenticate to the selected %s server (%s)', $mailbox->server_type(), $this->user_config->get('imap_servers')[$form['imap_server_id']]['user'])); } } } @@ -839,9 +746,8 @@ public function process() { } $path = sprintf("imap_%s_%s", $form['imap_server_id'], $form['folder']); $details = Hm_IMAP_List::dump($form['imap_server_id']); - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { $this->out('imap_mailbox_page_path', $path); if (isset($this->request->get['screen_emails']) && hex2bin($form['folder']) == 'INBOX' && $this->module_is_supported("contacts")) { $contacts = $this->get('contact_store'); @@ -850,9 +756,9 @@ public function process() { $existingEmails = array_map(function($c){ return $c->value('email_address'); },$contact_list); - list($total, $results) = $imap->get_mailbox_page(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword, $existingEmails, $include_content_body); + list($total, $results) = $mailbox->get_messages(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword, $existingEmails, $include_content_body); } else { - list($total, $results) = $imap->get_mailbox_page(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword, null, $include_content_body); + list($total, $results) = $mailbox->get_messages(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword, null, $include_content_body); } foreach ($results as $msg) { $msg['server_id'] = $form['imap_server_id']; @@ -864,8 +770,8 @@ public function process() { if ($this->isCeoFraud($msg['to'], $msg['subject'], $msg['preview_msg'])) { $folder = "Suspicious emails"; - if (!count($imap->get_mailbox_status($folder))) { - $imap->create_mailbox($folder); + if (!count($mailbox->get_mailbox_status($folder))) { + $mailbox->create_folder($folder); } $dest_folder = bin2hex($folder); $server_ids = array( @@ -885,14 +791,12 @@ public function process() { } $msgs[] = $msg; } - } - - if ($imap->selected_mailbox) { - $imap->selected_mailbox['detail']['exists'] = $total; - $this->out('imap_folder_detail', array_merge($imap->selected_mailbox, array('offset' => $offset, 'limit' => $limit))); + if ($folder = $mailbox->get_selected_folder()) { + $folder['detail']['exists'] = $total; + $this->out('imap_folder_detail', array_merge($folder, array('offset' => $offset, 'limit' => $limit))); } - $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state)); + $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state())); } $this->out('imap_mailbox_page', $msgs); $this->out('list_page', $list_page); @@ -989,29 +893,17 @@ public function process() { list($success, $form) = $this->process_form(array('imap_msg_uid', 'imap_server_id', 'folder')); if ($success) { $del_result = false; - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); $trash_folder = false; $specials = get_special_folders($this, $form['imap_server_id']); if (array_key_exists('trash', $specials) && $specials['trash']) { $trash_folder = $specials['trash']; } - if (imap_authed($imap)) { - if ($imap->select_mailbox(hex2bin($form['folder']))) { - $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state)); - if ($trash_folder && $trash_folder != hex2bin($form['folder'])) { - $moveResult = $imap->message_action('MOVE', array($form['imap_msg_uid']), $trash_folder); - if ($moveResult['status']) { - $del_result = true; - } - } - else { - if ($imap->message_action('DELETE', array($form['imap_msg_uid']))['status']) { - $del_result = true; - $imap->message_action('EXPUNGE', array($form['imap_msg_uid'])); - } - } + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + if ($mailbox->delete_message(hex2bin($form['folder']), $form['imap_msg_uid'], $trash_folder)) { + $del_result = true; } + $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state())); } if (!$del_result) { Hm_Msgs::add('ERRAn error occurred trying to delete this message'); @@ -1040,62 +932,64 @@ public function process() { if (!$success) { return; } - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); + $archive_folder = false; + $form_folder = hex2bin($form['folder']); $errors = 0; + $status = null; $specials = get_special_folders($this, $form['imap_server_id']); if (array_key_exists('archive', $specials) && $specials['archive']) { $archive_folder = $specials['archive']; } - if (!$archive_folder) { - Hm_Msgs::add('No archive folder configured for this IMAP server'); - $errors++; - } - if (!$errors && imap_authed($imap)) { - $archive_exists = count($imap->get_mailbox_status($archive_folder)); - if (!$archive_exists) { - Hm_Msgs::add('Configured archive folder for this IMAP server does not exist'); + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && ! $mailbox->is_imap() && empty($archive_folder)) { + // EWS supports archiving to user archive folders + $status = $mailbox->message_action($form_folder, 'ARCHIVE', array($form['imap_msg_uid']))['status']; + } else { + if (!$archive_folder) { + Hm_Msgs::add('No archive folder configured for this IMAP server'); $errors++; } - $form_folder = hex2bin($form['folder']); - - /* select source folder */ - if ($errors || !$imap->select_mailbox($form_folder)) { - Hm_Msgs::add('ERRAn error occurred archiving the message'); - $errors++; - } + if (! $errors && $mailbox && $mailbox->authed()) { + $archive_exists = count($mailbox->get_folder_status($archive_folder)); + if (!$archive_exists) { + Hm_Msgs::add('Configured archive folder for this IMAP server does not exist'); + $errors++; + } - /* path according to original option setting */ - if ($this->user_config->get('original_folder_setting', false)) { - $archive_folder .= '/'.$form_folder; - if (!count($imap->get_mailbox_status($archive_folder))) { - if (! $imap->create_mailbox($archive_folder)) { - $debug = $imap->show_debug(true, true, true); - if (! empty($debug['debug'])) { - Hm_Msgs::add('ERR' . array_pop($debug['debug'])); - } else { - Hm_Msgs::add('ERRCould not create configured archive folder for the original folder of the message'); + /* path according to original option setting */ + if ($this->user_config->get('original_folder_setting', false)) { + $archive_folder .= '/' . $form_folder; + if (!count($mailbox->get_folder_status($archive_folder))) { + if (! $mailbox->create_folder($archive_folder)) { + $debug = $mailbox->get_debug(); + if (! empty($debug['debug'])) { + Hm_Msgs::add('ERR' . array_pop($debug['debug'])); + } else { + Hm_Msgs::add('ERRCould not create configured archive folder for the original folder of the message'); + } + $errors++; } - $errors++; } } - } - /* try to move the message */ - if (!$errors) { - $moveResult = $imap->message_action('MOVE', array($form['imap_msg_uid']), $archive_folder); - if ($moveResult['status']) { - Hm_Msgs::add("Message archived"); + /* try to move the message */ + if (! $errors) { + $status = $mailbox->message_action($form_folder, 'MOVE', array($form['imap_msg_uid']), $archive_folder)['status']; } } - else { - Hm_Msgs::add('ERRAn error occurred archiving the message'); - } } + + if ($status) { + Hm_Msgs::add("Message archived"); + } else { + Hm_Msgs::add('ERRAn error occurred archiving the message'); + } + + $this->save_hm_msgs(); } } @@ -1111,21 +1005,18 @@ public function process() { list($success, $form) = $this->process_form(array('imap_flag_state', 'imap_msg_uid', 'imap_server_id', 'folder')); if ($success) { $flag_result = false; - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (imap_authed($imap)) { - if ($imap->select_mailbox(hex2bin($form['folder']))) { - $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state)); - if ($form['imap_flag_state'] == 'flagged') { - $cmd = 'UNFLAG'; - } - else { - $cmd = 'FLAG'; - } - if ($imap->message_action($cmd, array($form['imap_msg_uid']))['status']) { - $flag_result = true; - } + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + if ($form['imap_flag_state'] == 'flagged') { + $cmd = 'UNFLAG'; + } + else { + $cmd = 'FLAG'; + } + if ($mailbox->message_action(hex2bin($form['folder']), $cmd, array($form['imap_msg_uid']))['status']) { + $flag_result = true; } + $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state())); } if (!$flag_result) { Hm_Msgs::add('ERRAn error occurred trying to flag this message'); @@ -1157,11 +1048,10 @@ public function process() { $ids = explode(',', $form['imap_snooze_ids']); foreach ($ids as $msg_part) { list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part); - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); - $imap = Hm_IMAP_List::connect($imap_server_id, $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { $folder = hex2bin($folder); - if (snooze_message($imap, $msg_id, $folder, $snooze_tag)) { + if (snooze_message($mailbox, $msg_id, $folder, $snooze_tag)) { $snoozed_messages[] = $msg_id; } } @@ -1190,21 +1080,22 @@ class Hm_Handler_imap_unsnooze_message extends Hm_Handler_Module { public function process() { $servers = Hm_IMAP_List::dump(); foreach (array_keys($servers) as $server_id) { - $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { $folder = 'Snoozed'; - if (!count($imap->get_mailbox_status($folder))) { + $status = $mailbox->get_folder_status($folder); + if (! count($status)) { continue; } - $ret = $imap->get_mailbox_page($folder, 'DATE', false, 'ALL'); + $folder = $status['id']; + $ret = $mailbox->get_messages($folder, 'DATE', false, 'ALL'); foreach ($ret[1] as $msg) { - $msg_headers = $imap->get_message_headers($msg['uid']); + $msg_headers = $mailbox->get_message_headers($folder, $msg['uid']); if (isset($msg_headers['X-Snoozed'])) { try { $snooze_headers = parse_snooze_header($msg_headers['X-Snoozed']); if (new DateTime($snooze_headers['until']) <= new DateTime()) { - snooze_message($imap, $msg['uid'], $folder, null); + snooze_message($mailbox, $msg['uid'], $folder, null); } } catch (Exception $e) { Hm_Debug::add(sprintf('ERR Cannot unsnooze message: %s', $msg_headers['subject'])); @@ -1237,15 +1128,14 @@ public function process() { $specials = get_special_folders($this, $server); $trash_folder = false; $archive_folder = false; - $cache = Hm_IMAP_List::get_cache($this->cache, $server); - $imap = Hm_IMAP_List::connect($server, $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($server, $this->cache); + if ($mailbox && $mailbox->authed()) { $server_details = $this->user_config->get('imap_servers')[$server]; if ($form['action_type'] == 'delete') { if (array_key_exists('trash', $specials)) { if ($specials['trash']) { $trash_folder = $specials['trash']; - } else { + } elseif ($mailbox->is_imap()) { Hm_Msgs::add(sprintf('ERRNo trash folder configured for %s', $server_details['name'])); } } @@ -1254,53 +1144,51 @@ public function process() { if(array_key_exists('archive', $specials)) { if($specials['archive']) { $archive_folder = $specials['archive']; - } else { + } elseif ($mailbox->is_imap()) { Hm_Msgs::add(sprintf('ERRNo archive folder configured for %s', $server_details['name'])); } } } foreach ($folders as $folder => $uids) { - if ($imap->select_mailbox(hex2bin($folder))) { - $status['imap_'.$server.'_'.$folder] = $imap->folder_state; + $status['imap_'.$server.'_'.$folder] = $imap->folder_state; - if ($form['action_type'] == 'delete' && $trash_folder && $trash_folder != hex2bin($folder)) { - if (!$imap->message_action('MOVE', $uids, $trash_folder)['status']) { - $errs++; - } - else { - foreach ($uids as $uid) { - $moved[] = sprintf("imap_%s_%s_%s", $server, $uid, $folder); - } - } + if ($mailbox->is_imap() && $form['action_type'] == 'delete' && $trash_folder && $trash_folder != hex2bin($folder)) { + if (! $mailbox->message_action(hex2bin($folder), 'MOVE', $uids, $trash_folder)['status']) { + $errs++; } - elseif ($form['action_type'] == 'archive' && $archive_folder && $archive_folder != hex2bin($folder)) { - /* path according to original option setting */ - if ($this->user_config->get('original_folder_setting', false)) { - $archive_folder .= '/'.hex2bin($folder); - $dest_path_exists = count($imap->get_mailbox_status($archive_folder)); - if (!$dest_path_exists) { - $imap->create_mailbox($archive_folder); - } - } - if (!$imap->message_action('MOVE', $uids, $archive_folder)['status']) { - $errs++; + else { + foreach ($uids as $uid) { + $moved[] = sprintf("imap_%s_%s_%s", $server, $uid, $folder); } - else { - foreach ($uids as $uid) { - $moved[] = sprintf("imap_%s_%s_%s", $server, $uid, $folder); - } + } + } + elseif ($mailbox->is_imap() && $form['action_type'] == 'archive' && $archive_folder && $archive_folder != hex2bin($folder)) { + /* path according to original option setting */ + if ($this->user_config->get('original_folder_setting', false)) { + $archive_folder .= '/' . hex2bin($folder); + $dest_path_exists = count($mailbox->get_folder_status($archive_folder)); + if (!$dest_path_exists) { + $mailbox->create_folder($archive_folder); } } + if (! $mailbox->message_action(hex2bin($folder), 'MOVE', $uids, $archive_folder)['status']) { + $errs++; + } else { - if (!$imap->message_action(mb_strtoupper($form['action_type']), $uids)['status']) { - $errs++; + foreach ($uids as $uid) { + $moved[] = sprintf("imap_%s_%s_%s", $server, $uid, $folder); } - else { - $msgs += count($uids); - if ($form['action_type'] == 'delete') { - $imap->message_action('EXPUNGE', $uids); - } + } + } + else { + if (! $mailbox->message_action(hex2bin($folder), mb_strtoupper($form['action_type']), $uids)['status']) { + $errs++; + } + else { + $msgs += count($uids); + if ($form['action_type'] == 'delete') { + $mailbox->message_action(hex2bin($folder), 'EXPUNGE', $uids); } } } @@ -1434,13 +1322,12 @@ public function process() { if ($success) { $ids = explode(',', $form['imap_server_ids']); foreach ($ids as $id) { - $cache = Hm_IMAP_List::get_cache($this->cache, $id); $start_time = microtime(true); - $imap = Hm_IMAP_List::connect($id, $cache); + $mailbox = Hm_IMAP_List::get_connected_mailbox($id, $this->cache); $this->out('imap_connect_time', microtime(true) - $start_time); - if (imap_authed($imap)) { - $this->out('imap_capabilities_list', $imap->get_capability()); - $this->out('imap_connect_status', $imap->get_state()); + if ($mailbox && $mailbox->authed()) { + $this->out('imap_capabilities_list', $mailbox->get_capability()); + $this->out('imap_connect_status', $mailbox->get_state()); $this->out('imap_status_server_id', $id); } else { @@ -1592,7 +1479,7 @@ public function process() { $cache = array(); foreach ($servers as $index => $server) { if (isset($server['object']) && is_object($server['object'])) { - if ($server['object']->use_cache) { + if ($server['object']->use_cache()) { $cache[$index] = $server['object']->dump_cache('array'); } } @@ -1605,6 +1492,81 @@ public function process() { } } +/** + * Save EWS server details + * @subpackage imap/handler + */ +class Hm_Handler_save_ews_server extends Hm_Handler_Module { + public function process() { + list($success, $form) = $this->process_form(array( + 'ews_profile_name', + 'ews_email', + 'ews_password', + 'ews_server', + 'ews_server_id', + 'ews_hide_from_c_page', + 'ews_create_profile', + 'ews_profile_signature', + 'ews_profile_reply_to', + 'ews_profile_is_default', + )); + if ($success) { + $imap_server_id = connect_to_imap_server( + $form['ews_server'], + $form['ews_profile_name'], + null, + $form['ews_email'], + $form['ews_password'], + null, + null, + null, + 'ews', + $this, + $form['ews_hide_from_c_page'], + $form['ews_server_id'], + ); + if(empty($imap_server_id)) { + Hm_Msgs::add("ERRCould not save server"); + return; + } + $smtp_server_id = connect_to_smtp_server( + $form['ews_server'], + $form['ews_profile_name'], + null, + $form['ews_email'], + $form['ews_password'], + null, + 'ews', + $form['ews_server_id'], + ); + if ($form['ews_create_profile'] && $imap_server_id && $smtp_server_id) { + if (! strstr($form['ews_email'], '@')) { + $address = $form['ews_email'] . '@' . $form['ews_server']; + } else { + $address = $form['ews_email']; + } + add_profile($form['ews_profile_name'], $form['ews_profile_signature'], $form['ews_profile_reply_to'], $form['ews_profile_is_default'], $address, $form['ews_server'], $form['ews_email'], $smtp_server_id, $imap_server_id, $this); + } + // auto-assign special folders + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache); + if (is_object($mailbox) && $mailbox->authed()) { + $specials = $this->user_config->get('special_imap_folders', array()); + $exposed = $mailbox->get_special_use_mailboxes(); + $specials[$imap_server_id] = [ + 'sent' => $exposed['sent'] ?? '', + 'draft' => $exposed['drafts'] ?? '', + 'trash' => $exposed['trash'] ?? '', + 'archive' => $exposed['archive'] ?? '', + 'junk' => $exposed['junk'] ?? '' + ]; + $this->user_config->set('special_imap_folders', $specials); + } + $this->session->record_unsaved('EWS server added'); + $this->session->secure_cookie($this->request, 'hm_reload_folders', '1'); + } + } +} + /** * Save IMAP servers * @subpackage imap/handler @@ -1889,14 +1851,22 @@ public function process() { } } + $mailbox = false; $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache, $imap_details['user'], $imap_details['pass']); - if (imap_authed($imap)) { - Hm_Msgs::add(sprintf("Successfully authenticated to the %s server : %s", $imap->server_type, $imap_details['user'])); - return; + $mailbox = Hm_IMAP_List::connect($form['imap_server_id'], $cache, $form['imap_user'], $form['imap_pass']); + if ($mailbox) { + if ($mailbox->authed()) { + Hm_Msgs::add(sprintf("Successfully authenticated to the %s server : %s", $mailbox->server_type(), $form['imap_user'])); + } + else { + Hm_Msgs::add(sprintf("ERRFailed to authenticate to the %s server : %s", $mailbox->server_type(), $form['imap_user'])); + } + } + else { + Hm_Msgs::add('ERRUsername and password are required'); + $this->out('old_form', $form); } } - Hm_Msgs::add("ERRFailed to authenticate to IMAP server"); } } } @@ -1934,97 +1904,54 @@ public function process() { $this->out('header_allow_images', $this->config->get('allow_external_image_sources')); $this->out('images_whitelist', explode(',', $this->user_config->get('images_whitelist_setting'))); - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { if ($this->user_config->get('unread_on_open_setting', false)) { - $imap->read_only = true; + $mailbox->set_read_only(true); } else { - $imap->read_only = $prefetch; - } - if ($imap->select_mailbox(hex2bin($form['folder']))) { - $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state)); - $msg_struct = $imap->get_message_structure($form['imap_msg_uid']); - $this->out('msg_struct', $msg_struct); - if ($part !== false) { - if ($part == 0) { - $max = 500000; - } - else { - $max = false; - } - $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $part)); - $msg_struct_current = array_shift($struct); - $msg_text = $imap->get_message_content($form['imap_msg_uid'], $part, $max, $msg_struct_current); - } - else { - if (!$this->user_config->get('text_only_setting', false)) { - list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', 'html', $msg_struct); - if (!$part) { - list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', false, $msg_struct); - } - } - else { - list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', false, $msg_struct); - } - $struct = $imap->search_bodystructure( $msg_struct, array('imap_part_number' => $part)); - $msg_struct_current = array_shift($struct); - if (!trim($msg_text)) { - if (is_array($msg_struct_current) && array_key_exists('subtype', $msg_struct_current)) { - if ($msg_struct_current['subtype'] == 'plain') { - $subtype = 'html'; - } - else { - $subtype = 'plain'; - } - list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', $subtype, $msg_struct); - $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $part)); - $msg_struct_current = array_shift($struct); - } - } - } - if (isset($msg_struct_current['subtype']) && mb_strtolower($msg_struct_current['subtype'] == 'html')) { - $msg_text = add_attached_images($msg_text, $form['imap_msg_uid'], $msg_struct, $imap); - } - $save_reply_text = false; - if ($part == 0 || (isset($msg_struct_current['type']) && mb_strtolower($msg_struct_current['type'] == 'text'))) { - $save_reply_text = true; - } - $msg_headers = $imap->get_message_headers($form['imap_msg_uid']); - $this->out('list_headers', get_list_headers($msg_headers)); - $this->out('msg_headers', $msg_headers); - $this->out('imap_prefecth', $prefetch); - $this->out('imap_msg_part', "$part"); - $this->out('use_message_part_icons', $this->user_config->get('msg_part_icons_setting', false)); - $this->out('simple_msg_part_view', $this->user_config->get('simple_msg_parts_setting', DEFAULT_SIMPLE_MSG_PARTS)); - $this->out('allow_delete_attachment', $this->user_config->get('allow_delete_attachment_setting', false)); - if ($msg_struct_current) { - $this->out('msg_struct_current', $msg_struct_current); - } - $this->out('msg_text', $msg_text); - $download_args = sprintf("page=message&uid=%s&list_path=imap_%s_%s", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder']); - $this->out('msg_download_args', $download_args.'&imap_download_message=1'); - $this->out('msg_attachment_remove_args', $download_args.'&imap_remove_attachment=1'); - $this->out('msg_show_args', sprintf("page=message&uid=%s&list_path=imap_%s_%s&imap_show_message=1", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder'])); - - if ($this->get('imap_allow_images', false)) { - if ($this->module_is_supported('contacts') && $this->user_config->get('contact_auto_collect_setting', false)) { - $this->out('collect_contacts', true); - $this->out('collected_contact_email', $msg_headers["Return-Path"]); - $this->out('collected_contact_name', $msg_headers["From"]); - } + $mailbox->set_read_only($prefetch); + } + list($msg_struct, $msg_struct_current, $msg_text, $part) = $mailbox->get_structured_message(hex2bin($form['folder']), $form['imap_msg_uid'], $part, $this->user_config->get('text_only_setting', false)); + $save_reply_text = false; + if ($part == 0 || (isset($msg_struct_current['type']) && mb_strtolower($msg_struct_current['type'] == 'text'))) { + $save_reply_text = true; + } + $msg_headers = $mailbox->get_message_headers(hex2bin($form['folder']), $form['imap_msg_uid']); + $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state())); + $this->out('msg_struct', $msg_struct); + $this->out('list_headers', get_list_headers($msg_headers)); + $this->out('msg_headers', $msg_headers); + $this->out('imap_prefecth', $prefetch); + $this->out('imap_msg_part', "$part"); + $this->out('use_message_part_icons', $this->user_config->get('msg_part_icons_setting', false)); + $this->out('simple_msg_part_view', $this->user_config->get('simple_msg_parts_setting', DEFAULT_SIMPLE_MSG_PARTS)); + $this->out('allow_delete_attachment', $this->user_config->get('allow_delete_attachment_setting', false)); + if ($msg_struct_current) { + $this->out('msg_struct_current', $msg_struct_current); + } + $this->out('msg_text', $msg_text); + $download_args = sprintf("page=message&uid=%s&list_path=imap_%s_%s", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder']); + $this->out('msg_download_args', $download_args.'&imap_download_message=1'); + $this->out('msg_attachment_remove_args', $download_args.'&imap_remove_attachment=1'); + $this->out('msg_show_args', sprintf("page=message&uid=%s&list_path=imap_%s_%s&imap_show_message=1", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder'])); + + if ($this->get('imap_allow_images', false)) { + if ($this->module_is_supported('contacts') && $this->user_config->get('contact_auto_collect_setting', false)) { + $this->out('collect_contacts', true); + $this->out('collected_contact_email', $msg_headers["Return-Path"]); + $this->out('collected_contact_name', $msg_headers["From"]); } + } - if (!$prefetch) { - clear_existing_reply_details($this->session); - if ($part == 0) { - $msg_struct_current['type'] = 'text'; - $msg_struct_current['subtype'] = 'plain'; - } - $this->session->set(sprintf('reply_details_imap_%s_%s_%s', $form['imap_server_id'], $form['folder'], $form['imap_msg_uid']), - array('ts' => time(), 'msg_struct' => $msg_struct_current, 'msg_text' => ($save_reply_text ? $msg_text : ''), 'msg_headers' => $msg_headers)); + if (!$prefetch) { + clear_existing_reply_details($this->session); + if ($part == 0) { + $msg_struct_current['type'] = 'text'; + $msg_struct_current['subtype'] = 'plain'; } + $this->session->set(sprintf('reply_details_imap_%s_%s_%s', $form['imap_server_id'], $form['folder'], $form['imap_msg_uid']), + array('ts' => time(), 'msg_struct' => $msg_struct_current, 'msg_text' => ($save_reply_text ? $msg_text : ''), 'msg_headers' => $msg_headers)); } } } @@ -2040,10 +1967,9 @@ public function process() { $imap_msg_uid = $this->request->get['imap_msg_uid']; $folder = $this->request->get['imap_folder']; if ($imap_server_id && $imap_msg_uid && $folder) { - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); - $imap = Hm_IMAP_List::connect($imap_server_id, $cache); - if ($imap->select_mailbox(hex2bin($folder))) { - $msg_source = $imap->get_message_content($imap_msg_uid, 0, false); + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { + $msg_source = $mailbox->get_message_content(hex2bin($folder), $imap_msg_uid); $this->out('msg_source', $msg_source); } } @@ -2082,11 +2008,21 @@ public function process() { if (isset($this->request->post['imap_delete'])) { list($success, $form) = $this->process_form(array('imap_server_id')); if ($success) { + $type = imap_server_type($form['imap_server_id']); + if (strtolower($type) == 'ews') { + $details = Hm_IMAP_List::dump($form['imap_server_id']); + foreach (Hm_Profiles::getAll() as $profile) { + if ($details['user'] == $profile['user'] && $details['server'] == $profile['server']) { + Hm_Profiles::del($profile['id']); + Hm_SMTP_List::del($profile['smtp_id']); + } + } + } $res = Hm_IMAP_List::del($form['imap_server_id']); if ($res) { $this->out('deleted_server_id', $form['imap_server_id']); Hm_Msgs::add('Server deleted'); - $this->session->record_unsaved(sprintf('%s server deleted', imap_server_type($form['imap_server_id']))); + $this->session->record_unsaved(sprintf('%s server deleted', $type)); } } else { diff --git a/modules/imap/hm-ews.php b/modules/imap/hm-ews.php new file mode 100644 index 000000000..9b34fa702 --- /dev/null +++ b/modules/imap/hm-ews.php @@ -0,0 +1,1093 @@ +ews = ExchangeWebServices::fromUsernameAndPassword($config['server'], $config['username'], $config['password'], ['version' => ExchangeWebServices::VERSION_2016, 'trace' => 1]); + $this->api = new MailAPI($this->ews); + $this->api->getFolderByDistinguishedId(Enumeration\DistinguishedFolderIdNameType::INBOX); + $this->authed = true; + return true; + } catch (Exception\UnauthorizedException $e) { + return false; + } + } + + public function authed() { + return $this->authed; + } + + public function get_capability() { + // IMAP extra capabilities not supported here + return ''; + } + + public function get_folders($folder = null, $only_subscribed = false, $unsubscribed_folders = [], $with_input = false) { + $result = []; + if (empty($folder)) { + $folder = new Type\DistinguishedFolderIdType(Enumeration\DistinguishedFolderIdNameType::MESSAGE_ROOT); + } else { + $folder = new Type\FolderIdType($folder); + } + $request = array( + 'Traversal' => 'Shallow', + 'FolderShape' => array( + 'BaseShape' => 'AllProperties', + ), + 'ParentFolderIds' => $folder->toArray(true) + ); + $resp = $this->ews->FindFolder($request); + $folders = $resp->get('folders')->get('folder'); + if ($folders) { + $special = $this->get_special_use_folders(); + if ($folders instanceof Type\FolderType) { + $folders = [$folders]; + } + foreach($folders as $folder) { + $id = $folder->get('folderId')->get('id'); + $name = $folder->get('displayName'); + if ($only_subscribed && in_array($id, $unsubscribed_folders)) { + continue; + } + $result[$id] = array( + 'id' => $id, + 'parent' => $folder->get('parentFolderId')->get('id'), + 'delim' => false, + 'name' => $name, + 'name_parts' => [], + 'basename' => $name, + 'realname' => $name, + 'namespace' => '', + 'marked' => false, // doesn't seem to be used anywhere but imap returns it + 'noselect' => false, // all EWS folders are selectable + 'can_have_kids' => true, + 'has_kids' => $folder->get('childFolderCount') > 0, + 'children' => $folder->get('childFolderCount'), + 'special' => in_array($id, $special), + 'clickable' => ! $with_input && ! in_array($id, $unsubscribed_folders), + 'subscribed' => ! in_array($id, $unsubscribed_folders), + ); + } + } + return $result; + } + + public function get_special_use_folders($folder = false) { + $special = [ + 'trash' => Enumeration\DistinguishedFolderIdNameType::DELETED, + 'sent' => Enumeration\DistinguishedFolderIdNameType::SENT, + 'flagged' => false, + 'all' => false, + 'junk' => Enumeration\DistinguishedFolderIdNameType::JUNKEMAIL, + 'archive' => false, + 'drafts' => Enumeration\DistinguishedFolderIdNameType::DRAFTS, + ]; + foreach ($special as $type => $folderId) { + if ($folderId) { + try { + $distinguishedFolder = $this->api->getFolderByDistinguishedId($folderId); + if ($distinguishedFolder) { + $special[$type] = $distinguishedFolder->get('folderId')->get('id'); + } + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + } + } + } + $special = array_filter($special); + if (isset($special[$folder])) { + return [$folder => $special[$folder]]; + } else { + return $special; + } + } + + public function get_folder_status($folder) { + try { + if ($this->is_distinguished_folder($folder)) { + $folder = new Type\DistinguishedFolderIdType($folder); + $result = $this->api->getFolder($folder->toArray(true)); + } elseif (base64_encode(base64_decode($folder, true)) === $folder) { + $folder = new Type\FolderIdType($folder); + $result = $this->api->getFolder($folder->toArray(true)); + } else { + $result = $this->api->getFolderByDisplayName($folder, Enumeration\DistinguishedFolderIdNameType::MESSAGE_ROOT); + if (! $result) { + throw new Exception('Folder not found.'); + } + } + return [ + 'id' => $result->get('folderId')->get('id'), + 'name' => $result->get('displayName'), + 'messages' => $result->get('totalCount'), + 'uidvalidity' => false, + 'uidnext' => false, + 'recent' => false, + 'unseen' => $result->get('unreadCount'), + ]; + } catch (Exception\ExchangeException $e) { + // since this is used for missing folders check, we skip error reporting + return []; + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + return []; + } + } + + public function create_folder($folder, $parent = null) { + if (empty($parent)) { + $parent = new Type\DistinguishedFolderIdType(Enumeration\DistinguishedFolderIdNameType::MESSAGE_ROOT); + } else { + $parent = new Type\FolderIdType($parent); + } + try { + $request = [ + 'Folders' => ['Folder' => [ + 'DisplayName' => $folder + ]], + 'ParentFolderId' => $parent->toArray(true), + ]; + $result = $this->ews->CreateFolder($request); + return $result->get('id'); + } catch(\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + return false; + } + } + + public function rename_folder($folder, $new_name, $parent = null) { + $result = []; + if ($this->is_distinguished_folder($folder)) { + $folder = new Type\DistinguishedFolderIdType($folder); + } else { + $folder = new Type\FolderIdType($folder); + } + $new_folder = new Type\FolderType(); + $new_folder->displayName = $new_name; + $request = [ + 'FolderChanges' => [ + 'FolderChange' => [ + 'FolderId' => $folder->toArray(false), + 'Updates' => [ + 'SetFolderField' => [ + 'FieldURI' => [ + 'FieldURI' => 'folder:DisplayName', + ], + 'Folder' => $new_folder, + ], + ], + ], + ], + ]; + try { + $request = Type::buildFromArray($request); + $this->ews->UpdateFolder($request); + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + return false; + } + if ($parent) { + if ($this->is_distinguished_folder($parent)) { + $parent = new Type\DistinguishedFolderIdType($parent); + } else { + $parent = new Type\FolderIdType($parent); + } + $request = [ + 'FolderIds' => Utilities\getFolderIds([$folder]), + 'ToFolderId' => $parent->toArray(true), + ]; + try { + $request = Type::buildFromArray($request); + $this->ews->MoveFolder($request); + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + return false; + } + } + return true; + } + + public function delete_folder($folder) { + try { + return $this->api->deleteFolder(new Type\FolderIdType($folder)); + } catch(\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + return false; + } + } + + public function send_message($from, $recipients, $message, $delivery_receipt = false) { + try { + $msg = new Type\MessageType(); + $msg->setFrom($from); + $msg->setToRecipients($recipients); + $msg->setMimeContent(base64_encode($message)); + if ($delivery_receipt) { + $msg->setIsDeliveryReceiptRequested($delivery_receipt); + } + $this->api->sendMail($msg, [ + 'MessageDisposition' => 'SendOnly', // saving to sent items is handled by the imap module depending on the chosen sent folder + ]); + return; + } catch (\Exception $e) { + return $e->getMessage(); + } + } + + public function store_message($folder, $message, $seen = true, $draft = false) { + try { + if ($this->is_distinguished_folder($folder)) { + $folder = new Type\DistinguishedFolderIdType($folder); + } else { + $folder = new Type\FolderIdType($folder); + } + $msg = new Type\MessageType(); + $msg->setMimeContent(base64_encode($message)); + $flags = 0; + if ($seen) { + $flags |= self::PID_TAG_MESSAGE_READ; + } + if ($draft) { + $flags |= self::PID_TAG_MESSAGE_DRAFT; + } + $msg->addExtendedProperty(Type\ExtendedPropertyType::buildFromArray([ + 'ExtendedFieldURI' => [ + 'PropertyTag' => self::PID_TAG_MESSAGE_FLAGS, + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + 'Value' => $flags, + ])); + $result = $this->api->sendMail($msg, [ + 'MessageDisposition' => 'SaveOnly', + 'SavedItemFolderId' => $folder->toArray(true), + ]); + return $result->get('id'); + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + return false; + } + } + + /** + * Performs an EWS search using FindItem operation and supplies sorting + pagination arguments. + * Search can be perfomed using Advanced Query Syntax when keyword is an array containing terms + * searching in specific fields (e.g. advanced search) or Restrictions list when requesting + * filtering by extended properties as answered or unanswered emails. + */ + public function search($folder, $sort, $reverse, $flag_filter, $offset, $limit, $keyword, $trusted_senders) { + if ($this->is_distinguished_folder(strtolower($folder))) { + $folder = new Type\DistinguishedFolderIdType(strtolower($folder)); + } else { + $folder = new Type\FolderIdType($folder); + } + $request = array( + 'Traversal' => 'Shallow', + 'ItemShape' => array( + 'BaseShape' => 'IdOnly' + ), + 'IndexedPageItemView' => [ + 'MaxEntriesReturned' => $limit, + 'Offset' => $offset, + 'BasePoint' => 'Beginning', + ], + 'ParentFolderIds' => $folder->toArray(true) + ); + if (! empty($sort)) { + switch ($sort) { + case 'ARRIVAL': + $fieldURI = 'item:DateTimeCreated'; + break; + case 'DATE': + $fieldURI = 'item:DateTimeReceived'; + break; + case 'CC': + // TODO: figure out a way to sort by something not availalbe in FindItem operation + $fieldURI = null; + break; + case 'TO': + // TODO: figure out a way to sort by something not availalbe in FindItem operation + $fieldURI = null; + break; + case 'SUBJECT': + $fieldURI = 'item:Subject'; + break; + case 'FROM': + $fieldURI = 'message:From'; + break; + case 'SIZE': + $fieldURI = 'item:Size'; + break; + default: + $fieldURI = null; + } + if ($fieldURI) { + $request['SortOrder'] = [ + 'FieldOrder' => [ + 'Order' => $reverse ? 'Descending' : 'Ascending', + 'FieldURI' => [ + 'FieldURI' => $fieldURI, + ], + ] + ]; + } + } + $qs = []; + if (is_array($keyword)) { + foreach ($keyword as $term) { + switch ($term[0]) { + case 'SINCE': + $qs[] = "Received:>$term[1]"; + break; + case 'FROM': + $qs[] = "From:($term[1])"; + break; + case 'TO': + $qs[] = "To:($term[1])"; + break; + case 'CC': + $qs[] = "Cc:($term[1])"; + break; + case 'TEXT': + $qs[] = "(Subject:($term[1]) OR Body:($term[1]))"; + break; + case 'BODY': + $qs[] = "Body:($term[1])"; + break; + case 'SUBJECT': + $qs[] = "Subject:($term[1])"; + break; + default: + // noop + } + } + } elseif (! empty($keyword)) { + $qs[] = $keyword; + } + switch ($flag_filter) { + case 'UNSEEN': + $qs[] = 'isRead:false'; + break; + case 'SEEN': + $qs[] = 'isRead:true'; + break; + case 'FLAGGED': + $qs[] = 'isFlagged:true'; + break; + case 'UNFLAGGED': + $qs[] = 'isFlagged:false'; + break; + case 'ANSWERED': + $request['Restriction'] = [ + 'IsEqualTo' => [ + 'ExtendedFieldURI' => [ + 'PropertyTag' => self::PID_TAG_ICON_INDEX, + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + 'FieldURIOrConstant' => [ + 'Constant' => ['Value' => self::PID_TAG_ICON_REPLIED], + ], + ], + ]; + break; + case 'UNANSWERED': + $request['Restriction'] = [ + 'IsNotEqualTo' => [ + 'ExtendedFieldURI' => [ + 'PropertyTag' => self::PID_TAG_ICON_INDEX, + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + 'FieldURIOrConstant' => [ + 'Constant' => ['Value' => self::PID_TAG_ICON_REPLIED], + ], + ], + ]; + break; + break; + case 'ALL': + default: + // noop + } + if ($qs && empty($request['Restriction'])) { + $request['QueryString'] = implode(' ', $qs); + } elseif ($keyword && ! empty($request['Restriction'])) { + $restriction = ['And' => $request['Restriction']]; + $restriction['And']['Or'] = [ + [ + 'Contains' => [ + 'FieldURI' => ['FieldURI' => 'item:Subject'], + 'Constant' => ['Value' => $keyword], + ], + ], + [ + 'Contains' => [ + 'FieldURI' => ['FieldURI' => 'item:Body'], + 'Constant' => ['Value' => $keyword], + ], + ], + ]; + $request['Restriction'] = $restriction; + } + $request = Type::buildFromArray($request); + $result = $this->ews->FindItem($request); + $messages = $result->get('items')->get('message') ?? []; + if ($messages instanceof Type\MessageType) { + $messages = [$messages]; + } + $itemIds = array_map(function($msg) { + return $msg->get('itemId')->get('id'); + }, $messages); + return [$result->get('totalItemsInView'), $itemIds]; + } + + public function get_messages($folder, $sort, $reverse, $flag_filter, $offset, $limit, $keyword, $trusted_senders, $include_preview = false) { + list ($total, $itemIds) = $this->search($folder, $sort, $reverse, $flag_filter, $offset, $limit, $keyword, $trusted_senders); + return [$total, $this->get_message_list($itemIds, $include_preview)]; + } + + public function get_message_list($itemIds, $include_preview = false) { + if (empty($itemIds)) { + return []; + } + $request = array( + 'ItemShape' => array( + 'BaseShape' => 'AllProperties', + 'AdditionalProperties' => [ + 'ExtendedFieldURI' => [ + [ + 'PropertyTag' => self::PID_TAG_FLAG_STATUS, //check flagged msg + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + [ + 'PropertyTag' => self::PID_TAG_ICON_INDEX, // check if replied/answered + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + ], + ], + ), + 'ItemIds' => [ + 'ItemId' => array_map(function($id) { + return ['Id' => $id]; + }, $itemIds), + ], + ); + $request = Type::buildFromArray($request); + $result = $this->ews->GetItem($request); + if ($result instanceof Type\MessageType) { + $result = [$result]; + } + $messages = []; + foreach ($result as $message) { + $flags = $this->extract_flags($message); + $uid = bin2hex($message->get('itemId')->get('id')); + $msg = [ + 'uid' => $uid, + 'flags' => implode(' ', $flags), + 'internal_date' => $message->get('dateTimeCreated'), + 'size' => $message->get('size'), + 'date' => $message->get('dateTimeReceived'), + 'from' => $this->extract_mailbox($message->get('from')), + 'to' => $this->extract_mailbox($message->get('toRecipients')), + 'subject' => $message->get('subject'), + 'content-type' => null, + 'timestamp' => time(), + 'charset' => null, + 'x-priority' => null, + 'google_msg_id' => null, + 'google_thread_id' => null, + 'google_labels' => null, + 'list_archive' => null, + 'references' => $message->get('references'), + 'message_id' => $message->get('internetMessageId'), + 'x_auto_bcc' => null, + 'x_snoozed' => null, + ]; + foreach ($message->get('internetMessageHeaders')->InternetMessageHeader as $header) { + foreach (['x-gm-msgid' => 'google_msg_id', 'x-gm-thrid' => 'google_thread_id', 'x-gm-labels' => 'google_labels', 'x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'list-archive' => 'list_archive', 'content-type' => 'content-type', 'x-priority' => 'x-priority'] as $hname => $key) { + if (strtolower($header->get('headerName')) == $hname) { + $msg[$key] = (string) $header; + } + } + } + $cset = ''; + if (mb_stristr($msg['content-type'], 'charset=')) { + if (preg_match("/charset\=([^\s;]+)/", $msg['content-type'], $matches)) { + $cset = trim(mb_strtolower(str_replace(array('"', "'"), '', $matches[1]))); + } + } + $msg['charset'] = $cset; + $msg['preview_msg'] = $include_preview ? strip_tags($message->get('body')) : ""; + $messages[$uid] = $msg; + } + return $messages; + } + + public function message_action($action, $itemIds, $folder=false, $keyword=false) { + if (empty($itemIds)) { + return ['status' => true, 'responses' => []]; + } + if (! is_array($itemIds)) { + $itemIds = [$itemIds]; + } + $change = null; + $status = false; + $responses = []; + switch ($action) { + case 'ARCHIVE': + $status = $this->archive_items($itemIds); + break; + case 'DELETE': + $status = $this->delete_items($itemIds); + break; + case 'HARDDELETE': + $status = $this->delete_items($itemIds, true); + break; + case 'COPY': + $newIds = $this->copy_items($itemIds, $folder); + if ($newIds) { + foreach ($newIds as $key => $newId) { + $responses[] = ['oldUid' => $itemIds[$key], 'newUid' => $newId]; + } + $status = true; + } + break; + case 'MOVE': + $newIds = $this->move_items($itemIds, $folder); + if ($newIds) { + foreach ($newIds as $key => $newId) { + $responses[] = ['oldUid' => $itemIds[$key], 'newUid' => $newId]; + } + $status = true; + } + break; + case 'READ': + $change = ItemUpdateBuilder::buildUpdateItemChanges('Message', 'message', ['IsRead' => true]); + break; + case 'UNREAD': + $change = ItemUpdateBuilder::buildUpdateItemChanges('Message', 'message', ['IsRead' => false]); + break; + case 'FLAG': + $change = [ + 'SetItemField' => [ + 'ExtendedFieldURI' => [ + 'PropertyTag' => self::PID_TAG_FLAG_STATUS, + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + 'Message' => [ + 'ExtendedProperty' => [ + 'ExtendedFieldURI' => [ + 'PropertyTag' => self::PID_TAG_FLAG_STATUS, + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + 'Value' => self::PID_TAG_FLAG_FLAGGED, + ], + ], + ], + ]; + break; + case 'UNFLAG': + $change = [ + 'DeleteItemField' => [ + 'ExtendedFieldURI' => [ + 'PropertyTag' => self::PID_TAG_FLAG_STATUS, + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + ], + ]; + break; + case 'ANSWERED': + case 'UNDELETE': + case 'CUSTOM': + // TODO: unsupported out of the box, we can emulate via custom extended properties + $change = null; + $status = true; + break; + case 'EXPUNGE': + // not needed for EWS + return ['status' => true, 'responses' => $responses]; + default: + $change = null; + } + + if ($change) { + $changes = ['ItemChange' => []]; + foreach ($itemIds as $itemId) { + $changes['ItemChange'][] = [ + 'ItemId' => (new Type\ItemIdType(hex2bin($itemId)))->toArray(), + 'Updates' => $change, + ]; + } + $status = $this->api->updateItems($changes); + } + + return ['status' => $status, 'responses' => $responses]; + } + + public function get_message_headers($itemId) { + $request = array( + 'ItemShape' => array( + 'BaseShape' => 'AllProperties', + 'AdditionalProperties' => [ + 'ExtendedFieldURI' => [ + [ + 'PropertyTag' => self::PID_TAG_FLAG_STATUS, //check flagged msg + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + [ + 'PropertyTag' => self::PID_TAG_ICON_INDEX, // check if replied/answered + 'PropertyType' => Enumeration\MapiPropertyTypeType::INTEGER, + ], + ], + ], + ), + 'ItemIds' => [ + 'ItemId' => ['Id' => hex2bin($itemId)], + ], + ); + $request = Type::buildFromArray($request); + $message = $this->ews->GetItem($request); + $sender = $message->get('sender'); + $from = $message->get('from'); + $headers = []; + $headers['Arrival Date'] = $message->get('dateTimeCreated'); + if ($sender && $from) { + $headers['From'] = $message->get('sender')->get('mailbox')->get('name') . ' <' . $message->get('from')->get('mailbox')->get('emailAddress') . '>'; + } elseif ($sender) { + $headers['From'] = $this->extract_mailbox($sender); + } elseif ($from) { + $headers['From'] = $this->extract_mailbox($from); + } else { + $headers['From'] = null; + } + $headers['To'] = $this->extract_mailbox($message->get('toRecipients')); + if ($message->get('ccRecipients')) { + $headers['Cc'] = $this->extract_mailbox($message->get('ccRecipients')); + } + if ($message->get('bccRecipients')) { + $headers['Bcc'] = $this->extract_mailbox($message->get('bccRecipients')); + } + $headers['Flags'] = implode(' ', $this->extract_flags($message)); + foreach ($message->get('internetMessageHeaders')->InternetMessageHeader as $header) { + $name = $header->get('headerName'); + if (isset($headers[$name])) { + if (! is_array($headers[$name])) { + $headers[$name] = [$headers[$name]]; + } + $headers[$name][] = (string) $header; + } else { + $headers[$name] = (string) $header; + } + } + if (! $message->isRead()) { + $this->api->updateMailItem($message->getItemId(), [ + 'IsRead' => true, + ]); + } + return $headers; + } + + public function get_message_content($itemId, $part) { + if ($part) { + list($msg_struct, $msg_struct_current, $msg_text, $part) = $this->get_structured_message($itemId, $part, false); + return $msg_text; + } else { + $message = $this->get_mime_message_by_id($itemId); + $content = (string) $message; + return $content; + } + } + + public function stream_message_part($itemId, $part, $start_cb) { + if ($part !== '0' && $part) { + // imap handler modules strip this prefix + $part = '0.' . $part; + } + list($msg_struct, $part_struct, $msg_text, $part) = $this->get_structured_message($itemId, $part, false); + $charset = ''; + if (! empty($part_struct['attributes']['charset'])) { + $charset = '; charset=' . $part_struct['attributes']['charset']; + } + $part_name = get_imap_part_name($part_struct, $itemId, $part); + $start_cb($part_struct['type'] . '/' . $part_struct['subtype'] . $charset, $part_name); + if (! $charset) { + $charset = 'UTF-8'; + } else { + $charset = $part_struct['attributes']['charset']; + } + $stream = $part_struct['mime_object']->getContentStream($charset); + if ($stream) { + while (! $stream->eof()) { + echo $stream->read(1024); + } + } + } + + public function get_structured_message($itemId, $part, $text_only) { + $message = $this->get_mime_message_by_id($itemId); + $msg_struct = []; + $this->parse_mime_part($message, $msg_struct, 0); + if ($part !== false) { + $struct = $this->search_mime_part_in_struct($msg_struct, ['part_id' => $part], true); + } else { + $struct = null; + if (! $text_only) { + $struct = $this->search_mime_part_in_struct($msg_struct, ['type' => 'text', 'subtype' => 'html']); + } + if (! $struct) { + $struct = $this->search_mime_part_in_struct($msg_struct, ['type' => 'text']); + } + } + if ($struct) { + $part = array_key_first($struct); + $msg_struct_current = $struct[$part]; + $msg_text = $msg_struct_current['mime_object']->getContent(); + } else { + $part = false; + $msg_struct_current = null; + $msg_text = ''; + } + if (isset($msg_struct_current['subtype']) && mb_strtolower($msg_struct_current['subtype'] == 'html')) { + // add inline images + if (preg_match_all("/src=('|\"|)cid:([^\s'\"]+)/", $msg_text, $matches)) { + $cids = array_pop($matches); + foreach ($cids as $id) { + $struct = $this->search_mime_part_in_struct($msg_struct, ['id' => $id, 'type' => 'image']); + if ($struct) { + $struct = array_shift($struct); + $msg_text = str_replace('cid:'.$id, 'data:image/'.$struct['subtype'].';base64,'.base64_encode($struct['mime_object']->getContent()), $msg_text); + } + } + } + } + return [$msg_struct, $msg_struct_current, $msg_text, $part]; + } + + public function get_mime_message_by_id($itemId) { + $request = array( + 'ItemShape' => array( + 'BaseShape' => 'IdOnly', + 'IncludeMimeContent' => true, + ), + 'ItemIds' => [ + 'ItemId' => ['Id' => hex2bin($itemId)], + ], + ); + $request = Type::buildFromArray($request); + $message = $this->ews->GetItem($request); + $mime = $message->get('mimeContent'); + $content = base64_decode($mime); + if (strtoupper($mime->get('characterSet')) != 'UTF-8') { + $content = mb_convert_encoding($content, 'UTF-8', $mime->get('characterSet')); + } + $parser = new MailMimeParser(); + return $parser->parse($content, false); + } + + protected function parse_mime_part($part, &$struct, $part_num) { + $struct[$part_num] = []; + list($struct[$part_num]['type'], $struct[$part_num]['subtype']) = explode('/', $part->getContentType()); + if ($part->isMultiPart()) { + $boundary = $part->getHeaderParameter('Content-Type', 'boundary'); + if ($boundary) { + $struct[$part_num]['attributes'] = ['boundary' => $boundary]; + } + $struct[$part_num]['disposition'] = $part->getContentDisposition(); + $struct[$part_num]['language'] = ''; + $struct[$part_num]['location'] = ''; + } else { + $content = $part->getContent(); + $charset = $part->getCharset(); + if ($charset) { + $struct[$part_num]['attributes'] = ['charset' => $charset]; + } + $struct[$part_num]['id'] = $part->getContentId(); + $struct[$part_num]['description'] = $part->getHeaderValue('Content-Description'); + $struct[$part_num]['encoding'] = $part->getContentTransferEncoding(); + $struct[$part_num]['size'] = strlen($content); + $struct[$part_num]['lines'] = substr_count($content, "\n"); + $struct[$part_num]['md5'] = ''; + $struct[$part_num]['disposition'] = $part->getContentDisposition(); + if ($filename = $part->getFilename()) { + $struct[$part_num]['file_attributes'] = ['filename' => $filename]; + if ($part->getContentDisposition() == 'attachment') { + $struct[$part_num]['file_attributes']['attachment'] = true; + } + } else { + $struct[$part_num]['file_attributes'] = ''; + } + $struct[$part_num]['language'] = ''; + $struct[$part_num]['location'] = ''; + } + $struct[$part_num]['mime_object'] = $part; + if ($part->getChildCount() > 0) { + $struct[$part_num]['subs'] = []; + foreach ($part->getChildParts() as $i => $child) { + $this->parse_mime_part($child, $struct[$part_num]['subs'], $part_num . '.' . ($i+1)); + } + } + } + + protected function search_mime_part_in_struct($struct, $conditions, $all = false) { + $found = []; + foreach ($struct as $part_id => $sub) { + $matches = 0; + if (isset($conditions['part_id']) && $part_id == $conditions['part_id']) { + $matches++; + } + foreach ($conditions as $name => $value) { + if (isset($sub[$name]) && mb_stristr($sub[$name], $value)) { + $matches++; + } + } + if ($matches === count($conditions)) { + $part = $sub; + if (isset($part['subs'])) { + $part['subs'] = count($part['subs']); + } + $found[$part_id] = $part; + if (! $all) { + break; + } + } + if (isset($sub['subs'])) { + $found = array_merge($found, $this->search_mime_part_in_struct($sub['subs'], $conditions, $all)); + } + if (! $all && $found) { + break; + } + } + return $found; + } + + protected function extract_mailbox($data) { + if (is_array($data)) { + $result = []; + foreach ($data as $mailbox) { + $result[] = $this->extract_mailbox($mailbox); + } + return $result; + } elseif (is_object($data) && $data->Mailbox) { + return $data->Mailbox->get('name') . ' <' . $data->Mailbox->get('emailAddress') . '>'; + } elseif (is_object($data) && $data->get('mailbox')) { + return $data->get('mailbox')->get('name') . ' <' . $data->get('mailbox')->get('emailAddress') . '>'; + } else { + return (string) $data; + } + } + + protected function extract_flags($message) { + // note about flags: EWS - doesn't support the \Deleted flag + $flags = []; + if ($message->get('isRead')) { + $flags[] = '\\Seen'; + } + if ($message->get('isDraft')) { + $flags[] = '\\Draft'; + } + if ($extended_properties = $message->get('extendedProperty')) { + if ($extended_properties instanceof Type\ExtendedPropertyType) { + $extended_properties = [$extended_properties]; + } + foreach ($extended_properties as $prop) { + if (hexdec($prop->get('extendedFieldURI')->get('propertyTag')) == self::PID_TAG_FLAG_STATUS && $prop->get('value') > 0) { + $flags[] = '\\Flagged'; + } + if (hexdec($prop->get('extendedFieldURI')->get('propertyTag')) == self::PID_TAG_ICON_INDEX && $prop->get('value') == self::PID_TAG_ICON_REPLIED) { + $flags[] = '\\Answered'; + } + } + } + return $flags; + } + + protected function is_distinguished_folder($folder) { + $oClass = new ReflectionClass(new Enumeration\DistinguishedFolderIdNameType()); + $constants = $oClass->getConstants(); + return in_array($folder, $constants); + } + + protected function archive_items($itemIds) { + $result = true; + $folders = $this->get_parent_folders_of_items($itemIds); + foreach ($folders as $folder => $itemIds) { + if ($this->is_distinguished_folder($folder)) { + $folder = new Type\DistinguishedFolderIdType($folder); + } else { + $folder = new Type\FolderIdType($folder); + } + $request = [ + 'ArchiveSourceFolderId' => $folder->toArray(true), + 'ItemIds' => [ + 'ItemId' => $itemIds = array_map(function($itemId) { + return (new Type\ItemIdType($itemId))->toArray(); + }, $itemIds), + ] + ]; + $request = Type::buildFromArray($request); + try { + $result = $result && $this->ews->ArchiveItem($request); + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + $result = false; + } + } + return $result; + } + + protected function delete_items($itemIds, $hard = false) { + $result = true; + try { + if ($hard) { + $result = $this->api->deleteItems(array_map(function($itemId) { + return (new Type\ItemIdType(hex2bin($itemId)))->toArray(); + }, $itemIds), [ + 'DeleteType' => 'HardDelete', + ]); + } else { + $trash = $this->api->getFolderByDistinguishedId(Type\DistinguishedFolderIdNameType::DELETED); + $folders = $this->get_parent_folders_of_items($itemIds); + foreach ($folders as $folder => $itemIds) { + if ($trash && $folder == $trash->get('folderId')->get('id')) { + $options = ['DeleteType' => 'HardDelete']; + } else { + $options = []; + } + $result = $result && $this->api->deleteItems($itemIds, $options); + } + } + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + $result = false; + } + return $result; + } + + protected function copy_items($itemIds, $folder) { + if ($this->is_distinguished_folder($folder)) { + $folder = new Type\DistinguishedFolderIdType($folder); + } else { + $folder = new Type\FolderIdType($folder); + } + $request = [ + 'ToFolderId' => $folder->toArray(true), + 'ItemIds' => [ + 'ItemId' => array_map(function($itemId) { + return (new Type\ItemIdType(hex2bin($itemId)))->toArray(); + }, $itemIds), + ] + ]; + $request = Type::buildFromArray($request); + try { + $result = $this->ews->CopyItem($request); + if (! is_array($result)) { + $result = [$result]; + } + $result = array_map(function($itemId) { + return $itemId->get('id'); + }, $result); + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + $result = []; + } + return $result; + } + + protected function move_items($itemIds, $folder) { + if ($this->is_distinguished_folder($folder)) { + $folder = new Type\DistinguishedFolderIdType($folder); + } else { + $folder = new Type\FolderIdType($folder); + } + $request = [ + 'ToFolderId' => $folder->toArray(true), + 'ItemIds' => [ + 'ItemId' => array_map(function($itemId) { + return (new Type\ItemIdType(hex2bin($itemId)))->toArray(); + }, $itemIds), + ], + 'ReturnNewItemIds' => false, + ]; + $request = Type::buildFromArray($request); + try { + $result = $this->ews->MoveItem($request); + if (! is_array($result)) { + $result = [$result]; + } + $result = array_map(function($itemId) { + return $itemId->get('id'); + }, $result); + } catch (\Exception $e) { + Hm_Msgs::add('ERR' . $e->getMessage()); + $result = []; + } + return $result; + } + + protected function get_parent_folders_of_items($itemIds) { + $itemIds = array_map(function($itemId) { + return (new Type\ItemIdType(hex2bin($itemId)))->toArray(); + }, $itemIds); + $folders = null; + $request = [ + 'ItemShape' => [ + 'BaseShape' => 'IdOnly', + 'AdditionalProperties' => [ + 'FieldURI' => ['FieldURI' => 'item:ParentFolderId'], + ], + ], + 'ItemIds' => [ + 'ItemId' => $itemIds, + ], + ]; + $request = Type::buildFromArray($request); + $result = $this->ews->GetItem($request); + if ($result instanceof Type\MessageType) { + $result = [$result]; + } + foreach ($result as $message) { + $folder = $message->get('ParentFolderId')->get('id'); + if (! isset($folders[$folder])) { + $folders[$folder] = []; + } + $folders[$folder][] = $message->getItemId(); + } + return $folders; + } +} diff --git a/modules/imap/hm-imap.php b/modules/imap/hm-imap.php index 645082fd7..3e8759c32 100644 --- a/modules/imap/hm-imap.php +++ b/modules/imap/hm-imap.php @@ -11,6 +11,7 @@ require_once('hm-imap-cache.php'); require_once('hm-imap-bodystructure.php'); require_once('hm-jmap.php'); +require_once('hm-ews.php'); /** * IMAP connection manager @@ -21,18 +22,17 @@ class Hm_IMAP_List { use Hm_Server_List; public static $use_cache = true; + protected static $user_config; + protected static $session; public static function init($user_config, $session) { self::initRepo('imap_servers', $user_config, $session, self::$server_list); + self::$user_config = $user_config; + self::$session = $session; } public static function service_connect($id, $server, $user, $pass, $cache=false) { - if (array_key_exists('type', $server) && $server['type'] == 'jmap') { - self::$server_list[$id]['object'] = new Hm_JMAP(); - } - else { - self::$server_list[$id]['object'] = new Hm_IMAP(); - } + self::$server_list[$id]['object'] = new Hm_Mailbox($id, self::$user_config, self::$session); if (self::$use_cache && $cache && is_array($cache)) { self::$server_list[$id]['object']->load_cache($cache, 'array'); } @@ -58,6 +58,15 @@ public static function get_cache($hm_cache, $id) { $res = $hm_cache->get('imap'.$id); return $res; } + + public static function get_connected_mailbox($id, $hm_cache = null) { + if ($hm_cache) { + $cache = self::get_cache($hm_cache, $id); + } else { + $cache = false; + } + return self::connect($id, $cache); + } } /* for testing */ @@ -827,6 +836,7 @@ public function get_mailbox_status($mailbox, $args=array('UNSEEN', 'UIDVALIDITY' if ($this->check_response($response, true)) { $attributes = $this->parse_status_response($response); $this->check_mailbox_state_change($attributes); + $attributes['id'] = $mailbox; } return $attributes; } diff --git a/modules/imap/output_modules.php b/modules/imap/output_modules.php index c09551330..c72420fc6 100644 --- a/modules/imap/output_modules.php +++ b/modules/imap/output_modules.php @@ -185,7 +185,7 @@ protected function output() { if ($this->get('msg_headers')) { $txt = ''; $small_headers = array('subject', 'x-snoozed', 'date', 'from', 'to', 'reply-to', 'cc', 'flags'); - $reply_args = sprintf('&list_path=%s&uid=%d', + $reply_args = sprintf('&list_path=%s&uid=%s', $this->html_safe($this->get('msg_list_path')), $this->html_safe($this->get('msg_text_uid')) ); @@ -458,6 +458,10 @@ protected function output() { $type = 'JMAP'; } + if (array_key_exists('type', $vals) && $vals['type'] == 'ews') { + continue; + } + if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) { $disabled = 'disabled="disabled"'; $user_pc = $vals['user']; @@ -725,7 +729,7 @@ class Hm_Output_display_imap_status extends Hm_Output_Module { protected function output() { $res = ''; foreach ($this->get('imap_servers', array()) as $index => $vals) { - $res .= 'IMAP'.$vals['name'].''. + $res .= ''.(strtoupper($vals['type'] ?? 'IMAP')).''.$vals['name'].''. ''; } return $res; @@ -1449,6 +1453,166 @@ protected function output() { } } +class Hm_Output_server_config_ews extends Hm_Output_Module { + protected function output() { + $hasEWSActivated = in_array('imap', $this->get('router_module_list'), true); + + if(! $hasEWSActivated){ + return ''; + } + + $ews_servers_count = count(array_filter($this->get('imap_servers', array()), function($v) { return array_key_exists('type', $v) && $v['type'] == 'ews'; })); + + $res = '
+
+ + + ' . $this->trans('Exchange Servers') . ' + +
' . $ews_servers_count .' ' . $this->trans('EWS') . '
+
+
+
+
+
+ + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+ + + +
+
+
+ + +
+
+ + +
+
+ + + +
+
+
+
+ +
+
+
+ '; + + $list = $this->get('imap_servers', array()); + foreach ($list as $index => $vals) { + if (! array_key_exists('type', $vals) || $vals['type'] != 'ews') { + continue; + } + + $server_id = $vals['id']; + + if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) { + $disabled = 'disabled="disabled"'; + $user_pc = $vals['user']; + $pass_pc = $this->trans('[saved]'); + $pass_value = '*************'; + } + elseif (array_key_exists('nopass', $vals)) { + if (array_key_exists('user', $vals)) { + $user_pc = $vals['user']; + } + else { + $user_pc = ''; + } + $pass_pc = $this->trans('Password'); + $disabled = ''; + $pass_value = ''; + } + else { + $user_pc = ''; + $pass_pc = $this->trans('Password'); + $disabled = ''; + $pass_value = ''; + } + $res .= '
'; + $res .= '
'; + $res .= ''; + $res .= ''; + $res .= '
'; + $res .= sprintf(' +
%s
+
%s
', + $this->html_safe($vals['name']), $this->html_safe($vals['server'])); + + $res .= '
'; + + $res .= '
'; + $res .= ''; + $res .= '
'; + $res .= '
'; + $res .= '
'; + $res .= ''; + $res .= '
'; + $res .= '
'; + + $res .= '
'; + + // Buttons + $disabled = isset($vals['default']) ? ' disabled': ''; + if (!isset($vals['user']) || !$vals['user']) { + $res .= ''; + $res .= ''; + } else { + $keysToRemove = array('object', 'connected', 'default', 'nopass'); + $serverDetails = array_diff_key($vals, array_flip($keysToRemove)); + + $res .= 'html_safe(json_encode($serverDetails)).'\' data-id="'.$this->html_safe($serverDetails['name']).'" data-type="ews" />'; + $res .= ''; + $res .= ''; + } + + // Hide/Unhide Buttons + $hidden = array_key_exists('hide', $vals) && $vals['hide']; + $res .= ''; + $res .= ''; + + $res .= ''; + $res .= '
'; + } + + $res .= ' +
+
'; + + return $res; + } +} + /** * Option to set the per page count for IMAP folder views * @subpackage imap/output diff --git a/modules/imap/setup.php b/modules/imap/setup.php index 0aab24b11..4a75d5985 100644 --- a/modules/imap/setup.php +++ b/modules/imap/setup.php @@ -19,15 +19,19 @@ add_output('info', 'imap_server_ids', true, 'imap', 'page_js', 'before'); /* servers page data */ +add_handler('servers', 'profile_data', true, 'profiles', 'load_user_data', 'after'); +add_handler('servers', 'compose_profile_data', true, 'profiles', 'profile_data', 'after'); add_handler('servers', 'process_add_imap_server', true, 'imap', 'message_list_type', 'after'); add_handler('servers', 'process_add_jmap_server', true, 'imap', 'process_add_imap_server', 'after'); add_handler('servers', 'save_imap_servers', true, 'imap', 'process_add_jmap_server', 'after'); +add_handler('servers', 'save_ews_server', true, 'imap', 'save_imap_servers', 'after'); add_output('servers', 'display_configured_imap_servers', true, 'imap', 'server_config_stepper_accordion_end_part', 'before'); add_output('servers', 'imap_server_ids', true, 'imap', 'page_js', 'before'); add_output('servers', 'stepper_setup_server_jmap', true, 'imap', 'server_config_stepper_end_part', 'before'); add_output('servers', 'stepper_setup_server_imap', true, 'imap', 'server_config_stepper_end_part', 'before'); add_output('servers', 'stepper_setup_server_jmap_imap_common', true, 'imap', 'server_config_stepper_end_part', 'before'); +add_output('servers', 'server_config_ews', true, 'imap', 'server_config_stepper_accordion_end_part', 'after'); /* settings page data */ add_handler('settings', 'process_sent_since_setting', true, 'imap', 'date', 'after'); @@ -118,6 +122,10 @@ /* ajax server setup callback data */ setup_base_ajax_page('ajax_imap_debug', 'core'); +add_handler('ajax_imap_debug', 'profile_data', true, 'profiles', 'load_user_data', 'after'); +add_handler('ajax_imap_debug', 'compose_profile_data', true, 'profiles', 'profile_data', 'after'); +add_handler('ajax_imap_debug', 'profile_data', true, 'smtp', 'compose_profile_data', 'after'); +add_handler('ajax_imap_debug', 'load_smtp_servers_from_config', true, 'smtp', 'profile_data', 'after'); add_handler('ajax_imap_debug', 'load_imap_servers_from_config', true); add_handler('ajax_imap_debug', 'imap_oauth2_token_check', true); add_handler('ajax_imap_debug', 'imap_hide', true); @@ -442,6 +450,16 @@ 'tag_id' => FILTER_DEFAULT, 'first_time_screen_emails' => FILTER_VALIDATE_INT, 'move_messages_in_screen_email' => FILTER_VALIDATE_BOOLEAN, + 'ews_server_id' => FILTER_DEFAULT, + 'ews_profile_name' => FILTER_DEFAULT, + 'ews_email' => FILTER_DEFAULT, + 'ews_password' => FILTER_UNSAFE_RAW, + 'ews_server' => FILTER_DEFAULT, + 'ews_hide_from_c_page' => FILTER_VALIDATE_INT, + 'ews_create_profile' => FILTER_VALIDATE_INT, + 'ews_profile_is_default' => FILTER_VALIDATE_INT, + 'ews_profile_signature' => FILTER_DEFAULT, + 'ews_profile_reply_to' => FILTER_DEFAULT, 'imap_folder_uid' => FILTER_DEFAULT, 'imap_folder' => FILTER_DEFAULT, 'identifier' => FILTER_DEFAULT, diff --git a/modules/imap/site.js b/modules/imap/site.js index 4dbfb7817..c751439a6 100644 --- a/modules/imap/site.js +++ b/modules/imap/site.js @@ -75,6 +75,7 @@ var imapServersPageHandler = function() { $('.hide_imap_connection').on('click', imap_hide); $('.unhide_imap_connection').on('click', imap_unhide); $('.test_imap_connect').on('click', imap_test_action); + $('.edit_ews_server_connection').on('click', ews_edit_action); var dsp = Hm_Utils.get_from_local_storage('.imap_section'); if (dsp === 'block' || dsp === 'none') { @@ -84,6 +85,26 @@ var imapServersPageHandler = function() { if (jdsp === 'block' || jdsp === 'none') { $('.jmap_section').css('display', jdsp); } + + $('.ews-btn').on('click', function() { + $(this).hide().prev().removeClass('d-none'); + }); +}; + +var ews_edit_action = function(event) { + event.preventDefault(); + Hm_Notices.hide(true); + var details = $(this).data('server-details'); + + $('.ews-btn').trigger('click'); + $('#ews_profile_name').val(details.name).trigger('focus'); + $('#ews_email').val(details.user); + $('#ews_password').val(''); + $('#ews_profile_reply_to').val(''); + $('#ews_create_profile').trigger("click", true); + $('#ews_server').val(details.server); + $('#ews_server_id').val(details.id); + $('#ews_hide_from_c_page').prop("checked", details.hide); }; var set_message_content = function(path, msg_uid) { diff --git a/modules/imap_folders/modules.php b/modules/imap_folders/modules.php index 4e926b7d4..7daa5f982 100644 --- a/modules/imap_folders/modules.php +++ b/modules/imap_folders/modules.php @@ -98,15 +98,13 @@ public function process() { if (!$success || !in_array($form['special_folder_type'], array('sent', 'draft', 'trash', 'archive', 'junk'), true)) { return; } - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - - if (!is_object($imap) || $imap->get_state() != 'authenticated') { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if (!is_object($mailbox) || ! $mailbox->authed()) { Hm_Msgs::add('ERRUnable to connect to the selected IMAP server'); return; } - $new_folder = prep_folder_name($imap, $form['folder'], true); - if (!$new_folder || !$imap->select_mailbox($new_folder)) { + $new_folder = $mailbox->prep_folder_name($form['folder']); + if (! $new_folder || ! $mailbox->get_folder_status($new_folder)) { Hm_Msgs::add('ERRSelected folder not found'); return; } @@ -132,15 +130,13 @@ public function process() { list($success, $form) = $this->process_form(array('imap_server_id', 'imap_service_name')); if ($success) { - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - - if (!is_object($imap) || $imap->get_state() != 'authenticated') { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if (!is_object($mailbox) || ! $mailbox->authed()) { Hm_Msgs::add('ERRUnable to connect to the selected IMAP server'); return; } $specials = $this->user_config->get('special_imap_folders', array()); - $exposed = $imap->get_special_use_mailboxes(); + $exposed = $mailbox->get_special_use_mailboxes(); if ($form['imap_service_name'] == 'gandi') { $specials[$form['imap_server_id']] = array( 'sent' => 'Sent', @@ -178,15 +174,12 @@ public function process() { list($success, $form) = $this->process_form(array('folder', 'imap_server_id')); if ($success) { $parent = false; - $parent_str = false; if (array_key_exists('parent', $this->request->post) && trim($this->request->post['parent'])) { - $parent_str = decode_folder_str($this->request->post['parent']); + $parent = decode_folder_str($this->request->post['parent']); } - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (is_object($imap) && $imap->get_state() == 'authenticated') { - $new_folder = prep_folder_name($imap, $form['folder'], false, $parent_str); - if ($new_folder && $imap->create_mailbox($new_folder)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + if ($form['folder'] && $mailbox->create_folder($form['folder'], $parent)) { Hm_Msgs::add('Folder created'); $this->cache->del('imap_folders_imap_'.$form['imap_server_id'].'_'); $this->out('imap_folders_success', true); @@ -206,17 +199,14 @@ class Hm_Handler_process_folder_rename extends Hm_Handler_Module { public function process() { list($success, $form) = $this->process_form(array('imap_server_id', 'folder', 'new_folder')); if ($success) { - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - $parent_str = false; + $parent = false; if (array_key_exists('parent', $this->request->post)) { - $parent_str = $this->request->post['parent']; + $parent = $this->request->post['parent']; } - if (is_object($imap) && $imap->get_state() == 'authenticated') { - $old_folder = prep_folder_name($imap, $form['folder'], true); - $new_folder = prep_folder_name($imap, $form['new_folder'], false, $parent_str); - if ($new_folder && $old_folder && $imap->rename_mailbox($old_folder, $new_folder)) { - if ($this->module_is_supported('sievefilters') && $this->user_config->get('enable_sieve_filter_setting', DEFAULT_ENABLE_SIEVE_FILTER)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + if ($form['folder'] && $form['new_folder'] && $mailbox->rename_folder($form['folder'], $form['new_folder'], $parent)) { + if ($this->module_is_supported('sievefilters') && $this->user_config->get('enable_sieve_filter_setting', DEFAULT_ENABLE_SIEVE_FILTER) && $mailbox->is_imap()) { $imap_servers = $this->user_config->get('imap_servers'); $imap_account = $imap_servers[$form['imap_server_id']]; $linked_mailboxes = get_sieve_linked_mailbox($imap_account, $this); @@ -271,17 +261,16 @@ class Hm_Handler_process_folder_delete extends Hm_Handler_Module { public function process() { list($success, $form) = $this->process_form(array('imap_server_id', 'folder')); if ($success) { - $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']); - $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache); - if (is_object($imap) && $imap->get_state() == 'authenticated') { - $del_folder = prep_folder_name($imap, $form['folder'], true); - if ($this->module_is_supported('sievefilters') && $this->user_config->get('enable_sieve_filter_setting', DEFAULT_ENABLE_SIEVE_FILTER)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + if ($this->module_is_supported('sievefilters') && $this->user_config->get('enable_sieve_filter_setting', DEFAULT_ENABLE_SIEVE_FILTER) && $mailbox->is_imap()) { + $del_folder = prep_folder_name($mailbox->get_connection(), $form['folder'], true); if (is_mailbox_linked_with_filters($del_folder, $form['imap_server_id'], $this)) { Hm_Msgs::add('ERRThis folder can\'t be deleted because it is used in a filter.'); return; } } - if ($del_folder && $imap->delete_mailbox($del_folder)) { + if ($form['folder'] && $mailbox->delete_folder($form['folder'])) { Hm_Msgs::add('Folder deleted'); $this->cache->del('imap_folders_imap_'.$form['imap_server_id'].'_'); $this->out('imap_folders_success', true); @@ -307,6 +296,14 @@ public function process() { $this->out('archive_folder', $specials[$this->request->get['imap_server_id']]['archive']); $this->out('draft_folder', $specials[$this->request->get['imap_server_id']]['draft']); $this->out('junk_folder', $specials[$this->request->get['imap_server_id']]['junk']); + $mailbox = Hm_IMAP_List::get_connected_mailbox($this->request->get['imap_server_id'], $this->cache); + if ($mailbox && $mailbox->authed()) { + $folder_names = []; + foreach ($specials[$this->request->get['imap_server_id']] as $name => $folder) { + $folder_names[$name] = $mailbox->get_folder_name($folder); + } + $this->out('special_folder_names', $folder_names); + } } } else { @@ -344,13 +341,12 @@ public function process() { list($success, $form) = $this->process_form(array('folder', 'subscription_state')); if ($success) { $imap_server_id = $this->request->get['imap_server_id']; - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); - $imap = Hm_IMAP_List::connect($imap_server_id, $cache); - if (imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache); + if ($mailbox && $mailbox->authed()) { $folder = hex2bin($form['folder']); - $success = $imap->mailbox_subscription($folder, $form['subscription_state']); + $success = $mailbox->folder_subscription($folder, $form['subscription_state']); if ($success) { - Hm_Msgs::add(sprintf('%s to %s', $form['subscription_state']? 'Subscribed': 'Unsubscribed', $folder)); + Hm_Msgs::add(sprintf('%s to %s', $form['subscription_state']? 'Subscribed': 'Unsubscribed', $mailbox->get_folder_name($folder))); $this->cache->del('imap_folders_imap_'.$imap_server_id.'_'); } else { Hm_Msgs::add(sprintf('ERRAn error occurred %s to %s', $form['subscription_state']? 'subscribing': 'unsubscribing', $folder)); @@ -489,13 +485,15 @@ protected function output() { } $sent_folder = $this->get('sent_folder', $this->trans('Not set')); - if (!$sent_folder) { - $sent_folder = $this->trans('Not set'); + if (! $sent_folder) { + $folder_name = $this->trans('Not set'); + } else { + $folder_name = $this->get('special_folder_names')['sent'] ?? $sent_folder; } $res = '
'; $res .= '
- '.$this->trans('Sent Folder').':'.$sent_folder.' + '.$this->trans('Sent Folder').':'.$folder_name.'
'; $res .= '
@@ -533,13 +531,15 @@ protected function output() { } $archive_folder = $this->get('archive_folder', $this->trans('Not set')); - if (!$archive_folder) { - $archive_folder = $this->trans('Not set'); + if (! $archive_folder) { + $folder_name = $this->trans('Not set'); + } else { + $folder_name = $this->get('special_folder_names')['archive'] ?? $archive_folder; } $res = '
'; $res .= ''; $res .= '
@@ -577,13 +577,15 @@ protected function output() { } $draft_folder = $this->get('draft_folder', $this->trans('Not set')); - if (!$draft_folder) { - $draft_folder = $this->trans('Not set'); + if (! $draft_folder) { + $folder_name = $this->trans('Not set'); + } else { + $folder_name = $this->get('special_folder_names')['draft'] ?? $draft_folder; } $res = '
'; $res .= ''; $res .= '
@@ -621,13 +623,15 @@ protected function output() { } $trash_folder = $this->get('trash_folder', $this->trans('Not set')); - if (!$trash_folder) { - $trash_folder = $this->trans('Not set'); + if (! $trash_folder) { + $folder_name = $this->trans('Not set'); + } else { + $folder_name = $this->get('special_folder_names')['trash'] ?? $trash_folder; } $res = '
'; $res .= ''; $res .= ''; $res .= '
@@ -662,13 +666,15 @@ protected function output() { } $junk_folder = $this->get('junk_folder', $this->trans('Not set')); - if (!$junk_folder) { - $junk_folder = $this->trans('Not set'); + if (! $junk_folder) { + $folder_name = $this->trans('Not set'); + } else { + $folder_name = $this->get('special_folder_names')['junk'] ?? $junk_folder; } $res = '
'; $res .= ''; $res .= ''; diff --git a/modules/nux/modules.php b/modules/nux/modules.php index f1b4378a9..f3942401a 100644 --- a/modules/nux/modules.php +++ b/modules/nux/modules.php @@ -65,6 +65,8 @@ class Hm_Handler_nux_homepage_data extends Hm_Handler_Module { public function process() { $imap_servers = NULL; + $jmap_servers = NULL; + $ews_servers = NULL; $smtp_servers = NULL; $feed_servers = NULL; $profiles = NULL; @@ -72,13 +74,17 @@ public function process() { $modules = $this->config->get_modules(); if (data_source_available($modules, 'imap')) { - $imap_servers = count(Hm_IMAP_List::dump(false)); + $servers = Hm_IMAP_List::dump(false); + $imap_servers = count(array_filter($servers, function($server) { return empty($server['type']) || $server['type'] === 'imap'; })); + $jmap_servers = count(array_filter($servers, function($server) { return @$server['type'] === 'jmap'; })); + $ews_servers = count(array_filter($servers, function($server) { return @$server['type'] === 'ews'; })); } if (data_source_available($modules, 'feeds')) { $feed_servers = count(Hm_Feed_List::dump(false)); } if (data_source_available($modules, 'smtp')) { - $smtp_servers = count(Hm_SMTP_List::dump(false)); + $servers = Hm_SMTP_List::dump(false); + $smtp_servers = count(array_filter($servers, function($server) { return empty($server['type']) || $server['type'] === 'smtp'; })); } if (data_source_available($modules, 'profiles')) { Hm_Profiles::init($this); @@ -87,6 +93,8 @@ public function process() { $this->out('nux_server_setup', array( 'imap' => $imap_servers, + 'jmap' => $jmap_servers, + 'ews' => $ews_servers, 'feeds' => $feed_servers, 'smtp' => $smtp_servers, 'profiles' => $profiles @@ -183,8 +191,8 @@ public function process() { if (! can_save_last_added_server('Hm_IMAP_List', $form['nux_email'])) { return; } - $imap = Hm_IMAP_List::connect($new_id, false); - if ($imap && $imap->get_state() == 'authenticated') { + $mailbox = Hm_IMAP_List::connect($new_id, false); + if ($mailbox && $mailbox->authed()) { if (isset($details['smtp'])) { Hm_SMTP_List::add(array( 'name' => $details['name'], @@ -404,6 +412,7 @@ public function process() $server['profile']['is_default'], $server['username'], ($server['jmap']['server'] ?? $server['imap']['server']), + $server['username'], $smtp_server_id, ($jmap_server_id ?? $imap_server_id), $this @@ -558,7 +567,7 @@ protected function output() { } $server_data = $this->get('nux_server_setup', array()); $tz = $this->get('tzone'); - $protos = array('imap', 'smtp', 'feeds', 'profiles'); + $protos = array('imap', 'jmap', 'ews', 'smtp', 'feeds', 'profiles'); $res = '

'.$this->trans('Welcome to Cypht').'

'; $res .= '

'.$this->trans('Add a popular E-mail source quickly and easily').'

'; diff --git a/modules/profiles/functions.php b/modules/profiles/functions.php index bb7abe014..dcfc10005 100644 --- a/modules/profiles/functions.php +++ b/modules/profiles/functions.php @@ -3,7 +3,7 @@ if (!defined('DEBUG_MODE')) { die(); } if (!hm_exists('add_profile')) { - function add_profile($name, $signature, $reply_to, $is_default, $email, $server_mail, $smtp_server_id, $imap_server_id, $context, $remark = '') { + function add_profile($name, $signature, $reply_to, $is_default, $email, $server, $user, $smtp_server_id, $imap_server_id, $context, $remark = '') { $profile = array( 'name' => $name, 'sig' => $signature, @@ -12,11 +12,13 @@ function add_profile($name, $signature, $reply_to, $is_default, $email, $server_ 'replyto' => $reply_to, 'default' => $is_default, 'address' => $email, - 'server' => $server_mail, - 'user' => $email, + 'server' => $server, + 'user' => $user, 'type' => 'imap' ); - - Hm_Profiles::add($profile); + $id = Hm_Profiles::add($profile); + if ($is_default) { + Hm_Profiles::setDefault($id); + } } } diff --git a/modules/sievefilters/modules.php b/modules/sievefilters/modules.php index 35b157931..8568b7fee 100644 --- a/modules/sievefilters/modules.php +++ b/modules/sievefilters/modules.php @@ -291,14 +291,13 @@ public function process() { $imap_server_id = $idx; } } - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); - $imap = Hm_IMAP_List::connect($imap_server_id, $cache); - if (!imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache); + if (! $mailbox || ! $mailbox->authed()) { Hm_Msgs::add('ERRIMAP Authentication Failed'); return; } $mailboxes = []; - foreach ($imap->get_mailbox_list() as $idx => $mailbox) { + foreach ($mailbox->get_folders() as $idx => $mailbox) { $mailboxes[] = $mailbox['name']; } $this->out('mailboxes', json_encode($mailboxes)); @@ -452,17 +451,12 @@ public function process() { if (isset($this->request->post['imap_msg_uid'])) { $form['imap_msg_uid'] = $this->request->post['imap_msg_uid']; - $imap = Hm_IMAP_List::connect($this->request->post['imap_server_id']); - - if (!imap_authed($imap)) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($this->request->post['imap_server_id'], $this->cache); + if (! $mailbox || ! $mailbox->authed()) { Hm_Msgs::add('ERRIMAP Authentication Failed'); return; } - if (!$imap->select_mailbox(hex2bin($this->request->post['folder']))) { - Hm_Msgs::add('ERRIMAP Mailbox select error'); - return; - } - $msg_header = $imap->get_message_headers($form['imap_msg_uid']); + $msg_header = $mailbox->get_message_headers(hex2bin($this->request->post['folder']), $form['imap_msg_uid']); $test_pattern = "/(?:[a-z0-9!#$%&'*+=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/"; preg_match_all($test_pattern, $msg_header['From'], $email_sender); $email_sender = $email_sender[0][0]; diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index 260ad84e8..68a8db3b1 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -4,10 +4,11 @@ if (!defined('DEBUG_MODE')) { die(); } if (!hm_exists('connect_to_smtp_server')) { - function connect_to_smtp_server($address, $name, $port, $user, $pass, $tls, $server_id = false) { + function connect_to_smtp_server($address, $name, $port, $user, $pass, $tls, $type, $server_id = false) { $smtp_list = array( 'name' => $name, 'server' => $address, + 'type' => $type, 'hide' => false, 'port' => $port, 'user' => $user, @@ -25,13 +26,13 @@ function connect_to_smtp_server($address, $name, $port, $user, $pass, $tls, $ser } } else { $smtp_server_id = Hm_SMTP_List::add($smtp_list); - if (! can_save_last_added_server('Hm_SMTP_List', $user)) { + if ($type != 'ews' && ! can_save_last_added_server('Hm_SMTP_List', $user)) { return; } } - $smtp = Hm_SMTP_List::connect($smtp_server_id, false); - if (smtp_authed($smtp)) { + $mailbox = Hm_SMTP_List::connect($smtp_server_id, false); + if ($mailbox->authed()) { return $smtp_server_id; } else { diff --git a/modules/smtp/hm-smtp.php b/modules/smtp/hm-smtp.php index ace96b306..779bb4c5e 100644 --- a/modules/smtp/hm-smtp.php +++ b/modules/smtp/hm-smtp.php @@ -14,8 +14,13 @@ class Hm_SMTP_List { use Hm_Server_List; + protected static $user_config; + protected static $session; + public static function init($user_config, $session) { self::initRepo('smtp_servers', $user_config, $session, self::$server_list); + self::$user_config = $user_config; + self::$session = $session; } public static function service_connect($id, $server, $user, $pass, $cache=false) { @@ -25,7 +30,8 @@ public static function service_connect($id, $server, $user, $pass, $cache=false) 'port' => $server['port'], 'tls' => $server['tls'], 'username' => $user, - 'password' => $pass + 'password' => $pass, + 'type' => array_key_exists('type', $server) ? $server['type'] : 'smtp', ); if (array_key_exists('auth', $server)) { $config['auth'] = $server['auth']; @@ -33,16 +39,17 @@ public static function service_connect($id, $server, $user, $pass, $cache=false) if (array_key_exists('no_auth', $server)) { $config['no_auth'] = true; } - self::$server_list[$id]['object'] = new Hm_SMTP($config); - - if (!self::$server_list[$id]['object']->connect()) { + self::$server_list[$id]['object'] = new Hm_Mailbox($id, self::$user_config, self::$session); + if (! self::$server_list[$id]['object']->connect($config)) { return self::$server_list[$id]['object']; } return false; } + public static function get_cache($session, $id) { return false; } + public static function address_list() { $addrs = array(); foreach (self::$server_list as $server) { diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 6d2adb9ee..5142ce3e3 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -56,11 +56,10 @@ public function process() { && array_key_exists('list_path', $this->request->get) && array_key_exists('uid', $this->request->get)) { $path = explode('_', $this->request->get['list_path']); - $imap = Hm_IMAP_List::connect($path[1]); - if ($imap->select_mailbox(hex2bin($path[2]))) { - $msg_struct = $imap->get_message_structure($this->request->get['uid']); - list($part, $msg_text) = $imap->get_first_message_part($this->request->get['uid'], 'text', 'plain', $msg_struct); - $msg_header = $imap->get_message_headers($this->request->get['uid']); + $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1]); + if ($mailbox && $mailbox->authed()) { + list ($msg_struct, $msg_struct_first, $msg_text, $part) = $mailbox->get_structured_message(hex2bin($path[2]), $this->request->get['uid'], false, true); + $msg_header = $mailbox->get_message_headers(hex2bin($path[2]), $this->request->get['uid']); if (!array_key_exists('From', $msg_header) || count($msg_header) == 0) { return; } @@ -78,7 +77,7 @@ public function process() { $new_attachment['size'] = $sub['size']; $new_attachment['type'] = $sub['type']; $file_path = $this->config->get('attachment_dir').DIRECTORY_SEPARATOR.$new_attachment['name']; - $content = Hm_Crypt::ciphertext($imap->get_message_content($this->request->get['uid'], $ind), Hm_Request_Key::generate()); + $content = Hm_Crypt::ciphertext($mailbox->get_message_content(hex2bin($path[2]), $this->request->get['uid'], $ind), Hm_Request_Key::generate()); file_put_contents($file_path, $content); $new_attachment['tmp_name'] = $file_path; $new_attachment['filename'] = $file_path; @@ -126,14 +125,13 @@ public function process() { if (array_key_exists('forward_as_attachment', $this->request->get)) { $path = explode('_', $this->request->get['list_path']); - $imap = Hm_IMAP_List::connect($path[1]); - if ($imap && $imap->select_mailbox(hex2bin($path[2]))) { - $msg_struct = $imap->get_message_structure($this->request->get['uid']); - $msg_header = $imap->get_message_headers($this->request->get['uid']); + $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1]); + if ($mailbox && $mailbox->authed()) { + $msg_header = $mailbox->get_message_headers(hex2bin($path[2]), $this->request->get['uid']); if (!array_key_exists('From', $msg_header) || count($msg_header) == 0) { return; } - $content = $imap->get_message_content($this->request->get['uid'], 0, false, false); + $content = $mailbox->get_message_content(hex2bin($path[2]), $this->request->get['uid']); # Attachment Download $attached_files = []; @@ -143,7 +141,7 @@ public function process() { if (!is_dir($file_dir)) { mkdir($file_dir); } - $name = $msg_header['subject'] . '.eml'; + $name = $msg_header['Subject'] . '.eml'; $file_path = $file_dir . $name; $attached_files[$this->request->get['uid']][] = array( 'name' => $name, @@ -151,8 +149,9 @@ public function process() { 'size' => strlen($content), 'tmp_name' => $file_path, 'filename' => $file_path, - 'basename' => $msg_header['subject'] + 'basename' => $msg_header['Subject'] ); + $content = Hm_Crypt::ciphertext($content, Hm_Request_Key::generate()); file_put_contents($file_path, $content); $this->session->set('uploaded_files', $attached_files); $this->out('as_attr', true); @@ -172,11 +171,10 @@ public function process() { } if (array_key_exists('forward', $this->request->get)) { $path = explode('_', $this->request->get['list_path']); - $imap = Hm_IMAP_List::connect($path[1]); - if ($imap && $imap->select_mailbox(hex2bin($path[2]))) { - $msg_struct = $imap->get_message_structure($this->request->get['uid']); - list($part, $msg_text) = $imap->get_first_message_part($this->request->get['uid'], 'text', 'plain', $msg_struct); - $msg_header = $imap->get_message_headers($this->request->get['uid']); + $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1]); + if ($mailbox && $mailbox->authed()) { + list ($msg_struct, $msg_struct_first, $msg_text, $part) = $mailbox->get_structured_message(hex2bin($path[2]), $this->request->get['uid'], false, true); + $msg_header = $mailbox->get_message_headers(hex2bin($path[2]), $this->request->get['uid']); if (!array_key_exists('From', $msg_header) || count($msg_header) == 0) { return; } @@ -196,7 +194,7 @@ public function process() { $new_attachment['size'] = $sub['size']; $new_attachment['type'] = $sub['type']; $file_path = $file_dir . $new_attachment['name']; - $content = Hm_Crypt::ciphertext($imap->get_message_content($this->request->get['uid'], $ind), Hm_Request_Key::generate()); + $content = Hm_Crypt::ciphertext($mailbox->get_message_content(hex2bin($path[2]), $this->request->get['uid'], $ind), Hm_Request_Key::generate()); file_put_contents($file_path, $content); $new_attachment['tmp_name'] = $file_path; $new_attachment['filename'] = $file_path; @@ -518,13 +516,17 @@ public function process() { } } } - $smtp = Hm_SMTP_List::connect($form['smtp_server_id'], false, $smtp_details['user'], $smtp_details['pass']); - if (smtp_authed($smtp)) { + $mailbox = Hm_SMTP_List::connect($form['smtp_server_id'], false, $smtp_details['user'], $smtp_details['pass']); + if ($mailbox && $mailbox->authed()) { Hm_Msgs::add("Successfully authenticated to the SMTP server"); - return; + } + elseif ($mailbox && $mailbox->state() == 'connected') { + Hm_Msgs::add("ERRConnected, but failed to authenticate to the SMTP server"); + } + else { + Hm_Msgs::add("ERRFailed to authenticate to the SMTP server"); } } - Hm_Msgs::add("ERRFailed to authenticate to the SMTP server"); } } } @@ -660,8 +662,6 @@ public function process() { 'draft_subject' => $form['compose_subject'], 'draft_smtp' => $smtp_id ); - $from_params = ''; - $recipients_params = ''; /* parse attachments */ $uploaded_files = []; @@ -679,11 +679,6 @@ public function process() { /* msg details */ list($body, $cc, $bcc, $in_reply_to, $draft) = get_outbound_msg_detail($this->request->post, $draft, $body_type); - if ($this->user_config->get('enable_compose_delivery_receipt_setting', false) && !empty($this->request->post['compose_delivery_receipt'])) { - $from_params = 'RET=HDRS'; - $recipients_params = 'NOTIFY=SUCCESS,FAILURE'; - } - /* smtp server details */ $smtp_details = Hm_SMTP_List::dump($smtp_id, true); if (!$smtp_details) { @@ -703,8 +698,8 @@ public function process() { list($from, $reply_to) = outbound_address_check($this, $from, $reply_to); /* try to connect */ - $smtp = Hm_SMTP_List::connect($smtp_id, false); - if (!smtp_authed($smtp)) { + $mailbox = Hm_SMTP_List::connect($smtp_id, false); + if (! $mailbox || ! $mailbox->authed()) { Hm_Msgs::add("ERRFailed to authenticate to the SMTP server"); repopulate_compose_form($draft, $this); return; @@ -726,7 +721,7 @@ public function process() { } /* send the message */ - $err_msg = $smtp->send_message($from, $recipients, $mime->get_mime_msg(), $from_params, $recipients_params); + $err_msg = $mailbox->send_message($from, $recipients, $mime->get_mime_msg(), $this->user_config->get('enable_compose_delivery_receipt_setting', false) && !empty($this->request->post['compose_delivery_receipt'])); if ($err_msg) { Hm_Msgs::add(sprintf("ERR%s", $err_msg)); repopulate_compose_form($draft, $this); @@ -737,7 +732,7 @@ public function process() { $auto_bcc = $this->user_config->get('smtp_auto_bcc_setting', DEFAULT_SMTP_AUTO_BCC); if ($auto_bcc) { $mime->set_auto_bcc($from); - $bcc_err_msg = $smtp->send_message($from, array($from), $mime->get_mime_msg()); + $bcc_err_msg = $mailbox->send_message($from, array($from), $mime->get_mime_msg()); } /* check for associated IMAP server to save a copy */ @@ -757,13 +752,13 @@ public function process() { $msg_path = explode('_', $this->request->post['compose_msg_path']); $msg_uid = $this->request->post['compose_msg_uid']; - $imap = Hm_IMAP_List::connect($msg_path[1]); - if ($imap->select_mailbox(hex2bin($msg_path[2]))) { + $mailbox = Hm_IMAP_List::get_connected_mailbox($msg_path[1]); + if ($mailbox && $mailbox->authed()) { $specials = get_special_folders($this, $msg_path[1]); if (array_key_exists('archive', $specials) && $specials['archive']) { $archive_folder = $specials['archive']; - $imap->message_action('ARCHIVE', array($msg_uid)); - $imap->message_action('MOVE', array($msg_uid), $archive_folder); + $mailbox->message_action(hex2bin($msg_path[2]), 'ARCHIVE', array($msg_uid)); + $mailbox->message_action(hex2bin($msg_path[2]), 'MOVE', array($msg_uid), $archive_folder); } } } @@ -1405,6 +1400,10 @@ protected function output() { $no_edit = false; + if (array_key_exists('type', $vals) && $vals['type'] == 'ews') { + continue; + } + if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) { $disabled = 'disabled="disabled"'; $user_pc = $vals['user']; @@ -1732,13 +1731,10 @@ function get_primary_recipient($profiles, $headers, $smtp_servers, $is_draft=Fal */ if (!hm_exists('delete_draft')) { function delete_draft($id, $cache, $imap_server_id, $folder) { - $imap = Hm_IMAP_List::connect($imap_server_id); - if (! imap_authed($imap)) { - return false; - } - if ($imap->select_mailbox($folder)) { - if ($imap->message_action('DELETE', array($id))['status']) { - $imap->message_action('EXPUNGE', array($id)); + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id); + if ($mailbox && $mailbox->authed()) { + if ($mailbox->message_action($folder, 'DELETE', array($id))['status']) { + $mailbox->message_action($folder, 'EXPUNGE', array($id)); return true; } } @@ -1858,7 +1854,9 @@ function get_uploaded_files_from_array($uploaded_files) { } function prepare_draft_mime($atts, $uploaded_files, $from = false, $name = '') { - $uploaded_files = get_uploaded_files_from_array($uploaded_files); + if (! empty($uploaded_files) && ! is_array($uploaded_files[0])) { + $uploaded_files = get_uploaded_files_from_array($uploaded_files); + } $mime = new Hm_MIME_Msg( $atts['draft_to'], $atts['draft_subject'], @@ -1912,9 +1910,10 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files Hm_Msgs::add('ERRThere is no draft directory configured for this account.'); return -1; } - $cache = Hm_IMAP_List::get_cache($mod_cache, $imap_profile['id']); - $imap = Hm_IMAP_List::connect($imap_profile['id'], $cache); - $draft_folder = $imap->select_mailbox($specials['draft']); + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_profile['id'], $mod_cache); + if (! $mailbox || ! $mailbox->authed()) { + return -1; + } $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name); $res = $mime->process_attachments(); @@ -1923,24 +1922,21 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - if ($imap->append_start($specials['draft'], mb_strlen($msg), false, true)) { - $imap->append_feed($msg."\r\n"); - if (!$imap->append_end()) { - Hm_Msgs::add('ERRAn error occurred saving the draft message'); - return -1; - } + if (! $mailbox->store_message($specials['draft'], $msg, false, true)) { + Hm_Msgs::add('ERRAn error occurred saving the draft message'); + return -1; } - $mailbox_page = $imap->get_mailbox_page($specials['draft'], 'ARRIVAL', true, 'DRAFT', 0, 10); + $messages = $mailbox->get_messages($specials['draft'], 'ARRIVAL', true, 'DRAFT', 0, 10); // Remove old version from the mailbox if ($id) { - $imap->message_action('DELETE', array($id)); - $imap->message_action('EXPUNGE', array($id)); + $mailbox->message_action($specials['draft'], 'DELETE', array($id)); + $mailbox->message_action($specials['draft'], 'EXPUNGE', array($id)); } - foreach ($mailbox_page[1] as $mail) { - $msg_header = $imap->get_message_headers($mail['uid']); + foreach ($messages[1] as $mail) { + $msg_header = $mailbox->get_message_headers($specials['draft'], $mail['uid']); // Convert all header keys to lowercase $msg_header_lower = array_change_key_case($msg_header, CASE_LOWER); $mime_headers_lower = array_change_key_case($mime->get_headers(), CASE_LOWER); @@ -2138,14 +2134,6 @@ function profile_from_compose_smtp_id($profiles, $id) { return false; }} -/** - * @subpackage smtp/functions - */ -if (!hm_exists('smtp_authed')) { -function smtp_authed($smtp) { - return is_object($smtp) && $smtp->state == 'authed'; -}} - /** * @subpackage smtp/functions */ @@ -2188,6 +2176,7 @@ function default_smtp_server($user_config, $session, $request, $config, $user, $ $attributes = array( 'name' => $config->get('default_smtp_name', 'Default'), 'default' => true, + 'type' => 'smtp', 'server' => $smtp_server, 'port' => $smtp_port, 'tls' => $smtp_tls, diff --git a/modules/smtp/setup.php b/modules/smtp/setup.php index 954926634..916bf9e61 100644 --- a/modules/smtp/setup.php +++ b/modules/smtp/setup.php @@ -27,7 +27,7 @@ add_handler('profiles', 'add_smtp_servers_to_page_data', true, 'smtp', 'load_smtp_servers_from_config', 'after'); /* servers page */ -add_handler('servers', 'load_smtp_servers_from_config', true, 'smtp', 'language', 'after'); +add_handler('servers', 'load_smtp_servers_from_config', true, 'smtp', 'load_user_data', 'after'); add_handler('servers', 'process_add_smtp_server', true, 'smtp', 'load_smtp_servers_from_config', 'after'); add_handler('servers', 'add_smtp_servers_to_page_data', true, 'smtp', 'process_add_smtp_server', 'after'); add_handler('servers', 'save_smtp_servers', true, 'smtp', 'add_smtp_servers_to_page_data', 'after'); @@ -120,7 +120,7 @@ 'reply_all' => FILTER_VALIDATE_INT, 'forward' => FILTER_VALIDATE_INT, 'forward_as_attachment' => FILTER_VALIDATE_INT, - 'draft_id' => FILTER_VALIDATE_INT, + 'draft_id' => FILTER_DEFAULT, 'hm_ajax_hook' => FILTER_DEFAULT, 'compose_to' => FILTER_DEFAULT, 'mailto_uri' => FILTER_DEFAULT, @@ -139,7 +139,7 @@ 'allowed_output' => array( 'file_details' => array(FILTER_UNSAFE_RAW, false), 'draft_subject' => array(FILTER_DEFAULT, false), - 'draft_id' => array(FILTER_VALIDATE_INT, false), + 'draft_id' => array(FILTER_DEFAULT, false), 'profile_value' => array(FILTER_DEFAULT, false), 'msg_sent_and_archived' => array(FILTER_VALIDATE_BOOLEAN, false), 'sent_msg_id' => array(FILTER_VALIDATE_BOOLEAN, false), @@ -171,7 +171,7 @@ 'compose_delivery_receipt' => FILTER_VALIDATE_BOOLEAN, 'enable_compose_delivery_receipt' => FILTER_VALIDATE_INT, 'compose_smtp_id' => FILTER_DEFAULT, - 'draft_id' => FILTER_VALIDATE_INT, + 'draft_id' => FILTER_DEFAULT, 'draft_body' => FILTER_UNSAFE_RAW, 'draft_subject' => FILTER_UNSAFE_RAW, 'draft_to' => FILTER_UNSAFE_RAW, diff --git a/modules/smtp/site.js b/modules/smtp/site.js index 44c62b2bb..30a6bce69 100644 --- a/modules/smtp/site.js +++ b/modules/smtp/site.js @@ -336,13 +336,14 @@ var is_valid_recipient = function(recipient) { var process_compose_form = function(){ var msg_uid = getMessageUidParam(); - var detail = Hm_Utils.parse_folder_path(getListPathParam(), 'imap'); - var class_name = 'imap_' + detail.server_id + '_' + msg_uid + '_' + detail.folder; - var key = 'imap_' + Hm_Utils.get_url_page_number() + '_' + getListPathParam(); - var next_message = Hm_Message_List.prev_next_links(key, class_name)[1]; - - if (next_message) { - $('.compose_next_email_data').val(next_message); + if (getListPathParam()) { + var detail = Hm_Utils.parse_folder_path(getListPathParam(), 'imap'); + var class_name = 'imap_' + detail.server_id + '_' + msg_uid + '_' + detail.folder; + var key = 'imap_' + Hm_Utils.get_url_page_number() + '_' + getListPathParam(); + var next_message = Hm_Message_List.prev_next_links(key, class_name)[1]; + if (next_message) { + $('.compose_next_email_data').val(next_message); + } } var uploaded_files = $("input[name='uploaded_files[]']").map(function () { return $(this).val(); }).get(); @@ -352,6 +353,7 @@ var process_compose_form = function(){ $('.smtp_send_archive').addClass('disabled_input'); $('.smtp_send').on("click", function () { return false; }); } + var force_send_message = function() { // Check if the force_send input already exists var forceSendInput = document.getElementById('force_send'); diff --git a/modules/tags/handler_modules.php b/modules/tags/handler_modules.php index 53c735225..8836e6d52 100644 --- a/modules/tags/handler_modules.php +++ b/modules/tags/handler_modules.php @@ -33,6 +33,7 @@ public function process() { foreach ($ids as $msg_part) { list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part); $folder = hex2bin($folder); + $msg_id = hex2bin($msg_id); $tagged = Hm_Tags::addMessage($form['tag_id'], $imap_server_id, $folder, $msg_id); if ($tagged) { $taged_messages++; @@ -135,18 +136,16 @@ public function process() { foreach ($ids as $serverId) { $folders = Hm_Tags::getFolders($tag_id, $serverId); if (!empty($folders)) { - $cache = Hm_IMAP_List::get_cache($this->cache, $serverId); - $imap = Hm_IMAP_List::connect($serverId, $cache); + $mailbox = Hm_IMAP_List::get_connected_mailbox($serverId, $this->cache); $server_details = Hm_IMAP_List::dump($serverId); - if (imap_authed($imap)) { + if ($mailbox && $mailbox->authed()) { foreach ($folders as $folder => $messageIds) { - $imap->select_mailbox($folder); $messages = array_map(function($msg) use ($serverId, $folder, $server_details) { $msg['server_id'] = $serverId; $msg['folder'] = bin2hex($folder); $msg['server_name'] = $server_details['name']; return $msg; - }, $imap->get_message_list($messageIds)); + }, $mailbox->get_message_list($folder, $messageIds)); $msg_list = array_merge($msg_list, $messages); } } diff --git a/modules/tags/hm-tags.php b/modules/tags/hm-tags.php index 5155b8632..d79580f79 100644 --- a/modules/tags/hm-tags.php +++ b/modules/tags/hm-tags.php @@ -39,16 +39,8 @@ public static function addMessage($tagId, $serverId, $folder, $messageId) { public static function registerFolder($tag_id, $serverId, $folder) { $tag = self::get($tag_id); - if (isset($tag['server'])) { - if (isset($tag['server'][$serverId])) { - if (!in_array($folder, $tag['server'][$serverId])) { - $tag['server'][$serverId][] = $folder; - } - } else { - $tag['server'][$serverId] = array($folder); - } - } else { - $tag['server'] = [$serverId => [$folder]]; + if (! isset($tag['server'][$serverId][$folder])) { + $tag['server'][$serverId][$folder] = []; } self::edit($tag_id, $tag); } diff --git a/tests/phpunit/helpers.php b/tests/phpunit/helpers.php index 513671fc6..07028b61f 100644 --- a/tests/phpunit/helpers.php +++ b/tests/phpunit/helpers.php @@ -98,10 +98,11 @@ public static function change_state($val) { class Hm_IMAP_List extends Hm_Server_Wrapper { public static $state = false; public static function connect($id, $cache=false, $user=false, $pass=false, $save_credentials=false) { + global $user_config, $session; Hm_IMAP::$allow_connection = self::$state; Hm_IMAP::$allow_auth = self::$state; - self::$server_list[$id]['object'] = new Hm_IMAP(); - self::$server_list[$id]['object']->connect(); + self::$server_list[$id]['object'] = new Hm_Mailbox($id, $user_config, $session); + self::$server_list[$id]['object']->connect([]); self::$server_list[$id]['connected'] = true; return self::$server_list[$id]['object']; } diff --git a/tests/phpunit/modules/core/message_list_functions.php b/tests/phpunit/modules/core/message_list_functions.php index 9d8330ec3..4da49bec6 100644 --- a/tests/phpunit/modules/core/message_list_functions.php +++ b/tests/phpunit/modules/core/message_list_functions.php @@ -124,7 +124,7 @@ public function test_message_since_dropdown() { public function test_list_sources() { $mod = new Hm_Output_Test(array('foo' => 'bar', 'bar' => 'foo'), array('bar')); $this->assertEquals('
Sources
', list_sources(array(array('group' => 'background', 'type' => 'imap', 'folder' => 'foo')), $mod)); - $this->assertEquals('
Sources
imap blah foo
', list_sources(array(array('name' => 'blah', 'type' => 'imap', 'folder' => bin2hex('foo'))), $mod)); + $this->assertEquals('
Sources
imap blah foo
', list_sources(array(array('name' => 'blah', 'type' => 'imap', 'folder' => bin2hex('foo'), 'folder_name' => 'foo')), $mod)); $this->assertEquals('
Sources
imap blah INBOX
', list_sources(array(array('name' => 'blah', 'type' => 'imap')), $mod)); } /**