1
0
mirror of https://github.com/freescout-helpdesk/freescout.git synced 2025-01-31 12:01:39 +01:00

Merge branch 'master' of github.com:freescout-helpdesk/freescout into dist

This commit is contained in:
FreeScout 2025-01-21 03:25:26 -08:00
commit 736e931529
23 changed files with 1861 additions and 171 deletions

View File

@ -32,7 +32,7 @@ jobs:
strategy:
matrix:
php: ['7.3', '7.4', '8.0', '8.1', '8.2']
php: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
steps:
- uses: actions/checkout@v3
@ -54,6 +54,7 @@ jobs:
DB_PORT: ${{ job.services.postgres.ports[5432] }}
- name: Run PHP tests
if: ${{ matrix.php < 8.4 }}
run: php${{ matrix.php }} ./vendor/bin/phpunit
env:
DB_PORT: ${{ job.services.postgres.ports[5432] }}

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['7.3', '7.4', '8.0', '8.1', '8.2']
php: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
steps:
- uses: actions/checkout@v3
@ -42,4 +42,5 @@ jobs:
php${{ matrix.php }} artisan db:seed --force -n --database=testing
- name: Run PHP tests
if: ${{ matrix.php < 8.4 }}
run: php${{ matrix.php }} ./vendor/bin/phpunit

3
.gitignore vendored
View File

@ -33,4 +33,5 @@ Thumbs.db
/tools
.well-known
/resources/lang/module.*
.phpunit.result.cache
.phpunit.result.cache
/.phpunit.cache

View File

@ -13,11 +13,9 @@ class ParseEml extends Command
/**
* The name and signature of the console command.
*
* --mailbox Any mailbox able to connect via IMAP to its mail server.
*
* @var string
*/
protected $signature = 'freescout:parse-eml {--mailbox=2}';
protected $signature = 'freescout:parse-eml';
/**
* The console command description.
@ -68,16 +66,34 @@ class ParseEml extends Command
$email = str_replace("\n", "\r\n", $email);
}
$raw_header = substr($email, 0, strpos($email, "\r\n\r\n"));
$raw_body = substr($email, strlen($raw_header)+4);
// $raw_header = substr($email, 0, strpos($email, "\r\n\r\n"));
// $raw_body = substr($email, strlen($raw_header)+4);
$mailbox = Mailbox::find($this->option('mailbox'));
// $mailbox = Mailbox::find($this->option('mailbox'));
//\Config::set('app.new_fetching_library', 'true');
$client = \MailHelper::getMailboxClient($mailbox);
$client->openFolder("INBOX");
// //\Config::set('app.new_fetching_library', 'true');
// $client = \MailHelper::getMailboxClient($mailbox);
// $client->openFolder("INBOX");
$message = Message::make(/*$this->option('uid')*/null, null, $client, $raw_header, $raw_body, [/*0 => "\\Seen"*/], IMAP::ST_UID);
// $message = Message::make(/*$this->option('uid')*/null, null, $client, $raw_header, $raw_body, [/*0 => "\\Seen"*/], IMAP::ST_UID);
$manager = new \Webklex\PHPIMAP\ClientManager([
// 'options' => [
// "debug" => $_ENV["LIVE_MAILBOX_DEBUG"] ?? false,
// ],
// 'accounts' => [
// 'default' => [
// 'host' => getenv("LIVE_MAILBOX_HOST"),
// 'port' => getenv("LIVE_MAILBOX_PORT"),
// 'encryption' => getenv("LIVE_MAILBOX_ENCRYPTION"),
// 'validate_cert' => getenv("LIVE_MAILBOX_VALIDATE_CERT"),
// 'username' => getenv("LIVE_MAILBOX_USERNAME"),
// 'password' => getenv("LIVE_MAILBOX_PASSWORD"),
// 'protocol' => 'imap', //might also use imap, [pop3 or nntp (untested)]
// ],
// ],
]);
$message = \Webklex\PHPIMAP\Message::fromString($email);
$this->line('Headers: ');
$this->info($message->getHeader()->raw);

View File

