Reintroduce Webklex IMAP for ticket processing as PHP-IMAP is no longer being developed. This is optional for now and considered beta can be found in cron/ticket_email_parser.php

This commit is contained in:
johnnyq
2025-09-10 14:27:46 -04:00
parent 981fb9585d
commit ce7d84aa2f
2035 changed files with 174115 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<?php
/*
* File: AttachmentDecoder.php
* Category: -
* Author: M.Goldenbaum
* Created: 12.04.24 20:14
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Decoder;
use Exception;
use Webklex\PHPIMAP\EncodingAliases;
use Webklex\PHPIMAP\IMAP;
/**
* Class AttachmentDecoder
*
* @package Webklex\PHPIMAP
*/
class AttachmentDecoder extends MessageDecoder {
}

View File

@@ -0,0 +1,161 @@
<?php
/*
* File: Decoder.php
* Category: -
* Author: M.Goldenbaum
* Created: 12.04.24 20:14
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Decoder;
use Webklex\PHPIMAP\EncodingAliases;
/**
* Class Decoder
*
* @package Webklex\PHPIMAP
*/
abstract class Decoder implements DecoderInterface {
/**
* Decoder constructor.
* @param array $options Decoder options
* @param string $fallback_encoding Fallback encoding
*/
public function __construct(
/**
* Options
*
* @var array
*/
protected array $options = [],
/**
* Fallback Encoding
*
* @var string
*/
protected string $fallback_encoding = 'UTF-8',
) {
$this->options = array_merge([
'header' => 'utf-8',
'message' => 'utf-8',
'attachment' => 'utf-8',
], $this->options);
}
/**
* Decode a given value
* @param array|string|null $value
* @param string|null $encoding
* @return mixed
*/
public function decode(array|string|null $value, ?string $encoding = null): mixed {
return $value;
}
/**
* Convert the encoding
* @param string $str The string to convert
* @param string $from The source encoding
* @param string $to The target encoding
*
* @return mixed|string
*/
public function convertEncoding(string $str, string $from = "ISO-8859-2", string $to = "UTF-8"): mixed {
$from = EncodingAliases::get($from, $this->fallback_encoding);
$to = EncodingAliases::get($to, $this->fallback_encoding);
if ($from === $to) {
return $str;
}
return EncodingAliases::convert($str, $from, $to);
}
/**
* Decode MIME header elements
* @link https://php.net/manual/en/function.imap-mime-header-decode.php
* @param string $text The MIME text
*
* @return array<int,object> Returns an array of objects. Each *object has two properties, charset and text.
*/
public function mimeHeaderDecode(string $text): array {
if (extension_loaded('imap')) {
$result = \imap_mime_header_decode($text);
return is_array($result) ? $result : [];
}
$charset = $this->getEncoding($text);
return [(object)[
"charset" => $charset,
"text" => $this->convertEncoding($text, $charset)
]];
}
/**
* Test if a given value is utf-8 encoded
* @param $value
*
* @return bool
*/
public static function isUTF8($value): bool {
return str_starts_with(strtolower($value), '=?utf-8?');
}
/**
* Check if a given pair of strings has been decoded
* @param $encoded
* @param $decoded
*
* @return bool
*/
public static function notDecoded($encoded, $decoded): bool {
return str_starts_with($decoded, '=?')
&& strlen($decoded) - 2 === strpos($decoded, '?=')
&& str_contains($encoded, $decoded);
}
/**
* Set the configuration used for decoding
* @param array $config
*
* @return Decoder
*/
public function setOptions(array $config): static {
$this->options = $config;
return $this;
}
/**
* Get the configuration used for decoding
*
* @return array
*/
public function getOptions(): array {
return $this->options;
}
/**
* Get the fallback encoding
*
* @return string
*/
public function getFallbackEncoding(): string {
return $this->fallback_encoding;
}
/**
* Set the fallback encoding
*
* @param string $fallback_encoding
* @return Decoder
*/
public function setFallbackEncoding(string $fallback_encoding): static {
$this->fallback_encoding = $fallback_encoding;
return $this;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* File: DecoderInterface.php
* Category: -
* Author: M.Goldenbaum
* Created: 12.04.24 20:25
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Decoder;
/**
* Interface DecoderInterface
*
* @package Webklex\PHPIMAP\Decoder
*/
interface DecoderInterface {
/**
* DecoderInterface constructor.
* @param array $options
* @param string $fallback_encoding
*/
public function __construct(array $options = [], string $fallback_encoding = 'UTF-8');
/**
* Decode a given value
*
* @param array|string|null $value
* @param string|null $encoding
* @return string|array|null
*/
public function decode(array|string|null $value, ?string $encoding = null): mixed;
public function mimeHeaderDecode(string $text): array;
public function convertEncoding(string $str, string $from = "ISO-8859-2", string $to = "UTF-8"): mixed;
public function getEncoding(object|string $structure): string;
public function getOptions(): array;
public function setOptions(array $config): static;
public function getFallbackEncoding(): string;
public function setFallbackEncoding(string $fallback_encoding): static;
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* File: Decoder.php
* Category: -
* Author: M.Goldenbaum
* Created: 12.04.24 20:14
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Decoder;
use Webklex\PHPIMAP\EncodingAliases;
/**
* Class HeaderDecoder
*
* @package Webklex\PHPIMAP
*/
class HeaderDecoder extends Decoder {
public function decode(array|string|null $value, ?string $encoding = null): mixed {
if (is_array($value)) {
return $this->decodeHeaderArray($value);
}
$original_value = $value;
$decoder = $this->options['header'];
if ($value !== null) {
if ($decoder === 'utf-8') {
$decoded_values = $this->mimeHeaderDecode($value);
$tempValue = "";
foreach ($decoded_values as $decoded_value) {
$tempValue .= $this->convertEncoding($decoded_value->text, $decoded_value->charset);
}
if ($tempValue) {
$value = $tempValue;
} else if (extension_loaded('imap')) {
$value = \imap_utf8($value);
} else if (function_exists('iconv_mime_decode')) {
$value = iconv_mime_decode($value, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8");
} else {
$value = mb_decode_mimeheader($value);
}
} elseif ($decoder === 'iconv') {
$value = iconv_mime_decode($value, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8");
} else if (self::isUTF8($value)) {
$value = mb_decode_mimeheader($value);
}
if (self::notDecoded($original_value, $value)) {
$value = $this->convertEncoding($original_value, $this->getEncoding($original_value));
}
}
return $value;
}
/**
* Get the encoding of a given abject
* @param object|string $structure
*
* @return string
*/
public function getEncoding(object|string $structure): string {
if (property_exists($structure, 'parameters')) {
foreach ($structure->parameters as $parameter) {
if (strtolower($parameter->attribute) == "charset") {
return EncodingAliases::get($parameter->value == "default" ? EncodingAliases::detectEncoding($parameter->value) : $parameter->value, $this->fallback_encoding);
}
}
} elseif (property_exists($structure, 'charset')) {
return EncodingAliases::get($structure->charset == "default" ? EncodingAliases::detectEncoding($structure->charset) : $structure->charset, $this->fallback_encoding);
} elseif (is_string($structure) === true) {
$result = mb_detect_encoding($structure);
return $result === false ? $this->fallback_encoding : $result;
}
return $this->fallback_encoding;
}
/**
* Decode a given array
* @param array $values
*
* @return array
*/
private function decodeHeaderArray(array $values): array {
foreach ($values as $key => $value) {
$values[$key] = $this->decode($value);
}
return $values;
}
}

View File

@@ -0,0 +1,119 @@
<?php
/*
* File: MessageDecoder.php
* Category: -
* Author: M.Goldenbaum
* Created: 12.04.24 20:14
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Decoder;
use Exception;
use Webklex\PHPIMAP\EncodingAliases;
use Webklex\PHPIMAP\IMAP;
/**
* Class MessageDecoder
*
* @package Webklex\PHPIMAP
*/
class MessageDecoder extends Decoder {
public function decode(array|string|null $value, ?string $encoding = null): mixed {
if(is_array($value)) {
return array_map(function($item){
return $this->decode($item);
}, $value);
}
switch ($encoding) {
case IMAP::MESSAGE_ENC_BINARY:
if (extension_loaded('imap')) {
return base64_decode(\imap_binary($value));
}
return base64_decode($value);
case IMAP::MESSAGE_ENC_BASE64:
return base64_decode($value);
case IMAP::MESSAGE_ENC_QUOTED_PRINTABLE:
return quoted_printable_decode($value);
case IMAP::MESSAGE_ENC_8BIT:
case IMAP::MESSAGE_ENC_7BIT:
case IMAP::MESSAGE_ENC_OTHER:
default:
return $value;
}
}
/**
* Get the encoding of a given abject
* @param object|string $structure
*
* @return string
*/
public function getEncoding(object|string $structure): string {
if (property_exists($structure, 'parameters')) {
foreach ($structure->parameters as $parameter) {
if (strtolower($parameter->attribute) == "charset") {
return EncodingAliases::get($parameter->value, "ISO-8859-2");
}
}
} elseif (property_exists($structure, 'charset')) {
return EncodingAliases::get($structure->charset, "ISO-8859-2");
} elseif (is_string($structure) === true) {
return EncodingAliases::detectEncoding($structure);
}
return $this->fallback_encoding;
}
/**
* Convert the encoding
* @param $str
* @param string $from
* @param string $to
*
* @return mixed|string
*/
public function convertEncoding($str, string $from = "ISO-8859-2", string $to = "UTF-8"): mixed {
$from = EncodingAliases::get($from);
$to = EncodingAliases::get($to);
if ($from === $to) {
return $str;
}
// We don't need to do convertEncoding() if charset is ASCII (us-ascii):
// ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
// https://stackoverflow.com/a/11303410
//
// us-ascii is the same as ASCII:
// ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
// prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
// based on the typographical symbols predominantly in use there.
// https://en.wikipedia.org/wiki/ASCII
//
// convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
if (strtolower($from ?? '') == 'us-ascii' && $to == 'UTF-8') {
return $str;
}
if (function_exists('iconv') && !EncodingAliases::isUtf7($from) && !EncodingAliases::isUtf7($to)) {
try {
return iconv($from, $to.'//IGNORE', $str);
} catch (Exception) {
return @iconv($from, $to, $str);
}
} else {
if (!$from) {
return mb_convert_encoding($str, $to);
}
return mb_convert_encoding($str, $to, $from);
}
}
}