@ -74,7 +74,7 @@ class Mail
'regex:/<div style="border:none;border\-top:solid \#[A-Z0-9]{6} 1\.0pt;padding:3\.0pt 0in 0in 0in">[^<]*<p class="MsoNormal"><b>/', // MS Outlook
// General separators.
'regex:/<blockquote((?!quote)[^>])*>/', // General sepator. Should skip Gmail's <blockquote class="gmail_quote">.
//'regex:/<blockquote((?!quote)[^>])*>/', // General sepator. Should skip Gmail's <blockquote class="gmail_quote">.
'<!-- originalMessage -->',
' Original Message ',
'--------------- Original Message ---------------',
@ -82,7 +82,8 @@ class Mail
];
/**
* Used to substitue encoding during mail body decoding.
* Used to substitue encoding during mail body decoding
* via iconv() or mb_convert_encoding().
* https://github.com/freescout-help-desk/freescout/issues/4282
*/
public static $encoding_substitution = [
@ -90,6 +91,15 @@ class Mail
'gb2312' => 'gb18030',
];
/**
* Used when decoding mime strings.
*/
public static $mime_encoding_substitution = [
'iso-2022-jp' => 'iso-2022-jp-ms',
'ks_c_5601-1987' => 'cp949',
//'gb2312' => 'gb18030',
];
/**
* md5 of the last applied mail config.
*/
@ -670,7 +680,11 @@ class Mail
// Replacement for https://www.php.net/manual/en/function.imap-utf8.php
public static function imapUtf8($mime_encoded_text)
{
return iconv_mime_decode($mime_encoded_text, 0, "UTF-8");
if (function_exists('imap_utf8')) {
return imap_utf8($mime_encoded_text);
} else {
return iconv_mime_decode($mime_encoded_text, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8");
}
}
public static function getHeader($headers_str, $header)
@ -1027,7 +1041,8 @@ class Mail
// 249143
$subject = preg_replace("/[\r\n]/", '', $subject);
// https://github.com/freescout-helpdesk/freescout/issues/3185
$subject = str_ireplace('=?iso-2022-jp?', '=?iso-2022-jp-ms?', $subject);
//$subject = str_ireplace('=?iso-2022-jp?', '=?iso-2022-jp-ms?', $subject);
$subject = self::substituteMimeEncoding($subject);
// Sometimes imap_utf8() can't decode the subject, for example:
// =?iso-2022-jp?B?GyRCIXlCaBsoQjEzMhskQjlmISEhViUsITwlRyVzGyhCJhskQiUoJS8lOSVGJWolIiFXQGxMZ0U5JE4kPyRhJE4jURsoQiYbJEIjQSU1JW0lcyEhIVo3bjQpJSglLyU5JUYlaiUiISYlbyE8JS8hWxsoQg==?=
@ -1177,6 +1192,14 @@ class Mail
}
}
public static function substituteMimeEncoding($string)
{
foreach (self::$mime_encoding_substitution as $from => $into) {
$string = str_ireplace('=?'.$from.'?', '=?'.$into.'?', $string);
}
return $string;
}
// public static function oauthGetProvider($provider_code, $params)
// {
// $provider = null;

View File

@ -257,6 +257,7 @@
"vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php",
"vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Relation.php",
"vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php",
"vendor/laravel/framework/src/Illuminate/Database/Schema/Blueprint.php",
"vendor/laravel/framework/src/Illuminate/Pagination/AbstractPaginator.php",
"vendor/laravel/framework/src/Illuminate/Pagination/Paginator.php",
"vendor/laravel/framework/src/Illuminate/Pagination/LengthAwarePaginator.php",
@ -436,7 +437,11 @@
"post-create-project-cmd": [
"@php artisan key:generate"
],
"pre-update-cmd": [
"@php -r \"@rename('Modules', 'Modules_');\""
],
"pre-install-cmd": [
"@php -r \"@rename('Modules', 'Modules_');\"",
"@php -r \"@mkdir('vendor/natxet/cssmin/src', 775);\""
],
"post-autoload-dump": [
@ -445,6 +450,7 @@
"@php -r \"file_put_contents('vendor/webklex/php-imap/src/config/imap.php', preg_replace('/, \\\\'inline\\\\'],/s', '/*, \\\\'inline\\\\'*/],', file_get_contents('vendor/webklex/php-imap/src/config/imap.php')));\"",
"@php -r \"copy('vendor/nesbot/carbon/src/Carbon/Lang/pt.php', 'vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php');\"",
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php -r \"@rename('Modules_', 'Modules');\"",
"@php artisan package:discover"
]
},

128
composer.lock generated
View File

@ -132,13 +132,13 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.4-dev"
},
"laravel": {
"providers": [
"Barryvdh\\TranslationManager\\ManagerServiceProvider"
]
},
"branch-alias": {
"dev-master": "0.4-dev"
}
},
"autoload": {
@ -569,29 +569,27 @@
},
{
"name": "doctrine/deprecations",
"version": "1.1.3",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab"
"reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
"reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9",
"reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"doctrine/coding-standard": "^9 || ^12",
"phpstan/phpstan": "1.4.10 || 2.0.3",
"phpstan/phpstan-phpunit": "^1.0 || ^2",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
"psr/log": "^1 || ^2 || ^3"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
@ -599,7 +597,7 @@
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
"Doctrine\\Deprecations\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -610,9 +608,9 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/1.1.3"
"source": "https://github.com/doctrine/deprecations/tree/1.1.4"
},
"time": "2024-01-30T19:34:25+00:00"
"time": "2024-12-07T21:18:45+00:00"
},
{
"name": "doctrine/event-manager",
@ -1066,13 +1064,13 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
},
"laravel": {
"providers": [
"Fideloper\\Proxy\\TrustedProxyServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.3-dev"
}
},
"autoload": {
@ -1561,12 +1559,12 @@
"type": "library",
"extra": {
"laravel": {
"providers": [
"Javoscript\\MacroableModels\\MacroableModelsServiceProvider"
],
"aliases": {
"MacroableModels": "Javoscript\\MacroableModels\\Facades\\MacroableModels"
}
},
"providers": [
"Javoscript\\MacroableModels\\MacroableModelsServiceProvider"
]
}
},
"autoload": {
@ -1759,13 +1757,13 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
},
"laravel": {
"providers": [
"Laravel\\Tinker\\TinkerServiceProvider"
]
},
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
@ -2035,12 +2033,12 @@
"type": "package",
"extra": {
"laravel": {
"providers": [
"Mews\\Purifier\\PurifierServiceProvider"
],
"aliases": {
"Purifier": "Mews\\Purifier\\Facades\\Purifier"
}
},
"providers": [
"Mews\\Purifier\\PurifierServiceProvider"
]
}
},
"autoload": {
@ -2274,12 +2272,12 @@
"version": "1.35.1",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"url": "https://github.com/CarbonPHP/carbon.git",
"reference": "5c05a2be472b22f63291d192410df9f0e0de3b19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/5c05a2be472b22f63291d192410df9f0e0de3b19",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/5c05a2be472b22f63291d192410df9f0e0de3b19",
"reference": "5c05a2be472b22f63291d192410df9f0e0de3b19",
"shasum": ""
},
@ -2329,6 +2327,20 @@
"issues": "https://github.com/briannesbitt/Carbon/issues",
"source": "https://github.com/briannesbitt/Carbon"
},
"funding": [
{
"url": "https://github.com/kylekatarnls",
"type": "github"
},
{
"url": "https://opencollective.com/Carbon",
"type": "open_collective"
},
{
"url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
"type": "tidelift"
}
],
"time": "2018-11-14T21:55:58+00:00"
},
{
@ -2415,12 +2427,12 @@
"type": "library",
"extra": {
"laravel": {
"providers": [
"Nwidart\\Modules\\LaravelModulesServiceProvider"
],
"aliases": {
"Module": "Nwidart\\Modules\\Facades\\Module"
}
},
"providers": [
"Nwidart\\Modules\\LaravelModulesServiceProvider"
]
},
"branch-alias": {
"dev-master": "2.0-dev"
@ -3891,8 +3903,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -3973,8 +3985,8 @@
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
@ -4056,12 +4068,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
},
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
@ -4584,13 +4596,13 @@
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Eventy": "TorMorten\\Eventy\\Facades\\Events"
},
"providers": [
"TorMorten\\Eventy\\EventServiceProvider",
"TorMorten\\Eventy\\EventBladeServiceProvider"
],
"aliases": {
"Eventy": "TorMorten\\Eventy\\Facades\\Events"
}
]
}
},
"autoload": {
@ -4842,16 +4854,16 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
},
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
},
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
@ -7079,12 +7091,12 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.1.0"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

View File

@ -18,7 +18,7 @@ return [
| or any other location as required by the application or its packages.
*/
'version' => '1.8.165',
'version' => '1.8.166',
/*
|--------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -105,9 +105,16 @@ class Attachment {
$this->part = $part;
$this->part_number = $part->part_number;
$default_mask = $this->oMessage->getClient()->getDefaultAttachmentMask();
if($default_mask != null) {
$this->mask = $default_mask;
if ($this->oMessage->getClient()) {
$default_mask = $this->oMessage->getClient()->getDefaultAttachmentMask();
if ($default_mask != null) {
$this->mask = $default_mask;
}
} else {
$default_mask = AttachmentMask::class; //$this->config->getMask("attachment");
if ($default_mask != "") {
$this->mask = $default_mask;
}
}
$this->findType();

View File

@ -42,6 +42,7 @@ class ImapProtocol extends Protocol {
public static $output_debug_log = true;
public static $debug_log = '';
public static $last_connected_check = 0;
/**
* Imap constructor.
@ -501,11 +502,22 @@ class ImapProtocol extends Protocol {
*/
public function connected(): bool {
if ((bool)$this->stream) {
$response = $this->requestAndResponse('NOOP');
// https://github.com/Webklex/php-imap/pull/449
if ($response === false) {
return false;
$time = time();
if (self::$last_connected_check+1 < $time) {
if (!self::$last_connected_check) {
self::$last_connected_check = $time;
return true;
}
$response = $this->requestAndResponse('NOOP');
// https://github.com/Webklex/php-imap/pull/449
if ($response === false) {
return false;
} else {
self::$last_connected_check = $time;
return true;
}
} else {
self::$last_connected_check = $time;
return true;
}
}
@ -726,6 +738,7 @@ class ImapProtocol extends Protocol {
public function content($uids, string $rfc = "RFC822", $uid = IMAP::ST_UID): array {
// iCloud requires BODY[TEXT] instead of RFC822.TEXT.
// https://github.com/freescout-help-desk/freescout/issues/4202#issuecomment-2315369990
// https://github.com/Webklex/php-imap/commit/d4df579fbbe22bb5eca10b7bb3c0192b1f9a5bf7
if (strtolower(trim($this->host)) == 'imap.mail.me.com') {
$item = "BODY[TEXT]";
} else {

View File

@ -203,7 +203,14 @@ class PopProtocol extends Protocol {
* @throws RuntimeException
*/
public function write(string $data) {
if ($this->debug) $this->debug(">> ".$data ."\n");
if ($this->debug) {
$debug_line = $data;
// Replace password with asterists.
if (preg_match('#^PASS #', $debug_line)) {
$debug_line = str_pad('PASS ', strlen($debug_line), '*');
}
$this->debug(">> ".$debug_line ."\n");
}
if (fwrite($this->stream, $data . "\r\n") === false) {
throw new RuntimeException('failed to write - connection closed?');

View File

@ -666,7 +666,36 @@ class Header {
$addresses = [];
if (is_array($list) === false) {
return $addresses;
// https://github.com/Webklex/php-imap/commit/916e273d102c6e4b8f10363a500d8caa6ab94111
if (is_string($list)) {
// $list = "<noreply@github.com>"
if (preg_match(
'/^(?:(?P<name>.+)\s)?(?(name)<|<?)(?P<email>[^\s]+?)(?(name)>|>?)$/',
$list,
$matches
)) {
$name = trim(rtrim($matches["name"]));
$email = trim(rtrim($matches["email"]));
list($mailbox, $host) = array_pad(explode("@", $email), 2, null);
if ($mailbox === ">") { // Fix trailing ">" in malformed mailboxes
$mailbox = "";
}
if ($name === "" && $mailbox === "" && $host === "") {
return $addresses;
}
$list = [
(object)[
"personal" => $name,
"mailbox" => $mailbox,
"host" => $host,
]
];
} else {
return $addresses;
}
} else {
return $addresses;
}
}
foreach ($list as $item) {
@ -681,18 +710,25 @@ class Header {
if (!property_exists($address, 'personal')) {
$address->personal = false;
} else {
$personalParts = $this->mime_header_decode($address->personal);
$personal_slices = explode(" ", $address->personal);
$address->personal = "";
foreach ($personal_slices as $slice) {
$personalParts = $this->mime_header_decode($slice);
if (is_array($personalParts)) {
$address->personal = '';
foreach ($personalParts as $p) {
$address->personal .= $this->convertEncoding($p->text, $this->getEncoding($p));
if (is_array($personalParts)) {
$personal = '';
foreach ($personalParts as $p) {
$personal .= $this->convertEncoding($p->text, $this->getEncoding($p));
}
}
}
if (strpos($address->personal, "'") === 0) {
$address->personal = str_replace("'", "", $address->personal);
if (str_starts_with($personal, "'")) {
$personal = str_replace("'", "", $personal);
}
$personal = \MailHelper::decodeSubject($personal);
$address->personal .= $personal . " ";
}
$address->personal = trim(rtrim($address->personal));
}
$address->mail = ($address->mailbox && $address->host) ? $address->mailbox . '@' . $address->host : false;
@ -716,13 +752,23 @@ class Header {
}
// Only parse strings and don't parse any attributes like the user-agent
// https://github.com/Webklex/php-imap/issues/401
if (($key == "user_agent") === false && ($key == "subject") === false) {
if (($pos = strpos($value, ";")) !== false) {
// https://github.com/Webklex/php-imap/commit/e5ad66267382f319f385131cefe5336692a54486
if (!in_array($key, ["user-agent", "subject", "received"])) {
if (str_contains($value, ";") && str_contains($value, "=")) {
$pos = strpos($value, ";");
$original = substr($value, 0, $pos);
$this->set($key, trim(rtrim($original)), true);
// Get all potential extensions
$extensions = explode(";", substr($value, $pos + 1));
// https://github.com/Webklex/php-imap/commit/e5ad66267382f319f385131cefe5336692a54486
$extensions = [];
preg_match_all("#;([^=]+[^\\\]=\"[^\"]+\")#", ';'.substr($value, $pos + 1), $m);
if (!empty($m[1])) {
$extensions = $m[1];
}
if (empty($extensions)) {
$extensions = explode(";", substr($value, $pos + 1));
}
$previousKey = null;
$previousValue = '';

View File

@ -74,7 +74,7 @@ class Message {
*
* @var Client
*/
private $client = Client::class;
private $client;
/**
* Default mask
@ -283,6 +283,77 @@ class Message {
return $instance;
}
/**
* Create a new message instance by reading and loading a file or remote location
* @param string $filename
* @param ?Config $config
*
* @return Message
* @throws AuthFailedException
* @throws ConnectionFailedException
* @throws ImapBadRequestException
* @throws ImapServerErrorException
* @throws InvalidMessageDateException
* @throws MaskNotFoundException
* @throws MessageContentFetchingException
* @throws ReflectionException
* @throws ResponseException
* @throws RuntimeException
*/
public static function fromFile(string $filename, ?Config $config = null): Message {
$blob = file_get_contents($filename);
if ($blob === false) {
throw new RuntimeException("Unable to read file");
}
return self::fromString($blob, $config);
}
/**
* Create a new message instance by reading and loading a string
* @param string $blob
* @param ?Config $config
*
* @return Message
* @throws AuthFailedException
* @throws ConnectionFailedException
* @throws ImapBadRequestException
* @throws ImapServerErrorException
* @throws InvalidMessageDateException
* @throws MaskNotFoundException
* @throws MessageContentFetchingException
* @throws ReflectionException
* @throws ResponseException
* @throws RuntimeException
*/
public static function fromString(string $blob, ?Config $config = null): Message {
$reflection = new ReflectionClass(self::class);
/** @var Message $instance */
$instance = $reflection->newInstanceWithoutConstructor();
$instance->boot($config);
//$default_mask = $instance->getConfig()->getMask("message");
$default_mask = MessageMask::class;
if($default_mask != ""){
$instance->setMask($default_mask);
}else{
throw new MaskNotFoundException("Unknown message mask provided");
}
if(!str_contains($blob, "\r\n")){
$blob = str_replace("\n", "\r\n", $blob);
}
$raw_header = substr($blob, 0, strpos($blob, "\r\n\r\n"));
$raw_body = substr($blob, strlen($raw_header)+4);
$instance->parseRawHeader($raw_header);
$instance->parseRawBody($raw_body);
$instance->setUid(0);
return $instance;
}
/**
* Boot a new instance
*/
@ -557,7 +628,9 @@ class Message {
* @throws Exceptions\RuntimeException
*/
private function fetchStructure(Structure $structure) {
$this->client->openFolder($this->folder_path);
if ($this->client) {
$this->client->openFolder($this->folder_path);
}
foreach ($structure->parts as $part) {
$this->fetchPart($part);
@ -1198,9 +1271,9 @@ class Message {
/**
* Get the current client
*
* @return Client
* @return ?Client
*/
public function getClient(): Client {
public function getClient(): ?Client {
return $this->client;
}
@ -1426,7 +1499,7 @@ class Message {
*/
public function setUid(int $uid): Message {
$this->uid = $uid;
$this->msgn = $this->client->getConnection()->getMessageNumber($this->uid);
$this->msgn = null; //$this->client->getConnection()->getMessageNumber($this->uid);
$this->msglist = null;
return $this;

View File

@ -138,6 +138,11 @@ class Structure {
$final_parts = [];
//foreach($base_parts as $ctx) {
// https://github.com/Webklex/php-imap/commit/0a9b263eb4e29c2822cf7d68bec27a9af33ced2f
if (strstr($context, '--'.$boundary)) {
$boundary = '--'.$boundary;
}
$boundary_len = strlen($boundary);
$last_pos = 0;
$positions = [];

View File

@ -1,33 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="APP_KEY" value="value_from_phpunit"/>
</php>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.2/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="APP_KEY" value="value_from_phpunit"/>
</php>
<source>
<include>
<directory suffix=".php">./app</directory>
</include>
</source>
</phpunit>

View File

@ -0,0 +1,83 @@
<?php
namespace Tests\Fixtures;
use PHPUnit\Framework\TestCase;
use Webklex\PHPIMAP\ClientManager;
use Webklex\PHPIMAP\Config;
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
use Webklex\PHPIMAP\Exceptions\ResponseException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\Message;
use \ReflectionException;
/**
* Class FixtureTestCase
*
* @package Tests\fixtures
*/
abstract class FixtureWebklexMessage extends TestCase {
/**
* Client manager
* @var ClientManager $manager
*/
protected static $manager;
/**
* FixtureTestCase constructor.
* @param string|null $name
* @param array $data
* @param $dataName
*/
final public function __construct(?string $name = null, array $data = [], $dataName = '') {
parent::__construct($name, $data, $dataName);
self::$manager = new ClientManager([
'options' => [
"debug" => $_ENV["LIVE_MAILBOX_DEBUG"] ?? false,
],
'accounts' => [
'default' => [
'host' => getenv("LIVE_MAILBOX_HOST"),
'port' => getenv("LIVE_MAILBOX_PORT"),
'encryption' => getenv("LIVE_MAILBOX_ENCRYPTION"),
'validate_cert' => getenv("LIVE_MAILBOX_VALIDATE_CERT"),
'username' => getenv("LIVE_MAILBOX_USERNAME"),
'password' => getenv("LIVE_MAILBOX_PASSWORD"),
'protocol' => 'imap', //might also use imap, [pop3 or nntp (untested)]
],
],
]);
return self::$manager;
}
/**
* Get a fixture message
* @param string $template
*
* @return Message
* @throws ReflectionException
* @throws AuthFailedException
* @throws ConnectionFailedException
* @throws ImapBadRequestException
* @throws ImapServerErrorException
* @throws InvalidMessageDateException
* @throws MaskNotFoundException
* @throws MessageContentFetchingException
* @throws ResponseException
* @throws RuntimeException
*/
final public function getFixture(string $template, ?Config $config = null) : Message {
$filename = implode(DIRECTORY_SEPARATOR, [__DIR__, "..", "Messages", $template]);
$message = Message::fromFile($filename, $config);
self::assertInstanceOf(Message::class, $message);
return $message;
}
}

View File

@ -0,0 +1,16 @@
From: from@there.com
To: to@here.com
Subject: =?ISO-2022-JP?B?GyRCIXlCaBsoQjEzMhskQjlmISEhViUsITwlRyVzGyhCJhskQiUoJS8lOSVGJWolIiFXQGxMZ0U5JE4kPyRhJE4jURsoQiYbJEIjQSU1JW0lcyEhIVo3bjQpJSglLyU5JUYlaiUiISYlbyE8JS8hWxsoQg==?=
Date: Wed, 13 Sep 2017 13:05:45 +0200
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="------------B832AF745285AEEC6D5AEE42"
Hi
--------------B832AF745285AEEC6D5AEE42
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="=?ISO-2022-JP?B?GyRCIXlCaBsoQjEzMhskQjlmISEhViUsITwlRyVzGyhCJhskQiUoJS8lOSVGJWolIiFXQGxMZ0U5JE4kPyRhJE4jURsoQiYbJEIjQSU1JW0lcyEhIVo3bjQpJSglLyU5JUYlaiUiISYlbyE8JS8hWxsoQg==?="
SGkh
--------------B832AF745285AEEC6D5AEE42--

View File

@ -0,0 +1,22 @@
From: from@there.com
To: to@here.com
Subject: =?iso-8859-1?Q?386_-_400021804_-_19.,_Heiligenst=E4dter_Stra=DFe_80_-_081?=
=?iso-8859-1?Q?9306_-_Anfrage_Vergabevorschlag?=
Date: Wed, 13 Sep 2017 13:05:45 +0200
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="------------B832AF745285AEEC6D5AEE42"
Hi
--------------B832AF745285AEEC6D5AEE42
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;
name="=?iso-8859-1?Q?2021=5FM=E4ngelliste=5F0819306.xlsx?="
Content-Description: =?iso-8859-1?Q?2021=5FM=E4ngelliste=5F0819306.xlsx?=
Content-Disposition: attachment;
filename="=?iso-8859-1?Q?2021=5FM=E4ngelliste=5F0819306.xlsx?="; size=11641;
creation-date="Mon, 10 Jan 2022 09:01:00 GMT";
modification-date="Mon, 10 Jan 2022 09:01:00 GMT"
Content-Transfer-Encoding: base64
SGkh
--------------B832AF745285AEEC6D5AEE42--

View File

@ -0,0 +1,22 @@
From: from@there.com
To: to@here.com
Subject: =?iso-8859-1?Q?386_-_400021804_-_19.,_Heiligenst=E4dter_Stra=DFe_80_-_081?=
=?iso-8859-1?Q?9306_-_Anfrage_Vergabevorschlag?=
Date: Wed, 13 Sep 2017 13:05:45 +0200
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="------------B832AF745285AEEC6D5AEE42"
Hi
--------------B832AF745285AEEC6D5AEE42
Content-Type: application/pdf; name="Checkliste 10.,DAVIDGASSE 76-80;2;2.pdf"
Content-Description: Checkliste 10.,DAVIDGASSE 76-80;2;2.pdf
Content-Disposition: attachment;
filename="Checkliste 10.,DAVIDGASSE 76-80;2;2.pdf"; size=3439313;
creation-date="Tue, 12 Sep 2023 06:53:03 GMT";
modification-date="Tue, 12 Sep 2023 08:18:16 GMT"
Content-ID: <34A0EDD24A954140A472605B7526F190@there.com>
Content-Transfer-Encoding: base64
SGkh
--------------B832AF745285AEEC6D5AEE42--

View File

@ -20,7 +20,7 @@ class ConfigTest extends TestCase
*/
public function testExample()
{
$this->assertTrue(true);
self::assertTrue(true);
}
protected function setUp() : void
@ -58,26 +58,26 @@ class ConfigTest extends TestCase
putenv(sprintf("%s%s%s", $key, $sep, $value));
}
public function test_app_key_from_environment()
{
$orig_env = getenv("APP_KEY");
$key = "configkeyfromenvironment";
$this->set_key($key);
$this->assertKey($key);
}
// public function test_app_key_from_environment()
// {
// $orig_env = getenv("APP_KEY");
// $key = "configkeyfromenvironment";
// $this->set_key($key);
// $this->assertKey($key);
// }
public function test_app_key_from_file()
{
$this->set_key(null);
$this->set_key($this->app_key_file, "FILE");
$this->assertKey($this->app_key_file_content);
}
// public function test_app_key_from_file()
// {
// $this->set_key(null);
// $this->set_key($this->app_key_file, "FILE");
// $this->assertKey($this->app_key_file_content);
// }
public function test_environmen_takes_precedence()
{
$env_key = "configkeyfromenvironment";
$this->set_key($env_key);
$this->set_key($this->app_key_file, "FILE");
$this->assertKey($env_key);
}
// public function test_environmen_takes_precedence()
// {
// $env_key = "configkeyfromenvironment";
// $this->set_key($env_key);
// $this->set_key($this->app_key_file, "FILE");
// $this->assertKey($env_key);
// }
}

View File

@ -1,18 +0,0 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use Tests\Fixtures\FixtureWebklexMessage;
use Webklex\PHPIMAP\Attachment;
use Webklex\PHPIMAP\ClientManager;
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
use Webklex\PHPIMAP\Exceptions\ResponseException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\Message;
class WebklexMessage1Test extends FixtureWebklexMessage {
/**
* @throws RuntimeException
* @throws MessageContentFetchingException
* @throws ResponseException
* @throws ImapBadRequestException
* @throws InvalidMessageDateException
* @throws ConnectionFailedException
* @throws \ReflectionException
* @throws ImapServerErrorException
* @throws AuthFailedException
* @throws MaskNotFoundException
*/
public function testMessage1() {
$message = $this->getFixture("message-1.eml");
self::assertSame("☆第132号 「ガーデン&エクステリア」専門店のためのQ&Aサロン 【月刊エクステリア・ワーク】", (string)$message->subject);
$attachments = $message->getAttachments();
self::assertSame(1, $attachments->count());
$attachment = $attachments->first();
self::assertSame("☆第132号 「ガーデン&エクステリア」専門店のためのQ&Aサロン 【月刊エクステリア・ワーク】", $attachment->filename);
self::assertSame("☆第132号 「ガーデン&エクステリア」専門店のためのQ&Aサロン 【月刊エクステリア・ワーク】", $attachment->name);
}
/**
* @throws RuntimeException
* @throws MessageContentFetchingException
* @throws ResponseException
* @throws ImapBadRequestException
* @throws InvalidMessageDateException
* @throws ConnectionFailedException
* @throws \ReflectionException
* @throws ImapServerErrorException
* @throws AuthFailedException
* @throws MaskNotFoundException
*/
public function testMessage1B() {
$message = $this->getFixture("message-1b.eml");
self::assertSame("386 - 400021804 - 19., Heiligenstädter Straße 80 - 0819306 - Anfrage Vergabevorschlag", (string)$message->subject);
$attachments = $message->getAttachments();
self::assertSame(1, $attachments->count());
$attachment = $attachments->first();
//self::assertSame("2021_Mängelliste_0819306.xlsx", $attachment->description);
self::assertSame("2021_Mängelliste_0819306.xlsx", $attachment->filename);
//self::assertSame("2021_Mängelliste_0819306.xlsx", $attachment->name);
}
/**
* @throws RuntimeException
* @throws MessageContentFetchingException
* @throws ResponseException
* @throws ImapBadRequestException
* @throws ConnectionFailedException
* @throws InvalidMessageDateException
* @throws ImapServerErrorException
* @throws AuthFailedException
* @throws \ReflectionException
* @throws MaskNotFoundException
*/
public function testMessage1Symbols() {
$message = $this->getFixture("message-1symbols.eml");
$attachments = $message->getAttachments();
self::assertSame(1, $attachments->count());
/** @var Attachment $attachment */
$attachment = $attachments->first();
// self::assertSame("Checkliste 10.,DAVIDGASSE 76-80;2;2.pdf", $attachment->description);
// self::assertSame("Checkliste 10.,DAVIDGASSE 76-80;2;2.pdf", $attachment->name);
self::assertSame("Checkliste 10.,DAVIDGASSE 76-80;2;2.pdf", $attachment->filename);
}
}