Merge branch 'MDL-80207-main' of https://github.com/HuongNV13/moodle

This commit is contained in:
Jun Pataleta
2024-02-20 11:06:24 +08:00
440 changed files with 9835 additions and 58143 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,56 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace tool_messageinbound;
/**
* The Mail Pickup Utils.
*
* @package tool_messageinbound
* @copyright 2023 Huong Nguyen <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class utils {
/** @var int Encoding type: 7 bit SMTP semantic data. */
const ENC7BIT = 0;
/** @var int Encoding type: 8 bit SMTP semantic data. */
const ENC8BIT = 1;
/** @var int Encoding type: 8 bit binary data. */
const ENCBINARY = 2;
/** @var int Encoding type: BASE64 encoded data. */
const ENCBASE64 = 3;
/** @var int Encoding type: Human-readable 8-as-7 bit data. */
const ENCQUOTEDPRINTABLE = 4;
/** @var int Encoding type: Unknown. */
const ENCOTHER = 5;
/**
* Get body content encoding.
*
* @return string[] List of body content encoding.
*/
public static function get_body_encoding(): array {
return [
self::ENC7BIT => '7BIT',
self::ENC8BIT => '8BIT',
self::ENCBINARY => 'BINARY',
self::ENCBASE64 => 'BASE64',
self::ENCQUOTEDPRINTABLE => 'QUOTED-PRINTABLE',
self::ENCOTHER => 'X-UNKNOWN',
];
}
}
@@ -0,0 +1,615 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| Copyright (C) 2000 Edmund Grimley Evans <[email protected]> |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Provide charset conversion functionality |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <[email protected]> |
| Author: Aleksander Machniak <[email protected]> |
| Author: Edmund Grimley Evans <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Character sets conversion functionality
*
* @package Framework
* @subpackage Core
*/
class rcube_charset
{
/**
* Character set aliases (some of them from HTML5 spec.)
*
* @var array
*/
static public $aliases = [
'USASCII' => 'WINDOWS-1252',
'ANSIX31101983' => 'WINDOWS-1252',
'ANSIX341968' => 'WINDOWS-1252',
'UNKNOWN8BIT' => 'ISO-8859-15',
'UNKNOWN' => 'ISO-8859-15',
'USERDEFINED' => 'ISO-8859-15',
'KSC56011987' => 'EUC-KR',
'GB2312' => 'GBK',
'GB231280' => 'GBK',
'UNICODE' => 'UTF-8',
'UTF7IMAP' => 'UTF7-IMAP',
'TIS620' => 'WINDOWS-874',
'ISO88599' => 'WINDOWS-1254',
'ISO885911' => 'WINDOWS-874',
'MACROMAN' => 'MACINTOSH',
'77' => 'MAC',
'128' => 'SHIFT-JIS',
'129' => 'CP949',
'130' => 'CP1361',
'134' => 'GBK',
'136' => 'BIG5',
'161' => 'WINDOWS-1253',
'162' => 'WINDOWS-1254',
'163' => 'WINDOWS-1258',
'177' => 'WINDOWS-1255',
'178' => 'WINDOWS-1256',
'186' => 'WINDOWS-1257',
'204' => 'WINDOWS-1251',
'222' => 'WINDOWS-874',
'238' => 'WINDOWS-1250',
'MS950' => 'CP950',
'WINDOWS31J' => 'CP932',
'WINDOWS949' => 'UHC',
'WINDOWS1257' => 'ISO-8859-13',
'ISO2022JP' => 'ISO-2022-JP-MS',
];
/**
* Windows codepages
*
* @var array
*/
static public $windows_codepages = [
37 => 'IBM037', // IBM EBCDIC US-Canada
437 => 'IBM437', // OEM United States
500 => 'IBM500', // IBM EBCDIC International
708 => 'ASMO-708', // Arabic (ASMO 708)
720 => 'DOS-720', // Arabic (Transparent ASMO); Arabic (DOS)
737 => 'IBM737', // OEM Greek (formerly 437G); Greek (DOS)
775 => 'IBM775', // OEM Baltic; Baltic (DOS)
850 => 'IBM850', // OEM Multilingual Latin 1; Western European (DOS)
852 => 'IBM852', // OEM Latin 2; Central European (DOS)
855 => 'IBM855', // OEM Cyrillic (primarily Russian)
857 => 'IBM857', // OEM Turkish; Turkish (DOS)
858 => 'IBM00858', // OEM Multilingual Latin 1 + Euro symbol
860 => 'IBM860', // OEM Portuguese; Portuguese (DOS)
861 => 'IBM861', // OEM Icelandic; Icelandic (DOS)
862 => 'DOS-862', // OEM Hebrew; Hebrew (DOS)
863 => 'IBM863', // OEM French Canadian; French Canadian (DOS)
864 => 'IBM864', // OEM Arabic; Arabic (864)
865 => 'IBM865', // OEM Nordic; Nordic (DOS)
866 => 'cp866', // OEM Russian; Cyrillic (DOS)
869 => 'IBM869', // OEM Modern Greek; Greek, Modern (DOS)
870 => 'IBM870', // IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
874 => 'windows-874', // ANSI/OEM Thai (ISO 8859-11); Thai (Windows)
875 => 'cp875', // IBM EBCDIC Greek Modern
932 => 'shift_jis', // ANSI/OEM Japanese; Japanese (Shift-JIS)
936 => 'gb2312', // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
950 => 'big5', // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
1026 => 'IBM1026', // IBM EBCDIC Turkish (Latin 5)
1047 => 'IBM01047', // IBM EBCDIC Latin 1/Open System
1140 => 'IBM01140', // IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
1141 => 'IBM01141', // IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
1142 => 'IBM01142', // IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
1143 => 'IBM01143', // IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
1144 => 'IBM01144', // IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
1145 => 'IBM01145', // IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
1146 => 'IBM01146', // IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
1147 => 'IBM01147', // IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
1148 => 'IBM01148', // IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
1149 => 'IBM01149', // IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
1200 => 'UTF-16', // Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
1201 => 'UTF-16BE', // Unicode UTF-16, big endian byte order; available only to managed applications
1250 => 'windows-1250', // ANSI Central European; Central European (Windows)
1251 => 'windows-1251', // ANSI Cyrillic; Cyrillic (Windows)
1252 => 'windows-1252', // ANSI Latin 1; Western European (Windows)
1253 => 'windows-1253', // ANSI Greek; Greek (Windows)
1254 => 'windows-1254', // ANSI Turkish; Turkish (Windows)
1255 => 'windows-1255', // ANSI Hebrew; Hebrew (Windows)
1256 => 'windows-1256', // ANSI Arabic; Arabic (Windows)
1257 => 'windows-1257', // ANSI Baltic; Baltic (Windows)
1258 => 'windows-1258', // ANSI/OEM Vietnamese; Vietnamese (Windows)
10000 => 'macintosh', // MAC Roman; Western European (Mac)
12000 => 'UTF-32', // Unicode UTF-32, little endian byte order; available only to managed applications
12001 => 'UTF-32BE', // Unicode UTF-32, big endian byte order; available only to managed applications
20127 => 'US-ASCII', // US-ASCII (7-bit)
20273 => 'IBM273', // IBM EBCDIC Germany
20277 => 'IBM277', // IBM EBCDIC Denmark-Norway
20278 => 'IBM278', // IBM EBCDIC Finland-Sweden
20280 => 'IBM280', // IBM EBCDIC Italy
20284 => 'IBM284', // IBM EBCDIC Latin America-Spain
20285 => 'IBM285', // IBM EBCDIC United Kingdom
20290 => 'IBM290', // IBM EBCDIC Japanese Katakana Extended
20297 => 'IBM297', // IBM EBCDIC France
20420 => 'IBM420', // IBM EBCDIC Arabic
20423 => 'IBM423', // IBM EBCDIC Greek
20424 => 'IBM424', // IBM EBCDIC Hebrew
20838 => 'IBM-Thai', // IBM EBCDIC Thai
20866 => 'koi8-r', // Russian (KOI8-R); Cyrillic (KOI8-R)
20871 => 'IBM871', // IBM EBCDIC Icelandic
20880 => 'IBM880', // IBM EBCDIC Cyrillic Russian
20905 => 'IBM905', // IBM EBCDIC Turkish
20924 => 'IBM00924', // IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
20932 => 'EUC-JP', // Japanese (JIS 0208-1990 and 0212-1990)
20936 => 'cp20936', // Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
20949 => 'cp20949', // Korean Wansung
21025 => 'cp1025', // IBM EBCDIC Cyrillic Serbian-Bulgarian
21866 => 'koi8-u', // Ukrainian (KOI8-U); Cyrillic (KOI8-U)
28591 => 'iso-8859-1', // ISO 8859-1 Latin 1; Western European (ISO)
28592 => 'iso-8859-2', // ISO 8859-2 Central European; Central European (ISO)
28593 => 'iso-8859-3', // ISO 8859-3 Latin 3
28594 => 'iso-8859-4', // ISO 8859-4 Baltic
28595 => 'iso-8859-5', // ISO 8859-5 Cyrillic
28596 => 'iso-8859-6', // ISO 8859-6 Arabic
28597 => 'iso-8859-7', // ISO 8859-7 Greek
28598 => 'iso-8859-8', // ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
28599 => 'iso-8859-9', // ISO 8859-9 Turkish
28603 => 'iso-8859-13', // ISO 8859-13 Estonian
28605 => 'iso-8859-15', // ISO 8859-15 Latin 9
38598 => 'iso-8859-8-i', // ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
50220 => 'iso-2022-jp', // ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
50221 => 'csISO2022JP', // ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
50222 => 'iso-2022-jp', // ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
50225 => 'iso-2022-kr', // ISO 2022 Korean
51932 => 'EUC-JP', // EUC Japanese
51936 => 'EUC-CN', // EUC Simplified Chinese; Chinese Simplified (EUC)
51949 => 'EUC-KR', // EUC Korean
52936 => 'hz-gb-2312', // HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
54936 => 'GB18030', // Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
65000 => 'UTF-7',
65001 => 'UTF-8',
];
/**
* Validate character set identifier.
*
* @param string $input Character set identifier
*
* @return bool True if valid, False if not valid
*/
public static function is_valid($input)
{
return is_string($input) && preg_match('|^[a-zA-Z0-9_./:#-]{2,32}$|', $input) > 0;
}
/**
* Parse and validate charset name string.
* Sometimes charset string is malformed, there are also charset aliases,
* but we need strict names for charset conversion (specially utf8 class)
*
* @param string $input Input charset name
*
* @return string The validated charset name
*/
public static function parse_charset($input)
{
static $charsets = [];
$charset = strtoupper((string) $input);
if (isset($charsets[$input])) {
return $charsets[$input];
}
$charset = preg_replace([
'/^[^0-9A-Z]+/', // e.g. _ISO-8859-JP$SIO
'/\$.*$/', // e.g. _ISO-8859-JP$SIO
'/UNICODE-1-1-*/', // RFC1641/1642
'/^X-/', // X- prefix (e.g. X-ROMAN8 => ROMAN8)
'/\*.*$/' // lang code according to RFC 2231.5
], '', $charset);
if ($charset == 'BINARY') {
return $charsets[$input] = null;
}
// allow A-Z and 0-9 only
$str = preg_replace('/[^A-Z0-9]/', '', $charset);
$result = $charset;
if (isset(self::$aliases[$str])) {
$result = self::$aliases[$str];
}
// UTF
else if (preg_match('/U[A-Z][A-Z](7|8|16|32)(BE|LE)*/', $str, $m)) {
$result = 'UTF-' . $m[1] . (!empty($m[2]) ? $m[2] : '');
}
// ISO-8859
else if (preg_match('/ISO8859([0-9]{0,2})/', $str, $m)) {
$iso = 'ISO-8859-' . ($m[1] ?: 1);
// some clients sends windows-1252 text as latin1,
// it is safe to use windows-1252 for all latin1
$result = $iso == 'ISO-8859-1' ? 'WINDOWS-1252' : $iso;
}
// handle broken charset names e.g. WINDOWS-1250HTTP-EQUIVCONTENT-TYPE
else if (preg_match('/(WIN|WINDOWS)([0-9]+)/', $str, $m)) {
$result = 'WINDOWS-' . $m[2];
}
// LATIN
else if (preg_match('/LATIN(.*)/', $str, $m)) {
$aliases = ['2' => 2, '3' => 3, '4' => 4, '5' => 9, '6' => 10,
'7' => 13, '8' => 14, '9' => 15, '10' => 16,
'ARABIC' => 6, 'CYRILLIC' => 5, 'GREEK' => 7, 'GREEK1' => 7, 'HEBREW' => 8
];
// some clients sends windows-1252 text as latin1,
// it is safe to use windows-1252 for all latin1
if ($m[1] == 1) {
$result = 'WINDOWS-1252';
}
// we need ISO labels
else if (!empty($aliases[$m[1]])) {
$result = 'ISO-8859-'.$aliases[$m[1]];
}
}
$charsets[$input] = $result;
return $result;
}
/**
* Convert a string from one charset to another.
*
* @param string $str Input string
* @param string $from Suspected charset of the input string
* @param string $to Target charset to convert to; defaults to RCUBE_CHARSET
*
* @return string Converted string
*/
public static function convert($str, $from, $to = null)
{
static $iconv_options;
$to = empty($to) ? RCUBE_CHARSET : self::parse_charset($to);
$from = self::parse_charset($from);
// It is a common case when UTF-16 charset is used with US-ASCII content (#1488654)
// In that case we can just skip the conversion (use UTF-8)
if ($from == 'UTF-16' && !preg_match('/[^\x00-\x7F]/', $str)) {
$from = 'UTF-8';
}
if ($from == $to || empty($str) || empty($from)) {
return $str;
}
$out = false;
$error_handler = function() { throw new \Exception(); };
// Ignore invalid characters
$mbstring_sc = mb_substitute_character();
mb_substitute_character('none');
// If mbstring reports an illegal character in input via E_WARNING.
// FIXME: Is this really true with substitute character 'none'?
// A warning is thrown in PHP<8 also on unsupported encoding, in PHP>=8 ValueError
// is thrown instead (therefore we catch Throwable below)
set_error_handler($error_handler, E_WARNING);
try {
$out = mb_convert_encoding($str, $to, $from);
}
catch (Throwable $e) {
$out = false;
}
restore_error_handler();
mb_substitute_character($mbstring_sc);
if ($out !== false) {
return $out;
}
if ($iconv_options === null) {
if (function_exists('iconv')) {
// ignore characters not available in output charset
$iconv_options = '//IGNORE';
if (iconv('', $iconv_options, '') === false) {
// iconv implementation does not support options
$iconv_options = '';
}
}
else {
$iconv_options = false;
}
}
// Fallback to iconv module, it is slower, but supports much more charsets than mbstring
if ($iconv_options !== false && $from != 'UTF7-IMAP' && $to != 'UTF7-IMAP'
&& $from !== 'ISO-2022-JP'
) {
// If iconv reports an illegal character in input it means that input string
// has been truncated. It's reported as E_NOTICE.
// PHP8 will also throw E_WARNING on unsupported encoding.
set_error_handler($error_handler, E_NOTICE | E_WARNING);
try {
$out = iconv($from, $to . $iconv_options, $str);
}
catch (Throwable $e) {
$out = false;
}
restore_error_handler();
if ($out !== false) {
return $out;
}
}
// return the original string
return $str;
}
/**
* Check if the specified input string matches one of the provided charsets.
* This includes UTF-32, UTF-16, RCUBE_CHARSET and default_charset.
*
* @param string $str Input string
* @param array $charsets Suspected charsets of the input string
*
* @return string|null First matching charset
*/
public static function check($str, $charsets = [])
{
$chunk = strlen($str) > 100 * 1024 ? substr($str, 0, 100 * 1024) : $str;
// Add dehault charset, system charset and easily detectable charset to the list
if (substr($chunk, 0, 4) == "\0\0\xFE\xFF") $charsets[] = 'UTF-32BE';
if (substr($chunk, 0, 4) == "\xFF\xFE\0\0") $charsets[] = 'UTF-32LE';
if (substr($chunk, 0, 2) == "\xFE\xFF") $charsets[] = 'UTF-16BE';
if (substr($chunk, 0, 2) == "\xFF\xFE") $charsets[] = 'UTF-16LE';
// heuristics
if (preg_match('/\x00\x00\x00[^\x00]/', $chunk)) $charsets[] = 'UTF-32BE';
if (preg_match('/[^\x00]\x00\x00\x00/', $chunk)) $charsets[] = 'UTF-32LE';
if (preg_match('/\x00[^\x00]\x00[^\x00]/', $chunk)) $charsets[] = 'UTF-16BE';
if (preg_match('/[^\x00]\x00[^\x00]\x00/', $chunk)) $charsets[] = 'UTF-16LE';
$charsets[] = RCUBE_CHARSET;
$charsets[] = (string) rcube::get_instance()->config->get('default_charset');
$charsets = array_map(['rcube_charset', 'parse_charset'], $charsets);
$charsets = array_unique(array_filter($charsets));
foreach ($charsets as $charset) {
$ret = self::convert($chunk, $charset);
if ($ret === rcube_charset::clean($ret)) {
return $charset;
}
}
}
/**
* Converts string from standard UTF-7 (RFC 2152) to UTF-8.
*
* @param string $str Input string (UTF-7)
*
* @return string Converted string (UTF-8)
* @deprecated use self::convert()
*/
public static function utf7_to_utf8($str)
{
return self::convert($str, 'UTF-7', 'UTF-8');
}
/**
* Converts string from UTF-16 to UTF-8 (helper for utf-7 to utf-8 conversion)
*
* @param string $str Input string
*
* @return string The converted string
* @deprecated use self::convert()
*/
public static function utf16_to_utf8($str)
{
return self::convert($str, 'UTF-16BE', 'UTF-8');
}
/**
* Convert the data ($str) from RFC 2060's UTF-7 to UTF-8.
* If input data is invalid, return the original input string.
* RFC 2060 obviously intends the encoding to be unique (see
* point 5 in section 5.1.3), so we reject any non-canonical
* form, such as &ACY- (instead of &-) or &AMA-&AMA- (instead
* of &AMAAwA-).
*
* @param string $str Input string (UTF7-IMAP)
*
* @return string Output string (UTF-8)
* @deprecated use self::convert()
*/
public static function utf7imap_to_utf8($str)
{
return self::convert($str, 'UTF7-IMAP', 'UTF-8');
}
/**
* Convert the data ($str) from UTF-8 to RFC 2060's UTF-7.
* Unicode characters above U+FFFF are replaced by U+FFFE.
* If input data is invalid, return an empty string.
*
* @param string $str Input string (UTF-8)
*
* @return string Output string (UTF7-IMAP)
* @deprecated use self::convert()
*/
public static function utf8_to_utf7imap($str)
{
return self::convert($str, 'UTF-8', 'UTF7-IMAP');
}
/**
* A method to guess character set of a string.
*
* @param string $string String
* @param string $failover Default result for failover
* @param string $language User language
*
* @return string Charset name
* @deprecated
*/
public static function detect($string, $failover = null, $language = null)
{
if (substr($string, 0, 4) == "\0\0\xFE\xFF") return 'UTF-32BE'; // Big Endian
if (substr($string, 0, 4) == "\xFF\xFE\0\0") return 'UTF-32LE'; // Little Endian
if (substr($string, 0, 2) == "\xFE\xFF") return 'UTF-16BE'; // Big Endian
if (substr($string, 0, 2) == "\xFF\xFE") return 'UTF-16LE'; // Little Endian
if (substr($string, 0, 3) == "\xEF\xBB\xBF") return 'UTF-8';
// heuristics
if (strlen($string) >= 4) {
if ($string[0] == "\0" && $string[1] == "\0" && $string[2] == "\0" && $string[3] != "\0") return 'UTF-32BE';
if ($string[0] != "\0" && $string[1] == "\0" && $string[2] == "\0" && $string[3] == "\0") return 'UTF-32LE';
if ($string[0] == "\0" && $string[1] != "\0" && $string[2] == "\0" && $string[3] != "\0") return 'UTF-16BE';
if ($string[0] != "\0" && $string[1] == "\0" && $string[2] != "\0" && $string[3] == "\0") return 'UTF-16LE';
}
if (empty($language)) {
$rcube = rcube::get_instance();
$language = $rcube->get_user_language();
}
// Prioritize charsets according to the current language (#1485669)
$prio = null;
switch ($language) {
case 'ja_JP':
$prio = ['ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS'];
break;
case 'zh_CN':
case 'zh_TW':
$prio = ['UTF-8', 'BIG-5', 'EUC-TW', 'GB18030'];
break;
case 'ko_KR':
$prio = ['UTF-8', 'EUC-KR', 'ISO-2022-KR'];
break;
case 'ru_RU':
$prio = ['UTF-8', 'WINDOWS-1251', 'KOI8-R'];
break;
case 'tr_TR':
$prio = ['UTF-8', 'ISO-8859-9', 'WINDOWS-1254'];
break;
}
// mb_detect_encoding() is not reliable for some charsets (#1490135)
// use mb_check_encoding() to make charset priority lists really working
if (!empty($prio) && function_exists('mb_check_encoding')) {
foreach ($prio as $encoding) {
if (mb_check_encoding($string, $encoding)) {
return $encoding;
}
}
}
if (function_exists('mb_detect_encoding')) {
$exclude = 'BASE64,UUENCODE,HTML-ENTITIES,Quoted-Printable,'
. '7bit,8bit,pass,wchar,byte2be,byte2le,byte4be,byte4le,'
. 'UCS-4,UCS-4BE,UCS-4LE,UCS-2,UCS-2BE,UCS-2LE';
if (empty($prio)) {
$prio = [
'UTF-8',
'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9',
'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16',
'WINDOWS-1252', 'WINDOWS-1251', 'WINDOWS-1254',
'EUC-JP', 'EUC-TW', 'KOI8-R', 'BIG-5', 'ISO-2022-KR', 'ISO-2022-JP', 'GB18030',
];
}
// We have to remove unwanted/uncommon encodings from the list.
// This is needed especially on PHP >= 8.1
$all_encodings = array_diff(mb_list_encodings(), explode(',', $exclude));
$encodings = array_unique(array_merge($prio, $all_encodings));
if ($encoding = mb_detect_encoding($string, $encodings, true)) {
return $encoding;
}
}
// No match, check for UTF-8
// from http://w3.org/International/questions/qa-forms-utf-8.html
if (preg_match('/\A(
[\x09\x0A\x0D\x20-\x7E]
| [\xC2-\xDF][\x80-\xBF]
| \xE0[\xA0-\xBF][\x80-\xBF]
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| \xF0[\x90-\xBF][\x80-\xBF]{2}
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)*\z/xs', substr($string, 0, 2048))
) {
return 'UTF-8';
}
return $failover;
}
/**
* Removes non-unicode characters from input.
* If the input is an array, both values and keys will be cleaned up.
*
* @param mixed $input String or array.
*
* @return mixed String or array
*/
public static function clean($input)
{
// handle input of type array
if (is_array($input)) {
foreach (array_keys($input) as $key) {
$k = is_string($key) ? self::clean($key) : $key;
$v = self::clean($input[$key]);
if ($k !== $key) {
unset($input[$key]);
if (!array_key_exists($k, $input)) {
$input[$k] = $v;
}
}
else {
$input[$k] = $v;
}
}
return $input;
}
if (!is_string($input) || $input == '') {
return $input;
}
$msch = mb_substitute_character();
mb_substitute_character('none');
$res = mb_convert_encoding($input, 'UTF-8', 'UTF-8');
mb_substitute_character($msch);
return $res;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,430 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| E-mail message headers representation |
+-----------------------------------------------------------------------+
| Author: Aleksander Machniak <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Struct representing an e-mail message header
*
* @package Framework
* @subpackage Storage
*/
class rcube_message_header
{
/**
* Message sequence number
*
* @var int
*/
public $id;
/**
* Message unique identifier
*
* @var int
*/
public $uid;
/**
* Message subject
*
* @var string
*/
public $subject;
/**
* Message sender (From)
*
* @var string
*/
public $from;
/**
* Message recipient (To)
*
* @var string
*/
public $to;
/**
* Message additional recipients (Cc)
*
* @var string
*/
public $cc;
/**
* Message hidden recipients (Bcc)
*
* @var string
*/
public $bcc;
/**
* Message Reply-To header
*
* @var string
*/
public $replyto;
/**
* Message In-Reply-To header
*
* @var string
*/
public $in_reply_to;
/**
* Message date (Date)
*
* @var string
*/
public $date;
/**
* Message identifier (Message-ID)
*
* @var string
*/
public $messageID;
/**
* Message size
*
* @var int
*/
public $size;
/**
* Message encoding
*
* @var string
*/
public $encoding;
/**
* Message charset
*
* @var string
*/
public $charset;
/**
* Message Content-type
*
* @var string
*/
public $ctype;
/**
* Message timestamp (based on message date)
*
* @var int
*/
public $timestamp;
/**
* IMAP bodystructure string
*
* @var string
*/
public $bodystructure;
/**
* IMAP body (RFC822.TEXT)
*
* @var string
*/
public $body;
/**
* IMAP part bodies
*
* @var array
*/
public $bodypart = [];
/**
* IMAP internal date
*
* @var string
*/
public $internaldate;
/**
* Message References header
*
* @var string
*/
public $references;
/**
* Message priority (X-Priority)
*
* @var int
*/
public $priority;
/**
* Message receipt recipient
*
* @var string
*/
public $mdn_to;
/**
* IMAP folder this message is stored in
*
* @var string
*/
public $folder;
/**
* Other message headers
*
* @var array
*/
public $others = [];
/**
* Message flags
*
* @var array
*/
public $flags = [];
/**
* Extra flags (for the messages list)
*
* @var array
* @deprecated Use $flags
*/
public $list_flags = [];
/**
* Extra columns content (for the messages list)
*
* @var array
*/
public $list_cols = [];
/**
* Message structure
*
* @var rcube_message_part
*/
public $structure;
/**
* Message thread depth
*
* @var int
*/
public $depth;
/**
* Whether the message has references in the thread
*
* @var bool
*/
public $has_children;
/**
* Number of flagged children (in a thread)
*
* @var int
*/
public $flagged_children;
/**
* Number of unread children (in a thread)
*
* @var int
*/
public $unread_children;
/**
* UID of the message parent (in a thread)
*
* @var int
*/
public $parent_uid;
/**
* IMAP MODSEQ value
*
* @var int
*/
public $modseq;
/**
* IMAP ENVELOPE
*
* @var string
*/
public $envelope;
/**
* Header name to rcube_message_header object property map
*
* @var array
*/
private $obj_headers = [
'date' => 'date',
'from' => 'from',
'to' => 'to',
'subject' => 'subject',
'reply-to' => 'replyto',
'cc' => 'cc',
'bcc' => 'bcc',
'mbox' => 'folder',
'folder' => 'folder',
'content-transfer-encoding' => 'encoding',
'in-reply-to' => 'in_reply_to',
'content-type' => 'ctype',
'charset' => 'charset',
'references' => 'references',
'disposition-notification-to' => 'mdn_to',
'x-confirm-reading-to' => 'mdn_to',
'message-id' => 'messageID',
'x-priority' => 'priority',
];
/**
* Returns header value
*
* @param string $name Header name
* @param bool $decode Decode the header content
*
* @return string|null Header content
*/
public function get($name, $decode = true)
{
$name = strtolower($name);
$value = null;
if (isset($this->obj_headers[$name]) && isset($this->{$this->obj_headers[$name]})) {
$value = $this->{$this->obj_headers[$name]};
}
else if (isset($this->others[$name])) {
$value = $this->others[$name];
}
if ($decode && $value !== null) {
if (is_array($value)) {
foreach ($value as $key => $val) {
$val = rcube_mime::decode_header($val, $this->charset);
$value[$key] = rcube_charset::clean($val);
}
}
else {
$value = rcube_mime::decode_header($value, $this->charset);
$value = rcube_charset::clean($value);
}
}
return $value;
}
/**
* Sets header value
*
* @param string $name Header name
* @param string $value Header content
*/
public function set($name, $value)
{
$name = strtolower($name);
if (isset($this->obj_headers[$name])) {
$this->{$this->obj_headers[$name]} = $value;
}
else {
$this->others[$name] = $value;
}
}
/**
* Factory method to instantiate headers from a data array
*
* @param array $arr Hash array with header values
*
* @return rcube_message_header instance filled with headers values
*/
public static function from_array($arr)
{
$obj = new rcube_message_header;
foreach ($arr as $k => $v) {
$obj->set($k, $v);
}
return $obj;
}
}
/**
* Class for sorting an array of rcube_message_header objects in a predetermined order.
*
* @package Framework
* @subpackage Storage
*/
class rcube_message_header_sorter
{
/** @var array Message UIDs */
private $uids = [];
/**
* Set the predetermined sort order.
*
* @param array $index Numerically indexed array of IMAP UIDs
*/
function set_index($index)
{
$index = array_flip($index);
$this->uids = $index;
}
/**
* Sort the array of header objects
*
* @param array $headers Array of rcube_message_header objects indexed by UID
*/
function sort_headers(&$headers)
{
uksort($headers, [$this, "compare_uids"]);
}
/**
* Sort method called by uksort()
*
* @param int $a Array key (UID)
* @param int $b Array key (UID)
*/
function compare_uids($a, $b)
{
// then find each sequence number in my ordered list
$posa = isset($this->uids[$a]) ? intval($this->uids[$a]) : -1;
$posb = isset($this->uids[$b]) ? intval($this->uids[$b]) : -1;
// return the relative position as the comparison value
return $posa - $posb;
}
}
@@ -0,0 +1,992 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| MIME message parsing utilities |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <[email protected]> |
| Author: Aleksander Machniak <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Class for parsing MIME messages
*
* @package Framework
* @subpackage Storage
*/
class rcube_mime
{
private static $default_charset;
/**
* Object constructor.
*/
function __construct($default_charset = null)
{
self::$default_charset = $default_charset;
}
/**
* Returns message/object character set name
*
* @return string Character set name
*/
public static function get_charset()
{
if (self::$default_charset) {
return self::$default_charset;
}
if ($charset = rcube::get_instance()->config->get('default_charset')) {
return $charset;
}
return RCUBE_CHARSET;
}
/**
* Parse the given raw message source and return a structure
* of rcube_message_part objects.
*
* It makes use of the rcube_mime_decode library
*
* @param string $raw_body The message source
*
* @return object rcube_message_part The message structure
*/
public static function parse_message($raw_body)
{
$conf = [
'include_bodies' => true,
'decode_bodies' => true,
'decode_headers' => false,
'default_charset' => self::get_charset(),
];
$mime = new rcube_mime_decode($conf);
return $mime->decode($raw_body);
}
/**
* Split an address list into a structured array list
*
* @param string|array $input Input string (or list of strings)
* @param int $max List only this number of addresses
* @param bool $decode Decode address strings
* @param string $fallback Fallback charset if none specified
* @param bool $addronly Return flat array with e-mail addresses only
*
* @return array Indexed list of addresses
*/
static function decode_address_list($input, $max = null, $decode = true, $fallback = null, $addronly = false)
{
// A common case when the same header is used many times in a mail message
if (is_array($input)) {
$input = implode(', ', $input);
}
$a = self::parse_address_list((string) $input, $decode, $fallback);
$out = [];
$j = 0;
// Special chars as defined by RFC 822 need to in quoted string (or escaped).
$special_chars = '[\(\)\<\>\\\.\[\]@,;:"]';
if (!is_array($a)) {
return $out;
}
foreach ($a as $val) {
$j++;
$address = trim($val['address']);
if ($addronly) {
$out[$j] = $address;
}
else {
$name = trim($val['name']);
$string = '';
if ($name && $address && $name != $address) {
$string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address);
}
else if ($address) {
$string = $address;
}
else if ($name) {
$string = $name;
}
$out[$j] = ['name' => $name, 'mailto' => $address, 'string' => $string];
}
if ($max && $j == $max) {
break;
}
}
return $out;
}
/**
* Decode a message header value
*
* @param string $input Header value
* @param string $fallback Fallback charset if none specified
*
* @return string Decoded string
*/
public static function decode_header($input, $fallback = null)
{
$str = self::decode_mime_string((string)$input, $fallback);
return $str;
}
/**
* Decode a mime-encoded string to internal charset
*
* @param string $input Header value
* @param string $fallback Fallback charset if none specified
*
* @return string Decoded string
*/
public static function decode_mime_string($input, $fallback = null)
{
$default_charset = $fallback ?: self::get_charset();
// rfc: all line breaks or other characters not found
// in the Base64 Alphabet must be ignored by decoding software
// delete all blanks between MIME-lines, differently we can
// receive unnecessary blanks and broken utf-8 symbols
$input = preg_replace("/\?=\s+=\?/", '?==?', $input);
// encoded-word regexp
$re = '/=\?([^?]+)\?([BbQq])\?([^\n]*?)\?=/';
// Find all RFC2047's encoded words
if (preg_match_all($re, $input, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
// Initialize variables
$tmp = [];
$out = '';
$start = 0;
foreach ($matches as $idx => $m) {
$pos = $m[0][1];
$charset = $m[1][0];
$encoding = $m[2][0];
$text = $m[3][0];
$length = strlen($m[0][0]);
// Append everything that is before the text to be decoded
if ($start != $pos) {
$substr = substr($input, $start, $pos-$start);
$out .= rcube_charset::convert($substr, $default_charset);
$start = $pos;
}
$start += $length;
// Per RFC2047, each string part "MUST represent an integral number
// of characters . A multi-octet character may not be split across
// adjacent encoded-words." However, some mailers break this, so we
// try to handle characters spanned across parts anyway by iterating
// through and aggregating sequential encoded parts with the same
// character set and encoding, then perform the decoding on the
// aggregation as a whole.
$tmp[] = $text;
if (!empty($matches[$idx+1]) && ($next_match = $matches[$idx+1])) {
if ($next_match[0][1] == $start
&& $next_match[1][0] == $charset
&& $next_match[2][0] == $encoding
) {
continue;
}
}
$count = count($tmp);
$text = '';
// Decode and join encoded-word's chunks
if ($encoding == 'B' || $encoding == 'b') {
$rest = '';
// base64 must be decoded a segment at a time.
// However, there are broken implementations that continue
// in the following word, we'll handle that (#6048)
for ($i=0; $i<$count; $i++) {
$chunk = $rest . $tmp[$i];
$length = strlen($chunk);
if ($length % 4) {
$length = floor($length / 4) * 4;
$rest = substr($chunk, $length);
$chunk = substr($chunk, 0, $length);
}
$text .= base64_decode($chunk);
}
}
else { // if ($encoding == 'Q' || $encoding == 'q') {
// quoted printable can be combined and processed at once
for ($i=0; $i<$count; $i++) {
$text .= $tmp[$i];
}
$text = str_replace('_', ' ', $text);
$text = quoted_printable_decode($text);
}
$out .= rcube_charset::convert($text, $charset);
$tmp = [];
}
// add the last part of the input string
if ($start != strlen($input)) {
$out .= rcube_charset::convert(substr($input, $start), $default_charset);
}
// return the results
return $out;
}
// no encoding information, use fallback
return rcube_charset::convert($input, $default_charset);
}
/**
* Decode a mime part
*
* @param string $input Input string
* @param string $encoding Part encoding
*
* @return string Decoded string
*/
public static function decode($input, $encoding = '7bit')
{
switch (strtolower($encoding)) {
case 'quoted-printable':
return quoted_printable_decode($input);
case 'base64':
return base64_decode($input);
case 'x-uuencode':
case 'x-uue':
case 'uue':
case 'uuencode':
return convert_uudecode($input);
case '7bit':
default:
return $input;
}
}
/**
* Split RFC822 header string into an associative array
*/
public static function parse_headers($headers)
{
$result = [];
$headers = preg_replace('/\r?\n(\t| )+/', ' ', $headers);
$lines = explode("\n", $headers);
$count = count($lines);
for ($i=0; $i<$count; $i++) {
if ($p = strpos($lines[$i], ': ')) {
$field = strtolower(substr($lines[$i], 0, $p));
$value = trim(substr($lines[$i], $p+1));
if (!empty($value)) {
$result[$field] = $value;
}
}
}
return $result;
}
/**
* E-mail address list parser
*/
private static function parse_address_list($str, $decode = true, $fallback = null)
{
// remove any newlines and carriage returns before
$str = preg_replace('/\r?\n(\s|\t)?/', ' ', $str);
// extract list items, remove comments
$str = self::explode_header_string(',;', $str, true);
// simplified regexp, supporting quoted local part
$email_rx = '([^\s:]+|("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+"))@\S+';
$result = [];
foreach ($str as $key => $val) {
$name = '';
$address = '';
$val = trim($val);
// First token might be a group name, ignore it
$tokens = self::explode_header_string(' ', $val);
if (isset($tokens[0]) && $tokens[0][strlen($tokens[0])-1] == ':') {
$val = substr($val, strlen($tokens[0]));
}
if (preg_match('/(.*)<('.$email_rx.')$/', $val, $m)) {
// Note: There are cases like "Test<[email protected]" with no closing bracket,
// therefor we do not include it in the regexp above, but we have to
// remove it later, because $email_rx will catch it (#8164)
$address = rtrim($m[2], '>');
$name = trim($m[1]);
}
else if (preg_match('/^('.$email_rx.')$/', $val, $m)) {
$address = $m[1];
$name = '';
}
// special case (#1489092)
else if (preg_match('/(\s*<MAILER-DAEMON>)$/', $val, $m)) {
$address = 'MAILER-DAEMON';
$name = substr($val, 0, -strlen($m[1]));
}
else if (preg_match('/('.$email_rx.')/', $val, $m)) {
$name = $m[1];
}
else {
$name = $val;
}
// unquote and/or decode name
if ($name) {
// An unquoted name ending with colon is a address group name, ignore it
if ($name[strlen($name)-1] == ':') {
$name = '';
}
if (strlen($name) > 1 && $name[0] == '"' && $name[strlen($name)-1] == '"') {
$name = substr($name, 1, -1);
$name = stripslashes($name);
}
if ($decode) {
$name = self::decode_header($name, $fallback);
// some clients encode addressee name with quotes around it
if (strlen($name) > 1 && $name[0] == '"' && $name[strlen($name)-1] == '"') {
$name = substr($name, 1, -1);
}
}
}
if (!$address && $name) {
$address = $name;
$name = '';
}
if ($address) {
$address = self::fix_email($address);
$result[$key] = ['name' => $name, 'address' => $address];
}
}
return $result;
}
/**
* Explodes header (e.g. address-list) string into array of strings
* using specified separator characters with proper handling
* of quoted-strings and comments (RFC2822)
*
* @param string $separator String containing separator characters
* @param string $str Header string
* @param bool $remove_comments Enable to remove comments
*
* @return array Header items
*/
public static function explode_header_string($separator, $str, $remove_comments = false)
{
$length = strlen($str);
$result = [];
$quoted = false;
$comment = 0;
$out = '';
for ($i=0; $i<$length; $i++) {
// we're inside a quoted string
if ($quoted) {
if ($str[$i] == '"') {
$quoted = false;
}
else if ($str[$i] == "\\") {
if ($comment <= 0) {
$out .= "\\";
}
$i++;
}
}
// we are inside a comment string
else if ($comment > 0) {
if ($str[$i] == ')') {
$comment--;
}
else if ($str[$i] == '(') {
$comment++;
}
else if ($str[$i] == "\\") {
$i++;
}
continue;
}
// separator, add to result array
else if (strpos($separator, $str[$i]) !== false) {
if ($out) {
$result[] = $out;
}
$out = '';
continue;
}
// start of quoted string
else if ($str[$i] == '"') {
$quoted = true;
}
// start of comment
else if ($remove_comments && $str[$i] == '(') {
$comment++;
}
if ($comment <= 0) {
$out .= $str[$i];
}
}
if ($out && $comment <= 0) {
$result[] = $out;
}
return $result;
}
/**
* Interpret a format=flowed message body according to RFC 2646
*
* @param string $text Raw body formatted as flowed text
* @param string $mark Mark each flowed line with specified character
* @param bool $delsp Remove the trailing space of each flowed line
*
* @return string Interpreted text with unwrapped lines and stuffed space removed
*/
public static function unfold_flowed($text, $mark = null, $delsp = false)
{
$text = preg_split('/\r?\n/', $text);
$last = -1;
$q_level = 0;
$marks = [];
foreach ($text as $idx => $line) {
if ($q = strspn($line, '>')) {
// remove quote chars
$line = substr($line, $q);
// remove (optional) space-staffing
if (isset($line[0]) && $line[0] === ' ') {
$line = substr($line, 1);
}
// The same paragraph (We join current line with the previous one) when:
// - the same level of quoting
// - previous line was flowed
// - previous line contains more than only one single space (and quote char(s))
if ($q == $q_level
&& isset($text[$last]) && $text[$last][strlen($text[$last])-1] == ' '
&& !preg_match('/^>+ {0,1}$/', $text[$last])
) {
if ($delsp) {
$text[$last] = substr($text[$last], 0, -1);
}
$text[$last] .= $line;
unset($text[$idx]);
if ($mark) {
$marks[$last] = true;
}
}
else {
$last = $idx;
}
}
else {
if ($line == '-- ') {
$last = $idx;
}
else {
// remove space-stuffing
if (isset($line[0]) && $line[0] === ' ') {
$line = substr($line, 1);
}
$last_len = isset($text[$last]) ? strlen($text[$last]) : 0;
if (
$last_len && $line && !$q_level && $text[$last] != '-- '
&& isset($text[$last][$last_len-1]) && $text[$last][$last_len-1] == ' '
) {
if ($delsp) {
$text[$last] = substr($text[$last], 0, -1);
}
$text[$last] .= $line;
unset($text[$idx]);
if ($mark) {
$marks[$last] = true;
}
}
else {
$text[$idx] = $line;
$last = $idx;
}
}
}
$q_level = $q;
}
if (!empty($marks)) {
foreach (array_keys($marks) as $mk) {
$text[$mk] = $mark . $text[$mk];
}
}
return implode("\r\n", $text);
}
/**
* Wrap the given text to comply with RFC 2646
*
* @param string $text Text to wrap
* @param int $length Length
* @param string $charset Character encoding of $text
*
* @return string Wrapped text
*/
public static function format_flowed($text, $length = 72, $charset = null)
{
$text = preg_split('/\r?\n/', $text);
foreach ($text as $idx => $line) {
if ($line != '-- ') {
if ($level = strspn($line, '>')) {
// remove quote chars
$line = substr($line, $level);
// remove (optional) space-staffing and spaces before the line end
$line = rtrim($line, ' ');
if (isset($line[0]) && $line[0] === ' ') {
$line = substr($line, 1);
}
$prefix = str_repeat('>', $level) . ' ';
$line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix", false, $charset);
}
else if ($line) {
$line = self::wordwrap(rtrim($line), $length - 2, " \r\n", false, $charset);
// space-stuffing
$line = preg_replace('/(^|\r\n)(From| |>)/', '\\1 \\2', $line);
}
$text[$idx] = $line;
}
}
return implode("\r\n", $text);
}
/**
* Improved wordwrap function with multibyte support.
* The code is based on Zend_Text_MultiByte::wordWrap().
*
* @param string $string Text to wrap
* @param int $width Line width
* @param string $break Line separator
* @param bool $cut Enable to cut word
* @param string $charset Charset of $string
* @param bool $wrap_quoted When enabled quoted lines will not be wrapped
*
* @return string Text
*/
public static function wordwrap($string, $width = 75, $break = "\n", $cut = false, $charset = null, $wrap_quoted = true)
{
// Note: Never try to use iconv instead of mbstring functions here
// Iconv's substr/strlen are 100x slower (#1489113)
if ($charset && $charset != RCUBE_CHARSET) {
$charset = rcube_charset::parse_charset($charset);
mb_internal_encoding($charset);
}
// Convert \r\n to \n, this is our line-separator
$string = str_replace("\r\n", "\n", $string);
$separator = "\n"; // must be 1 character length
$result = [];
while (($stringLength = mb_strlen($string)) > 0) {
$breakPos = mb_strpos($string, $separator, 0);
// quoted line (do not wrap)
if ($wrap_quoted && $string[0] == '>') {
if ($breakPos === $stringLength - 1 || $breakPos === false) {
$subString = $string;
$cutLength = null;
}
else {
$subString = mb_substr($string, 0, $breakPos);
$cutLength = $breakPos + 1;
}
}
// next line found and current line is shorter than the limit
else if ($breakPos !== false && $breakPos < $width) {
if ($breakPos === $stringLength - 1) {
$subString = $string;
$cutLength = null;
}
else {
$subString = mb_substr($string, 0, $breakPos);
$cutLength = $breakPos + 1;
}
}
else {
$subString = mb_substr($string, 0, $width);
// last line
if ($breakPos === false && $subString === $string) {
$cutLength = null;
}
else {
$nextChar = mb_substr($string, $width, 1);
if ($nextChar === ' ' || $nextChar === $separator) {
$afterNextChar = mb_substr($string, $width + 1, 1);
// Note: mb_substr() does never return False
if ($afterNextChar === false || $afterNextChar === '') {
$subString .= $nextChar;
}
$cutLength = mb_strlen($subString) + 1;
}
else {
$spacePos = mb_strrpos($subString, ' ', 0);
if ($spacePos !== false) {
$subString = mb_substr($subString, 0, $spacePos);
$cutLength = $spacePos + 1;
}
else if ($cut === false) {
$spacePos = mb_strpos($string, ' ', 0);
if ($spacePos !== false && ($breakPos === false || $spacePos < $breakPos)) {
$subString = mb_substr($string, 0, $spacePos);
$cutLength = $spacePos + 1;
}
else if ($breakPos === false) {
$subString = $string;
$cutLength = null;
}
else {
$subString = mb_substr($string, 0, $breakPos);
$cutLength = $breakPos + 1;
}
}
else {
$cutLength = $width;
}
}
}
}
$result[] = $subString;
if ($cutLength !== null) {
$string = mb_substr($string, $cutLength, ($stringLength - $cutLength));
}
else {
break;
}
}
if ($charset && $charset != RCUBE_CHARSET) {
mb_internal_encoding(RCUBE_CHARSET);
}
return implode($break, $result);
}
/**
* A method to guess the mime_type of an attachment.
*
* @param string $path Path to the file or file contents
* @param string $name File name (with suffix)
* @param string $failover Mime type supplied for failover
* @param bool $is_stream Set to True if $path contains file contents
* @param bool $skip_suffix Set to True if the config/mimetypes.php map should be ignored
*
* @return string
* @author Till Klampaeckel <[email protected]>
* @see http://de2.php.net/manual/en/ref.fileinfo.php
* @see http://de2.php.net/mime_content_type
*/
public static function file_content_type($path, $name, $failover = 'application/octet-stream', $is_stream = false, $skip_suffix = false)
{
$mime_type = null;
$config = rcube::get_instance()->config;
// Detect mimetype using filename extension
if (!$skip_suffix) {
$mime_type = self::file_ext_type($name);
}
// try fileinfo extension if available
if (!$mime_type && function_exists('finfo_open')) {
$mime_magic = $config->get('mime_magic');
// null as a 2nd argument should be the same as no argument
// this however is not true on all systems/versions
if ($mime_magic) {
$finfo = finfo_open(FILEINFO_MIME, $mime_magic);
}
else {
$finfo = finfo_open(FILEINFO_MIME);
}
if ($finfo) {
$func = $is_stream ? 'finfo_buffer' : 'finfo_file';
$mime_type = $func($finfo, $path, FILEINFO_MIME_TYPE);
finfo_close($finfo);
}
}
// try PHP's mime_content_type
if (!$mime_type && !$is_stream && function_exists('mime_content_type')) {
$mime_type = @mime_content_type($path);
}
// fall back to user-submitted string
if (!$mime_type) {
$mime_type = $failover;
}
return $mime_type;
}
/**
* File type detection based on file name only.
*
* @param string $filename Path to the file or file contents
*
* @return string|null Mimetype label
*/
public static function file_ext_type($filename)
{
static $mime_ext = [];
if (empty($mime_ext)) {
foreach (rcube::get_instance()->config->resolve_paths('mimetypes.php') as $fpath) {
$mime_ext = array_merge($mime_ext, (array) @include($fpath));
}
}
// use file name suffix with hard-coded mime-type map
if (!empty($mime_ext) && $filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($ext && !empty($mime_ext[$ext])) {
return $mime_ext[$ext];
}
}
}
/**
* Get mimetype => file extension mapping
*
* @param string $mimetype Mime-Type to get extensions for
*
* @return array List of extensions matching the given mimetype or a hash array
* with ext -> mimetype mappings if $mimetype is not given
*/
public static function get_mime_extensions($mimetype = null)
{
static $mime_types, $mime_extensions;
// return cached data
if (is_array($mime_types)) {
return $mimetype ? (isset($mime_types[$mimetype]) ? $mime_types[$mimetype] : []) : $mime_extensions;
}
// load mapping file
$file_paths = [];
if ($mime_types = rcube::get_instance()->config->get('mime_types')) {
$file_paths[] = $mime_types;
}
// try common locations
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
$file_paths[] = 'C:/xampp/apache/conf/mime.types';
}
else {
$file_paths[] = '/etc/mime.types';
$file_paths[] = '/etc/httpd/mime.types';
$file_paths[] = '/etc/httpd2/mime.types';
$file_paths[] = '/etc/apache/mime.types';
$file_paths[] = '/etc/apache2/mime.types';
$file_paths[] = '/etc/nginx/mime.types';
$file_paths[] = '/usr/local/etc/httpd/conf/mime.types';
$file_paths[] = '/usr/local/etc/apache/conf/mime.types';
$file_paths[] = '/usr/local/etc/apache24/mime.types';
}
$mime_types = [];
$mime_extensions = [];
$lines = [];
$regex = "/([\w\+\-\.\/]+)\s+([\w\s]+)/i";
foreach ($file_paths as $fp) {
if (@is_readable($fp)) {
$lines = file($fp, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
break;
}
}
foreach ($lines as $line) {
// skip comments or mime types w/o any extensions
if ($line[0] == '#' || !preg_match($regex, $line, $matches)) {
continue;
}
$mime = $matches[1];
foreach (explode(' ', $matches[2]) as $ext) {
$ext = trim($ext);
$mime_types[$mime][] = $ext;
$mime_extensions[$ext] = $mime;
}
}
// fallback to some well-known types most important for daily emails
if (empty($mime_types)) {
foreach (rcube::get_instance()->config->resolve_paths('mimetypes.php') as $fpath) {
$mime_extensions = array_merge($mime_extensions, (array) @include($fpath));
}
foreach ($mime_extensions as $ext => $mime) {
$mime_types[$mime][] = $ext;
}
}
// Add some known aliases that aren't included by some mime.types (#1488891)
// the order is important here so standard extensions have higher prio
$aliases = [
'image/gif' => ['gif'],
'image/png' => ['png'],
'image/x-png' => ['png'],
'image/jpeg' => ['jpg', 'jpeg', 'jpe'],
'image/jpg' => ['jpg', 'jpeg', 'jpe'],
'image/pjpeg' => ['jpg', 'jpeg', 'jpe'],
'image/tiff' => ['tif'],
'image/bmp' => ['bmp'],
'image/x-ms-bmp' => ['bmp'],
'message/rfc822' => ['eml'],
'text/x-mail' => ['eml'],
];
foreach ($aliases as $mime => $exts) {
if (isset($mime_types[$mime])) {
$mime_types[$mime] = array_unique(array_merge((array) $mime_types[$mime], $exts));
}
else {
$mime_types[$mime] = $exts;
}
foreach ($exts as $ext) {
if (!isset($mime_extensions[$ext])) {
$mime_extensions[$ext] = $mime;
}
}
}
if ($mimetype) {
return !empty($mime_types[$mimetype]) ? $mime_types[$mimetype] : [];
}
return $mime_extensions;
}
/**
* Detect image type of the given binary data by checking magic numbers.
*
* @param string $data Binary file content
*
* @return string Detected mime-type or jpeg as fallback
*/
public static function image_content_type($data)
{
$type = 'jpeg';
if (preg_match('/^\x89\x50\x4E\x47/', $data)) $type = 'png';
else if (preg_match('/^\x47\x49\x46\x38/', $data)) $type = 'gif';
else if (preg_match('/^\x00\x00\x01\x00/', $data)) $type = 'ico';
// else if (preg_match('/^\xFF\xD8\xFF\xE0/', $data)) $type = 'jpeg';
return 'image/' . $type;
}
/**
* Try to fix invalid email addresses
*/
public static function fix_email($email)
{
$parts = rcube_utils::explode_quoted_string('@', $email);
foreach ($parts as $idx => $part) {
// remove redundant quoting (#1490040)
if (isset($part[0]) && $part[0] == '"' && preg_match('/^"([a-zA-Z0-9._+=-]+)"$/', $part, $m)) {
$parts[$idx] = $m[1];
}
}
return implode('@', $parts);
}
/**
* Fix mimetype name.
*
* @param string $type Mimetype
*
* @return string Mimetype
*/
public static function fix_mimetype($type)
{
$type = strtolower(trim($type));
$aliases = [
'image/x-ms-bmp' => 'image/bmp', // #4771
'pdf' => 'application/pdf', // #6816
];
if (!empty($aliases[$type])) {
return $aliases[$type];
}
// Some versions of Outlook create garbage Content-Type:
// application/pdf.A520491B_3BF7_494D_8855_7FAC2C6C0608
if (preg_match('/^application\/pdf.+/', $type)) {
return 'application/pdf';
}
// treat image/pjpeg (image/pjpg, image/jpg) as image/jpeg (#4196)
if (preg_match('/^image\/p?jpe?g$/', $type)) {
return 'image/jpeg';
}
return $type;
}
}
@@ -0,0 +1,446 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| SORT/SEARCH/ESEARCH response handler |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <[email protected]> |
| Author: Aleksander Machniak <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Class for accessing IMAP's SORT/SEARCH/ESEARCH result
*
* @package Framework
* @subpackage Storage
*/
class rcube_result_index
{
public $incomplete = false;
protected $raw_data;
protected $mailbox;
protected $meta = [];
protected $params = [];
protected $order = 'ASC';
const SEPARATOR_ELEMENT = ' ';
/**
* Object constructor.
*/
public function __construct($mailbox = null, $data = null, $order = null)
{
$this->mailbox = $mailbox;
$this->order = $order == 'DESC' ? 'DESC' : 'ASC';
$this->init($data);
}
/**
* Initializes object with SORT command response
*
* @param string $data IMAP response string
*/
public function init($data = null)
{
$this->meta = [];
$data = explode('*', (string)$data);
// ...skip unilateral untagged server responses
for ($i=0, $len=count($data); $i<$len; $i++) {
$data_item = &$data[$i];
if (preg_match('/^ SORT/i', $data_item)) {
// valid response, initialize raw_data for is_error()
$this->raw_data = '';
$data_item = substr($data_item, 5);
break;
}
else if (preg_match('/^ (E?SEARCH)/i', $data_item, $m)) {
// valid response, initialize raw_data for is_error()
$this->raw_data = '';
$data_item = substr($data_item, strlen($m[0]));
if (strtoupper($m[1]) == 'ESEARCH') {
$data_item = trim($data_item);
// remove MODSEQ response
if (preg_match('/\(MODSEQ ([0-9]+)\)$/i', $data_item, $m)) {
$data_item = substr($data_item, 0, -strlen($m[0]));
$this->params['MODSEQ'] = $m[1];
}
// remove TAG response part
if (preg_match('/^\(TAG ["a-z0-9]+\)\s*/i', $data_item, $m)) {
$data_item = substr($data_item, strlen($m[0]));
}
// remove UID
$data_item = preg_replace('/^UID\s*/i', '', $data_item);
// ESEARCH parameters
while (preg_match('/^([a-z]+) ([0-9:,]+)\s*/i', $data_item, $m)) {
$param = strtoupper($m[1]);
$value = $m[2];
$this->params[$param] = $value;
$data_item = substr($data_item, strlen($m[0]));
if (in_array($param, ['COUNT', 'MIN', 'MAX'])) {
$this->meta[strtolower($param)] = (int) $value;
}
}
// @TODO: Implement compression using compressMessageSet() in __sleep() and __wakeup() ?
// @TODO: work with compressed result?!
if (isset($this->params['ALL'])) {
$data_item = implode(self::SEPARATOR_ELEMENT,
rcube_imap_generic::uncompressMessageSet($this->params['ALL']));
}
}
break;
}
unset($data[$i]);
}
$data = array_filter($data);
if (empty($data)) {
return;
}
$data = array_shift($data);
$data = trim($data);
$data = preg_replace('/[\r\n]/', '', $data);
$data = preg_replace('/\s+/', ' ', $data);
$this->raw_data = $data;
}
/**
* Checks the result from IMAP command
*
* @return bool True if the result is an error, False otherwise
*/
public function is_error()
{
return $this->raw_data === null;
}
/**
* Checks if the result is empty
*
* @return bool True if the result is empty, False otherwise
*/
public function is_empty()
{
return empty($this->raw_data)
&& empty($this->meta['max']) && empty($this->meta['min']) && empty($this->meta['count']);
}
/**
* Returns number of elements in the result
*
* @return int Number of elements
*/
public function count()
{
if (isset($this->meta['count'])) {
return $this->meta['count'];
}
if (empty($this->raw_data)) {
$this->meta['count'] = 0;
$this->meta['length'] = 0;
}
else {
$this->meta['count'] = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT);
}
return $this->meta['count'];
}
/**
* Returns number of elements in the result.
* Alias for count() for compatibility with rcube_result_thread
*
* @return int Number of elements
*/
public function count_messages()
{
return $this->count();
}
/**
* Returns maximal message identifier in the result
*
* @return int|null Maximal message identifier
*/
public function max()
{
if ($this->is_empty()) {
return null;
}
if (!isset($this->meta['max'])) {
$this->meta['max'] = null;
$all = $this->get();
if (!empty($all)) {
$this->meta['max'] = (int) max($all);
}
}
return $this->meta['max'];
}
/**
* Returns minimal message identifier in the result
*
* @return int|null Minimal message identifier
*/
public function min()
{
if ($this->is_empty()) {
return null;
}
if (!isset($this->meta['min'])) {
$this->meta['min'] = null;
$all = $this->get();
if (!empty($all)) {
$this->meta['min'] = (int) min($all);
}
}
return $this->meta['min'];
}
/**
* Slices data set.
*
* @param int $offset Offset (as for PHP's array_slice())
* @param int $length Number of elements (as for PHP's array_slice())
*/
public function slice($offset, $length)
{
$data = $this->get();
$data = array_slice($data, $offset, $length);
$this->meta = [];
$this->meta['count'] = count($data);
$this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
}
/**
* Filters data set. Removes elements not listed in $ids list.
*
* @param array $ids List of IDs to remove.
*/
public function filter($ids = [])
{
$data = $this->get();
$data = array_intersect($data, $ids);
$this->meta = [];
$this->meta['count'] = count($data);
$this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
}
/**
* Reverts order of elements in the result
*/
public function revert()
{
$this->order = $this->order == 'ASC' ? 'DESC' : 'ASC';
if (empty($this->raw_data)) {
return;
}
$data = $this->get();
$data = array_reverse($data);
$this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
$this->meta['pos'] = [];
}
/**
* Check if the given message ID exists in the object
*
* @param int $msgid Message ID
* @param bool $get_index When enabled element's index will be returned.
* Elements are indexed starting with 0
*
* @return mixed False if message ID doesn't exist, True if exists or
* index of the element if $get_index=true
*/
public function exists($msgid, $get_index = false)
{
if (empty($this->raw_data)) {
return false;
}
$msgid = (int) $msgid;
$begin = implode('|', ['^', preg_quote(self::SEPARATOR_ELEMENT, '/')]);
$end = implode('|', ['$', preg_quote(self::SEPARATOR_ELEMENT, '/')]);
if (preg_match("/($begin)$msgid($end)/", $this->raw_data, $m,
$get_index ? PREG_OFFSET_CAPTURE : 0)
) {
if ($get_index) {
$idx = 0;
if (!empty($m[0][1])) {
$idx = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT, 0, $m[0][1]);
}
// cache position of this element, so we can use it in get_element()
$this->meta['pos'][$idx] = (int)$m[0][1];
return $idx;
}
return true;
}
return false;
}
/**
* Return all messages in the result.
*
* @return array List of message IDs
*/
public function get()
{
if (empty($this->raw_data)) {
return [];
}
return explode(self::SEPARATOR_ELEMENT, $this->raw_data);
}
/**
* Return all messages in the result.
*
* @return array List of message IDs
*/
public function get_compressed()
{
if (empty($this->raw_data)) {
return '';
}
return rcube_imap_generic::compressMessageSet($this->get());
}
/**
* Return result element at specified index
*
* @param int|string $index Element's index or "FIRST" or "LAST"
*
* @return int|null Element value
*/
public function get_element($index)
{
if (empty($this->raw_data)) {
return null;
}
$count = $this->count();
// first element
if ($index === 0 || $index === '0' || $index === 'FIRST') {
$pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT);
if ($pos === false) {
$result = (int) $this->raw_data;
}
else {
$result = (int) substr($this->raw_data, 0, $pos);
}
return $result;
}
// last element
if ($index === 'LAST' || $index == $count-1) {
$pos = strrpos($this->raw_data, self::SEPARATOR_ELEMENT);
if ($pos === false) {
$result = (int) $this->raw_data;
}
else {
$result = (int) substr($this->raw_data, $pos);
}
return $result;
}
// do we know the position of the element or the neighbour of it?
if (!empty($this->meta['pos'])) {
if (isset($this->meta['pos'][$index])) {
$pos = $this->meta['pos'][$index];
}
else if (isset($this->meta['pos'][$index-1])) {
$pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT,
$this->meta['pos'][$index-1] + 1);
}
else if (isset($this->meta['pos'][$index+1])) {
$pos = strrpos($this->raw_data, self::SEPARATOR_ELEMENT,
$this->meta['pos'][$index+1] - $this->length() - 1);
}
if (isset($pos) && preg_match('/([0-9]+)/', $this->raw_data, $m, 0, $pos)) {
return (int) $m[1];
}
}
// Finally use less effective method
$data = explode(self::SEPARATOR_ELEMENT, $this->raw_data);
return (int) $data[$index];
}
/**
* Returns response parameters, e.g. ESEARCH's MIN/MAX/COUNT/ALL/MODSEQ
* or internal data e.g. MAILBOX, ORDER
*
* @param ?string $param Parameter name
*
* @return array|string Response parameters or parameter value
*/
public function get_parameters($param = null)
{
$params = $this->params;
$params['MAILBOX'] = $this->mailbox;
$params['ORDER'] = $this->order;
if ($param !== null) {
return $params[$param] ?? null;
}
return $params;
}
/**
* Returns length of internal data representation
*
* @return int Data length
*/
protected function length()
{
if (!isset($this->meta['length'])) {
$this->meta['length'] = strlen($this->raw_data);
}
return $this->meta['length'];
}
}
@@ -0,0 +1,699 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| THREAD response handler |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <[email protected]> |
| Author: Aleksander Machniak <[email protected]> |
+-----------------------------------------------------------------------+
*/
/**
* Class for accessing IMAP's THREAD result
*
* @package Framework
* @subpackage Storage
*/
class rcube_result_thread
{
public $incomplete = false;
protected $raw_data;
protected $mailbox;
protected $meta = [];
protected $order = 'ASC';
const SEPARATOR_ELEMENT = ' ';
const SEPARATOR_ITEM = '~';
const SEPARATOR_LEVEL = ':';
/**
* Object constructor.
*/
public function __construct($mailbox = null, $data = null)
{
$this->mailbox = $mailbox;
$this->init($data);
}
/**
* Initializes object with IMAP command response
*
* @param string $data IMAP response string
*/
public function init($data = null)
{
$this->meta = [];
$data = explode('*', (string) $data);
// ...skip unilateral untagged server responses
for ($i = 0, $len = count($data); $i < $len; $i++) {
if (preg_match('/^ THREAD/i', $data[$i])) {
// valid response, initialize raw_data for is_error()
$this->raw_data = '';
$data[$i] = substr($data[$i], 7);
break;
}
unset($data[$i]);
}
if (empty($data)) {
return;
}
$data = array_shift($data);
$data = trim($data);
$data = preg_replace('/[\r\n]/', '', $data);
$data = preg_replace('/\s+/', ' ', $data);
$this->raw_data = empty($data) ? '' : $this->parse_thread($data);
}
/**
* Checks the result from IMAP command
*
* @return bool True if the result is an error, False otherwise
*/
public function is_error()
{
return $this->raw_data === null;
}
/**
* Checks if the result is empty
*
* @return bool True if the result is empty, False otherwise
*/
public function is_empty()
{
return empty($this->raw_data);
}
/**
* Returns number of elements (threads) in the result
*
* @return int Number of elements
*/
public function count()
{
if (isset($this->meta['count'])) {
return $this->meta['count'];
}
if (empty($this->raw_data)) {
$this->meta['count'] = 0;
}
else {
$this->meta['count'] = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT);
}
if (!$this->meta['count']) {
$this->meta['messages'] = 0;
}
return $this->meta['count'];
}
/**
* Returns number of all messages in the result
*
* @return int Number of elements
*/
public function count_messages()
{
if (isset($this->meta['messages'])) {
return $this->meta['messages'];
}
if (empty($this->raw_data)) {
$this->meta['messages'] = 0;
}
else {
$this->meta['messages'] = 1
+ substr_count($this->raw_data, self::SEPARATOR_ELEMENT)
+ substr_count($this->raw_data, self::SEPARATOR_ITEM);
}
if ($this->meta['messages'] == 0 || $this->meta['messages'] == 1) {
$this->meta['count'] = $this->meta['messages'];
}
return $this->meta['messages'];
}
/**
* Returns maximum message identifier in the result
*
* @return int|null Maximum message identifier
*/
public function max()
{
if ($this->is_empty()) {
return null;
}
if (!isset($this->meta['max'])) {
$this->meta['max'] = (int) @max($this->get());
}
return $this->meta['max'];
}
/**
* Returns minimum message identifier in the result
*
* @return int|null Minimum message identifier
*/
public function min()
{
if ($this->is_empty()) {
return null;
}
if (!isset($this->meta['min'])) {
$this->meta['min'] = (int) @min($this->get());
}
return $this->meta['min'];
}
/**
* Slices data set.
*
* @param int $offset Offset (as for PHP's array_slice())
* @param int $length Number of elements (as for PHP's array_slice())
*/
public function slice($offset, $length)
{
$data = explode(self::SEPARATOR_ELEMENT, $this->raw_data);
$data = array_slice($data, $offset, $length);
$this->meta = [];
$this->meta['count'] = count($data);
$this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
}
/**
* Filters data set. Removes threads not listed in $roots list.
*
* @param array $roots List of IDs of thread roots.
*/
public function filter($roots)
{
$datalen = strlen($this->raw_data);
$roots = array_flip($roots);
$result = '';
$start = 0;
$this->meta = ['count' => 0];
while ($start < $datalen
&& (($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)) !== false
|| ($pos = $datalen))
) {
$len = $pos - $start;
$elem = substr($this->raw_data, $start, $len);
$start = $pos + 1;
// extract root message ID
if ($npos = strpos($elem, self::SEPARATOR_ITEM)) {
$root = (int) substr($elem, 0, $npos);
}
else {
$root = $elem;
}
if (isset($roots[$root])) {
$this->meta['count']++;
$result .= self::SEPARATOR_ELEMENT . $elem;
}
}
$this->raw_data = ltrim($result, self::SEPARATOR_ELEMENT);
}
/**
* Reverts order of elements in the result
*/
public function revert()
{
$this->order = $this->order == 'ASC' ? 'DESC' : 'ASC';
if (empty($this->raw_data)) {
return;
}
$data = explode(self::SEPARATOR_ELEMENT, $this->raw_data);
$data = array_reverse($data);
$this->raw_data = implode(self::SEPARATOR_ELEMENT, $data);
$this->meta['pos'] = [];
}
/**
* Check if the given message ID exists in the object
*
* @param int $msgid Message ID
* @param bool $get_index When enabled element's index will be returned.
* Elements are indexed starting with 0
*
* @return bool True on success, False if message ID doesn't exist
*/
public function exists($msgid, $get_index = false)
{
$msgid = (int) $msgid;
$begin = implode('|', [
'^',
preg_quote(self::SEPARATOR_ELEMENT, '/'),
preg_quote(self::SEPARATOR_LEVEL, '/'),
]);
$end = implode('|', [
'$',
preg_quote(self::SEPARATOR_ELEMENT, '/'),
preg_quote(self::SEPARATOR_ITEM, '/'),
]);
if (preg_match("/($begin)$msgid($end)/", $this->raw_data, $m,
$get_index ? PREG_OFFSET_CAPTURE : 0)
) {
if ($get_index) {
$idx = 0;
if ($m[0][1]) {
$idx = substr_count($this->raw_data, self::SEPARATOR_ELEMENT, 0, $m[0][1]+1)
+ substr_count($this->raw_data, self::SEPARATOR_ITEM, 0, $m[0][1]+1);
}
// cache position of this element, so we can use it in get_element()
$this->meta['pos'][$idx] = (int)$m[0][1];
return $idx;
}
return true;
}
return false;
}
/**
* Return IDs of all messages in the result. Threaded data will be flattened.
*
* @return array List of message identifiers
*/
public function get()
{
if (empty($this->raw_data)) {
return [];
}
$regexp = '/(' . preg_quote(self::SEPARATOR_ELEMENT, '/')
. '|' . preg_quote(self::SEPARATOR_ITEM, '/') . '[0-9]+' . preg_quote(self::SEPARATOR_LEVEL, '/')
.')/';
return preg_split($regexp, $this->raw_data);
}
/**
* Return all messages in the result.
*
* @return array List of message identifiers
*/
public function get_compressed()
{
if (empty($this->raw_data)) {
return '';
}
return rcube_imap_generic::compressMessageSet($this->get());
}
/**
* Return result element at specified index (all messages, not roots)
*
* @param int|string $index Element's index or "FIRST" or "LAST"
*
* @return int Element value
*/
public function get_element($index)
{
$count = $this->count();
if (!$count) {
return null;
}
// first element
if ($index === 0 || $index === '0' || $index === 'FIRST') {
preg_match('/^([0-9]+)/', $this->raw_data, $m);
$result = (int) $m[1];
return $result;
}
// last element
if ($index === 'LAST' || $index == $count-1) {
preg_match('/([0-9]+)$/', $this->raw_data, $m);
$result = (int) $m[1];
return $result;
}
// do we know the position of the element or the neighbour of it?
if (!empty($this->meta['pos'])) {
$element = preg_quote(self::SEPARATOR_ELEMENT, '/');
$item = preg_quote(self::SEPARATOR_ITEM, '/') . '[0-9]+' . preg_quote(self::SEPARATOR_LEVEL, '/') .'?';
$regexp = '(' . $element . '|' . $item . ')';
if (isset($this->meta['pos'][$index])) {
if (preg_match('/([0-9]+)/', $this->raw_data, $m, null, $this->meta['pos'][$index])) {
$result = $m[1];
}
}
else if (isset($this->meta['pos'][$index-1])) {
// get chunk of data after previous element
$data = substr($this->raw_data, $this->meta['pos'][$index-1]+1, 50);
$data = preg_replace('/^[0-9]+/', '', $data); // remove UID at $index position
$data = preg_replace("/^$regexp/", '', $data); // remove separator
if (preg_match('/^([0-9]+)/', $data, $m)) {
$result = $m[1];
}
}
else if (isset($this->meta['pos'][$index+1])) {
// get chunk of data before next element
$pos = max(0, $this->meta['pos'][$index+1] - 50);
$len = min(50, $this->meta['pos'][$index+1]);
$data = substr($this->raw_data, $pos, $len);
$data = preg_replace("/$regexp\$/", '', $data); // remove separator
if (preg_match('/([0-9]+)$/', $data, $m)) {
$result = $m[1];
}
}
if (isset($result)) {
return (int) $result;
}
}
// Finally use less effective method
$data = $this->get();
return $data[$index] ?? null;
}
/**
* Returns response parameters e.g. MAILBOX, ORDER
*
* @param string $param Parameter name
*
* @return array|string Response parameters or parameter value
*/
public function get_parameters($param=null)
{
$params = [
'MAILBOX' => $this->mailbox,
'ORDER' => $this->order,
];
if ($param !== null) {
return $params[$param];
}
return $params;
}
/**
* THREAD=REFS sorting implementation (based on provided index)
*
* @param rcube_result_index $index Sorted message identifiers
*/
public function sort($index)
{
$this->order = $index->get_parameters('ORDER');
if (empty($this->raw_data)) {
return;
}
// when sorting search result it's good to make the index smaller
if ($index->count() != $this->count_messages()) {
$index->filter($this->get());
}
$result = array_fill_keys($index->get(), null);
$datalen = strlen($this->raw_data);
$start = 0;
// Here we're parsing raw_data twice, we want only one big array
// in memory at a time
// Assign roots
while (
($start < $datalen && ($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)))
|| ($start < $datalen && ($pos = $datalen))
) {
$len = $pos - $start;
$elem = substr($this->raw_data, $start, $len);
$start = $pos + 1;
$items = explode(self::SEPARATOR_ITEM, $elem);
$root = (int) array_shift($items);
if ($root) {
$result[$root] = $root;
foreach ($items as $item) {
list($lv, $id) = explode(self::SEPARATOR_LEVEL, $item);
$result[$id] = $root;
}
}
}
// get only unique roots
$result = array_filter($result); // make sure there are no nulls
$result = array_unique($result);
// Re-sort raw data
$result = array_fill_keys($result, null);
$start = 0;
while (
($start < $datalen && ($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)))
|| ($start < $datalen && ($pos = $datalen))
) {
$len = $pos - $start;
$elem = substr($this->raw_data, $start, $len);
$start = $pos + 1;
$npos = strpos($elem, self::SEPARATOR_ITEM);
$root = (int) ($npos ? substr($elem, 0, $npos) : $elem);
$result[$root] = $elem;
}
$this->raw_data = implode(self::SEPARATOR_ELEMENT, $result);
}
/**
* Returns data as tree
*
* @return array Data tree
*/
public function get_tree()
{
$datalen = strlen($this->raw_data);
$result = [];
$start = 0;
while ($start < $datalen
&& (($pos = strpos($this->raw_data, self::SEPARATOR_ELEMENT, $start)) !== false
|| ($pos = $datalen))
) {
$len = $pos - $start;
$elem = substr($this->raw_data, $start, $len);
$items = explode(self::SEPARATOR_ITEM, $elem);
$result[array_shift($items)] = $this->build_thread($items);
$start = $pos + 1;
}
return $result;
}
/**
* Returns thread depth and children data
*
* @return array Thread data
*/
public function get_thread_data()
{
$data = $this->get_tree();
$depth = [];
$children = [];
$this->build_thread_data($data, $depth, $children);
return [$depth, $children];
}
/**
* Creates 'depth' and 'children' arrays from stored thread 'tree' data.
*/
protected function build_thread_data($data, &$depth, &$children, $level = 0)
{
foreach ((array)$data as $key => $val) {
$empty = empty($val) || !is_array($val);
$children[$key] = !$empty;
$depth[$key] = $level;
if (!$empty) {
$this->build_thread_data($val, $depth, $children, $level + 1);
}
}
}
/**
* Converts part of the raw thread into an array
*/
protected function build_thread($items, $level = 1, &$pos = 0)
{
$result = [];
for ($len=count($items); $pos < $len; $pos++) {
list($lv, $id) = explode(self::SEPARATOR_LEVEL, $items[$pos]);
if ($level == $lv) {
$pos++;
$result[$id] = $this->build_thread($items, $level+1, $pos);
}
else {
$pos--;
break;
}
}
return $result;
}
/**
* IMAP THREAD response parser
*/
protected function parse_thread($str, $begin = 0, $end = 0, $depth = 0)
{
// Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about
// 7 times instead :-) See comments on http://uk2.php.net/references and this article:
// http://derickrethans.nl/files/phparch-php-variables-article.pdf
$node = '';
if (!$end) {
$end = strlen($str);
}
// Let's try to store data in max. compacted structure as a string,
// arrays handling is much more expensive
// For the following structure: THREAD (2)(3 6 (4 23)(44 7 96))((11)(12))
// -- 2
// -- 3
// \-- 6
// |-- 4
// | \-- 23
// |
// \-- 44
// \-- 7
// \-- 96
// -- 11
// \-- 12
//
// The output will be: 2 3~1:6~2:4~3:23~2:44~3:7~4:96 11~1:12
// Note: The "11" thread has no root, we use the first message as root
if ($str[$begin] != '(') {
// find next bracket
$stop = $begin + strcspn($str, '()', $begin, $end - $begin);
$messages = explode(' ', trim(substr($str, $begin, $stop - $begin)));
if (empty($messages)) {
return $node;
}
foreach ($messages as $msg) {
if ($msg) {
$node .= ($depth ? self::SEPARATOR_ITEM.$depth.self::SEPARATOR_LEVEL : '').$msg;
if (isset($this->meta['messages'])) {
$this->meta['messages']++;
}
else {
$this->meta['messages'] = 1;
}
$depth++;
}
}
if ($stop < $end) {
$node .= $this->parse_thread($str, $stop, $end, $depth);
}
}
else {
$off = $begin;
while ($off < $end) {
$start = $off;
$off++;
$n = 1;
while ($n > 0) {
$p = strpos($str, ')', $off);
if ($p === false) {
// error, wrong structure, mismatched brackets in IMAP THREAD response
// @TODO: write error to the log or maybe set $this->raw_data = null;
return $node;
}
$p1 = strpos($str, '(', $off);
if ($p1 !== false && $p1 < $p) {
$off = $p1 + 1;
$n++;
}
else {
$off = $p + 1;
$n--;
}
}
// Handle threads with missing parent by using first message as root
if (substr_compare($str, '((', $start, 2) === 0) {
// Extract the current thread, e.g. "((1)(2))"
$thread = substr($str, $start, $off - $start);
// Length of the first token, e.g. "(1)"
$len = strspn($thread, '(0123456789', 1) + 1;
// Extract the token and modify it to look like a thread root
$token = substr($thread, 1, $len);
// Warning: The order is important
$token = str_replace('(', '', $token);
$token = str_replace(' ', ' (', $token);
$token = str_replace(')', ' ', $token);
$thread = substr_replace($thread, $token, 1, $len);
// Parse the thread
$thread = $this->parse_thread($thread, 0, 0, $depth);
}
else {
$thread = $this->parse_thread($str, $start + 1, $off - 1, $depth);
}
if ($thread) {
if (!$depth) {
if ($node) {
$node .= self::SEPARATOR_ELEMENT;
}
}
$node .= $thread;
}
}
}
return $node;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,24 @@
Description of Roundcube Framework 1.6.6 library import into Moodle
We now use the client part of Roundcube Framework as a library in Moodle.
This library is used to receive emails from Moodle.
This library is not used to send emails.
For more information on this version of Roundcube Framework, check out https://github.com/roundcube/roundcubemail/releases/tag/1.6.6
To upgrade this library:
1. Download the latest release of Roundcube Framework (roundcube-framework-xxx-tar.gz) in https://github.com/roundcube/roundcubemail/releases.
2. Extract the contents of the release archive to a temp folder.
3. Copy the following files from the temp folder to the Moodle folder admin/tool/messageinbound/roundcube:
- rcube_charset.php
- rcube_imap_generic.php
- rcube_message_header.php
- rcube_mime.php
- rcube_result_index.php
- rcube_result_thread.php
- rcube_utils.php
4. Find and replace all array_first() calls with array_shift() in the following files:
- rcube_imap_generic.php
- rcube_result_index.php
- rcube_result_thread.php
5. Update admin/tool/messageinbound/thirdpartylibs.xml.
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<libraries>
<library>
<location>roundcube</location>
<name>Roundcube Framework</name>
<license>GPL</license>
<licenseversion>3.0+</licenseversion>
<version>1.6.6</version>
<repository>https://github.com/roundcube/roundcubemail</repository>
<copyrights>
<copyright>The Roundcube Dev Team</copyright>
</copyrights>
</library>
</libraries>
-1
View File
@@ -88,7 +88,6 @@ class core_component {
protected static $filestomap = ['lib.php', 'settings.php'];
/** @var array associative array of PSR-0 namespaces and corresponding paths. */
protected static $psr0namespaces = [
'Horde' => 'lib/horde/framework/Horde',
'Mustache' => 'lib/mustache/src/Mustache',
'CFPropertyList' => 'lib/plist/classes/CFPropertyList',
];
-147
View File
@@ -1,147 +0,0 @@
<?php
/**
* The Horde_Array:: class provides various methods for array manipulation.
*
* Copyright 2003-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael Slusarz <[email protected]>
* @author Marko Djukic <[email protected]>
* @author Jan Schneider <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Util
*/
class Horde_Array
{
/**
* Sorts an array on a specified key. If the key does not exist,
* defaults to the first key of the array.
*
* @param array &$array The array to be sorted, passed by reference.
* @param string $key The key by which to sort. If not specified then
* the first key is used.
* @param integer $dir Sort direction:
* 0 = ascending (default)
* 1 = descending
* @param boolean $assoc Keep key value association?
*/
public static function arraySort(array &$array, $key = null, $dir = 0,
$assoc = true)
{
/* Return if the array is empty. */
if (empty($array)) {
return;
}
/* If no key to sort by is specified, use the first key of the
* first element. */
if (is_null($key)) {
$keys = array_keys(reset($array));
$key = array_shift($keys);
}
/* Call the appropriate sort function. */
$helper = new Horde_Array_Sort_Helper();
$helper->key = $key;
$function = $dir ? 'reverseCompare' : 'compare';
if ($assoc) {
uasort($array, array($helper, $function));
} else {
usort($array, array($helper, $function));
}
}
/**
* Given an HTML type array field "example[key1][key2][key3]" breaks up
* the keys so that they could be used to reference a regular PHP array.
*
* @param string $field The field name to be examined.
* @param string &$base Will be set to the base element.
* @param array &$keys Will be set to the list of keys.
*
* @return boolean True on sucess, false on error.
*/
public static function getArrayParts($field, &$base, &$keys)
{
if (!preg_match('|([^\[]*)((\[[^\[\]]*\])+)|', $field, $matches)) {
return false;
}
$base = $matches[1];
$keys = explode('][', $matches[2]);
$keys[0] = substr($keys[0], 1);
$keys[count($keys) - 1] = substr($keys[count($keys) - 1], 0, strlen($keys[count($keys) - 1]) - 1);
return true;
}
/**
* Using an array of keys iterate through the array following the
* keys to find the final key value. If a value is passed then set
* that value.
*
* @param array &$array The array to be used.
* @param array &$keys The key path to follow as an array.
* @param array $value If set the target element will have this value set
* to it.
*
* @return mixed The final value of the key path.
*/
public static function getElement(&$array, array &$keys, $value = null)
{
if (count($keys)) {
$key = array_shift($keys);
return isset($array[$key])
? self::getElement($array[$key], $keys, $value)
: false;
}
if (!is_null($value)) {
$array = $value;
}
return $array;
}
/**
* Returns a rectangle of a two-dimensional array.
*
* @param array $array The array to extract the rectangle from.
* @param integer $row The start row of the rectangle.
* @param integer $col The start column of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $width The width of the rectangle.
*
* @return array The extracted rectangle.
*/
public static function getRectangle(array $array, $row, $col, $height,
$width)
{
$rec = array();
for ($y = $row; $y < $row + $height; $y++) {
$rec[] = array_slice($array[$y], $col, $width);
}
return $rec;
}
/**
* Given an array, returns an associative array with each element key
* derived from its value.
* For example:
* array(0 => 'foo', 1 => 'bar')
* would become:
* array('foo' => 'foo', 'bar' => 'bar')
*
* @param array $array An array of values.
*
* @return array An array with keys the same as values.
*/
public static function valuesToKeys(array $array)
{
return $array
? array_combine($array, $array)
: array();
}
}
@@ -1,77 +0,0 @@
<?php
/**
* Helper class for sorting arrays on arbitrary criteria for usort/uasort.
*
* Copyright 2003-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Marko Djukic <[email protected]>
* @author Jan Schneider <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Util
*/
class Horde_Array_Sort_Helper
{
/**
* The array key to sort by.
*
* @var string
*/
public $key;
/**
* Compare two associative arrays by the array key defined in self::$key.
*
* @param array $a
* @param array $b
*/
public function compare($a, $b)
{
return strcoll(Horde_String::lower($a[$this->key], true, 'UTF-8'), Horde_String::lower($b[$this->key], true, 'UTF-8'));
}
/**
* Compare, in reverse order, two associative arrays by the array key
* defined in self::$key.
*
* @param scalar $a TODO
* @param scalar $b TODO
*
* @return TODO
*/
public function reverseCompare($a, $b)
{
return strcoll(Horde_String::lower($b[$this->key], true, 'UTF-8'), Horde_String::lower($a[$this->key], true, 'UTF-8'));
}
/**
* Compare array keys case insensitively for uksort.
*
* @param scalar $a TODO
* @param scalar $b TODO
*
* @return TODO
*/
public function compareKeys($a, $b)
{
return strcoll(Horde_String::lower($a, true, 'UTF-8'), Horde_String::lower($b, true, 'UTF-8'));
}
/**
* Compare, in reverse order, array keys case insensitively for uksort.
*
* @param scalar $a TODO
* @param scalar $b TODO
*
* @return TODO
*/
public function reverseCompareKeys($a, $b)
{
return strcoll(Horde_String::lower($b, true, 'UTF-8'), Horde_String::lower($a, true, 'UTF-8'));
}
}
@@ -1,178 +0,0 @@
<?php
/**
* Copyright 2005-2008 Matthew Fonda <[email protected]>
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Matthew Fonda <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Provides blowfish encryption/decryption, with or without a secret key,
* for PHP strings.
*
* @author Matthew Fonda <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2008 Matthew Fonda
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*
* @property string $cipher The cipher block mode ('ecb' or 'cbc').
* @property string $key The encryption key in use.
* @property mixed $iv The initialization vector (false if using 'ecb').
*/
class Horde_Crypt_Blowfish
{
// Constants for 'ignore' parameter of constructor.
const IGNORE_OPENSSL = 1;
const IGNORE_MCRYPT = 2;
// Block size for Blowfish
const BLOCKSIZE = 8;
// Maximum key size for Blowfish
const MAXKEYSIZE = 56;
// IV Length for CBC
const IV_LENGTH = 8;
/**
* Blowfish crypt driver.
*
* @var Horde_Crypt_Blowfish_Base
*/
protected $_crypt;
/**
* Constructor.
*
* @param string $key Encryption key.
* @param array $opts Additional options:
* - cipher: (string) Either 'ecb' or 'cbc'.
* - ignore: (integer) A mask of drivers to ignore (IGNORE_* constants).
* - iv: (string) IV to use.
*/
public function __construct($key, array $opts = array())
{
$opts = array_merge(array(
'cipher' => 'ecb',
'ignore' => 0,
'iv' => null
), $opts);
if (!($opts['ignore'] & self::IGNORE_OPENSSL) &&
Horde_Crypt_Blowfish_Openssl::supported()) {
$this->_crypt = new Horde_Crypt_Blowfish_Openssl($opts['cipher']);
} elseif (!($opts['ignore'] & self::IGNORE_MCRYPT) &&
Horde_Crypt_Blowfish_Mcrypt::supported()) {
$this->_crypt = new Horde_Crypt_Blowfish_Mcrypt($opts['cipher']);
} else {
$this->_crypt = new Horde_Crypt_Blowfish_Php($opts['cipher']);
}
$this->setKey($key, $opts['iv']);
}
/**
*/
public function __get($name)
{
switch ($name) {
case 'cipher':
case 'key':
case 'iv':
return $this->_crypt->$name;
}
}
/**
* Encrypts a string.
*
* @param string $text The string to encrypt.
*
* @return string The ciphertext.
* @throws Horde_Crypt_Blowfish_Exception
*/
public function encrypt($text)
{
if (!is_string($text)) {
throw new Horde_Crypt_Blowfish_Exception('Data to encrypt must be a string.');
}
return $this->_crypt->encrypt($text);
}
/**
* Decrypts a string.
*
* @param string $text The string to decrypt.
*
* @return string The plaintext.
* @throws Horde_Crypt_Blowfish_Exception
*/
public function decrypt($text)
{
if (!is_string($text)) {
throw new Horde_Crypt_Blowfish_Exception('Data to decrypt must be a string.');
}
return $this->_crypt->decrypt($text);
}
/**
* Sets the secret key.
*
* The key must be non-zero, and less than or equal to MAXKEYSIZE
* characters (bytes) in length.
*
* @param string $key Key must be non-empty and less than MAXKEYSIZE
* bytes in length.
* @param string $iv The initialization vector to use. Only needed for
* 'cbc' cipher. If null, an IV is automatically
* generated.
*
* @throws Horde_Crypt_Blowfish_Exception
*/
public function setKey($key, $iv = null)
{
if (!is_string($key)) {
throw new Horde_Crypt_Blowfish_Exception('Encryption key must be a string.');
}
$len = strlen($key);
if (($len > self::MAXKEYSIZE) || ($len == 0)) {
throw new Horde_Crypt_Blowfish_Exception(sprintf('Encryption key must be less than %d characters (bytes) and non-zero. Supplied key length: %d', self::MAXKEYSIZE, $len));
}
$this->_crypt->key = $key;
switch ($this->_crypt->cipher) {
case 'cbc':
if (is_null($iv)) {
if (is_null($this->iv)) {
$this->_crypt->setIv();
}
} else {
$iv = substr($iv, 0, self::IV_LENGTH);
if (($len = strlen($iv)) < self::IV_LENGTH) {
$iv .= str_repeat(chr(0), self::IV_LENGTH - $len);
}
$this->_crypt->setIv($iv);
}
break;
case 'ecb':
$this->iv = false;
break;
}
}
}
@@ -1,128 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Abstract base driver class for blowfish encryption.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
abstract class Horde_Crypt_Blowfish_Base
{
/**
* Cipher method.
*
* @var string
*/
public $cipher;
/**
* Initialization vector.
*
* @var string
*/
public $iv = null;
/**
* Encryption key.
*
* @var string
*/
public $key;
/**
* Is this driver supported on this system?
*
* @return boolean True if supported.
*/
public static function supported()
{
return true;
}
/**
* Constructor.
*
* @param string $cipher Either 'ecb' or 'cbc'.
*/
public function __construct($cipher)
{
$this->cipher = $cipher;
}
/**
* Encrypts a string.
*
* @param string $text The string to encrypt.
*
* @return string The ciphertext.
* @throws Horde_Crypt_Blowfish_Exception
*/
abstract public function encrypt($text);
/**
* Decrypts a string.
*
* @param string $text The string to encrypt.
*
* @return string The ciphertext.
* @throws Horde_Crypt_Blowfish_Exception
*/
abstract public function decrypt($text);
/**
* Sets the initialization vector (required for CBC mode).
*
* @param string $iv Initialization vector.
*/
public function setIv($iv = null)
{
$this->iv = is_null($iv)
? substr(new Horde_Support_Randomid(), 0, 8)
: $iv;
}
/**
* Pad text to match blocksize length.
*
* @param string $text Unpadded text.
* @param boolean $ignore Don't pad if already at blocksize length.
*
* @return string Padded text.
*/
protected function _pad($text, $ignore = false)
{
$blocksize = Horde_Crypt_Blowfish::BLOCKSIZE;
$padding = $blocksize - (strlen($text) % $blocksize);
return ($ignore && ($padding == $blocksize))
? $text
: $text . str_repeat(chr($padding), $padding);
}
/**
* Unpad text from blocksize boundary.
*
* @param string $text Padded text.
*
* @return string Unpadded text.
*/
protected function _unpad($text)
{
return substr($text, 0, ord(substr($text, -1)) * -1);
}
}
@@ -1,25 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Exception object for the Horde_Crypt_Blowfish package.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
class Horde_Crypt_Blowfish_Exception extends Horde_Exception_Wrapped
{
}
@@ -1,87 +0,0 @@
<?php
/**
* Copyright 2005-2008 Matthew Fonda <[email protected]>
* Copyright 2008 Philippe Jausions <[email protected]>
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Mcrypt driver for blowfish encryption.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2008 Matthew Fonda
* @copyright 2008 Philippe Jausions
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
class Horde_Crypt_Blowfish_Mcrypt extends Horde_Crypt_Blowfish_Base
{
/**
* Mcrypt resource.
*
* @var resource
*/
private $_mcrypt;
/**
*/
public static function supported()
{
return PHP_VERSION_ID < 70100 && extension_loaded('mcrypt');
}
/**
*/
public function __construct($cipher)
{
parent::__construct($cipher);
$this->_mcrypt = mcrypt_module_open(MCRYPT_BLOWFISH, '', $cipher, '');
}
/**
*/
public function encrypt($text)
{
mcrypt_generic_init($this->_mcrypt, $this->key, empty($this->iv) ? str_repeat('0', Horde_Crypt_Blowfish::IV_LENGTH) : $this->iv);
$out = mcrypt_generic($this->_mcrypt, $this->_pad($text));
mcrypt_generic_deinit($this->_mcrypt);
return $out;
}
/**
*/
public function decrypt($text)
{
mcrypt_generic_init($this->_mcrypt, $this->key, empty($this->iv) ? str_repeat('0', Horde_Crypt_Blowfish::IV_LENGTH) : $this->iv);
$out = mdecrypt_generic($this->_mcrypt, $this->_pad($text, true));
mcrypt_generic_deinit($this->_mcrypt);
return $this->_unpad($out);
}
/**
*/
public function setIv($iv = null)
{
$this->iv = is_null($iv)
? mcrypt_create_iv(Horde_Crypt_Blowfish::IV_LENGTH, MCRYPT_RAND)
: $iv;
}
}
@@ -1,61 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Openssl driver for blowfish encryption.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
class Horde_Crypt_Blowfish_Openssl extends Horde_Crypt_Blowfish_Base
{
/**
*/
public static function supported()
{
if (extension_loaded('openssl')) {
$ciphers = openssl_get_cipher_methods();
return in_array('bf-ecb', $ciphers) && in_array('bf-cbc', $ciphers);
}
return false;
}
/**
*/
public function encrypt($text)
{
if (PHP_VERSION_ID <= 50302) {
return @openssl_encrypt($text, 'bf-' . $this->cipher, $this->key, true);
} elseif (PHP_VERSION_ID == 50303) {
// Need to mask error output, since an invalid warning message was
// issued prior to 5.3.4 for empty IVs in ECB mode.
return @openssl_encrypt($text, 'bf-' . $this->cipher, $this->key, true, strval($this->iv));
}
return openssl_encrypt($text, 'bf-' . $this->cipher, $this->key, true, strval($this->iv));
}
/**
*/
public function decrypt($text)
{
return (PHP_VERSION_ID <= 50302)
? openssl_decrypt($text, 'bf-' . $this->cipher, $this->key, true)
: openssl_decrypt($text, 'bf-' . $this->cipher, $this->key, true, strval($this->iv));
}
}
@@ -1,128 +0,0 @@
<?php
/**
* Copyright 2015-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* PBKDF2 (Password-Based Key Derivation Function 2) implementation (RFC
* 2898; PKCS #5 v2.0).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
* @link https://defuse.ca/php-pbkdf2.htm pbkdf2 code released to the
* public domain.
*/
class Horde_Crypt_Blowfish_Pbkdf2
{
/**
* Hash algorithm used to create key.
*
* @var string
*/
public $hashAlgo;
/**
* Number of iterations to use.
*
* @var integer
*/
public $iterations;
/**
* Salt.
*
* @var string
*/
public $salt;
/**
* The derived key.
*
* @var string
*/
protected $_key;
/**
* Constructor.
*
* @param string $pass The password.
* @param string $key_length Length of the derived key (in bytes).
* @param array $opts Additional options:
* - algo: (string) Hash algorithm.
* - i_count: (integer) Iteration count.
* - salt: (string) The salt to use.
*/
public function __construct($pass, $key_length, array $opts = array())
{
$this->iterations = isset($opts['i_count'])
? $opts['i_count']
: 16384;
if (($key_length <= 0) || ($this->iterations <= 0)) {
throw new InvalidArgumentException('Invalid arguments');
}
$this->hashAlgo = isset($opts['algo'])
? $opts['algo']
: 'SHA256';
/* Nice to have, but salt does not need to be cryptographically
* secure random value. */
$this->salt = isset($opts['salt'])
? $opts['salt']
: (function_exists('openssl_random_pseudo_bytes')
? openssl_random_pseudo_bytes($key_length)
: substr(hash('sha512', new Horde_Support_Randomid(), true), 0, $key_length));
if (function_exists('hash_pbkdf2')) {
$this->_key = hash_pbkdf2(
$this->hashAlgo,
$pass,
$this->salt,
$this->iterations,
$key_length,
true
);
return;
}
$hash_length = strlen(hash($this->hashAlgo, '', true));
$block_count = ceil($key_length / $hash_length);
$hash = '';
for ($i = 1; $i <= $block_count; ++$i) {
// $i encoded as 4 bytes, big endian.
$last = $this->salt . pack('N', $i);
for ($j = 0; $j < $this->iterations; $j++) {
$last = hash_hmac($this->hashAlgo, $last, $pass, true);
if ($j) {
$xorsum ^= $last;
} else {
$xorsum = $last;
}
}
$hash .= $xorsum;
}
$this->_key = substr($hash, 0, $key_length);
}
/**
*/
public function __toString()
{
return $this->_key;
}
}
@@ -1,75 +0,0 @@
<?php
/**
* Copyright 2005-2008 Matthew Fonda <[email protected]>
* Copyright 2008 Philippe Jausions <[email protected]>
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Native PHP driver for blowfish encryption.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2008 Matthew Fonda
* @copyright 2008 Philippe Jausions
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
class Horde_Crypt_Blowfish_Php extends Horde_Crypt_Blowfish_Base
{
/**
* Subclass object.
*
* @var Horde_Crypt_Blowfish_Php_Base
*/
protected $_ob;
/**
*/
public function encrypt($text)
{
$this->_init();
return $this->_ob->encrypt($this->_pad($text), $this->iv);
}
/**
*/
public function decrypt($text)
{
$this->_init();
return $this->_unpad($this->_ob->decrypt($this->_pad($text, true), $this->iv));
}
/**
* Initialize the subclass.
*/
protected function _init()
{
if (!isset($this->_ob) ||
($this->_ob->md5 != hash('md5', $this->key))) {
switch ($this->cipher) {
case 'cbc':
$this->_ob = new Horde_Crypt_Blowfish_Php_Cbc($this->key);
break;
case 'ecb':
$this->_ob = new Horde_Crypt_Blowfish_Php_Ecb($this->key);
break;
}
}
}
}
@@ -1,459 +0,0 @@
<?php
/**
* Copyright 2005-2008 Matthew Fonda <[email protected]>
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Matthew Fonda <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* Base subclass for the PHP driver.
*
* @author Matthew Fonda <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2008 Matthew Fonda
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
abstract class Horde_Crypt_Blowfish_Php_Base
{
/**
* MD5 sum of the key used.
*
* @var string
*/
public $md5;
/**
* P-Array contains 18 32-bit subkeys.
*
* @var array
*/
protected $_P = array(
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B
);
/**
* Array of four S-Blocks each containing 256 32-bit entries.
*
* @var array
*/
protected $_S = array(
array(
0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
), array(
0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
), array(
0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
), array(
0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
)
);
/**
* Constructor.
*
* @param string $key Encrpytion key.
*/
public function __construct($key)
{
$data = $datal = $datar = $k = 0;
$len = strlen($key);
for ($i = 0; $i < 18; ++$i) {
$data = 0;
for ($j = 4; $j > 0; --$j) {
$data = $data << 8 | ord($key[$k]);
$k = ($k + 1) % $len;
}
$this->_P[$i] ^= $data;
}
for ($i = 0; $i <= 16; $i += 2) {
$this->_encipher($datal, $datar);
$this->_P[$i] = $datal;
$this->_P[$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[0][$i] = $datal;
$this->_S[0][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[1][$i] = $datal;
$this->_S[1][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[2][$i] = $datal;
$this->_S[2][$i+1] = $datar;
}
for ($i = 0; $i < 256; $i += 2) {
$this->_encipher($datal, $datar);
$this->_S[3][$i] = $datal;
$this->_S[3][$i+1] = $datar;
}
$this->md5 = hash('md5', $key);
}
/**
*/
abstract public function encrypt($text, $iv);
/**
*/
abstract public function decrypt($text, $iv);
/**
* Workaround for XOR on certain systems.
*
* @param integer|float $l
* @param integer|float $r
*
* @return float
*/
protected function _binxor($l, $r)
{
$x = (($l < 0) ? (float)($l + 4294967296) : (float)$l)
^ (($r < 0) ? (float)($r + 4294967296) : (float)$r);
return (float)(($x < 0) ? $x + 4294967296 : $x);
}
/**
* Enciphers a single 64-bit block.
*
* @param int &$Xl
* @param int &$Xr
*/
protected function _encipher(&$Xl, &$Xr)
{
if ($Xl < 0) {
$Xl += 4294967296;
}
if ($Xr < 0) {
$Xr += 4294967296;
}
for ($i = 0; $i < 16; ++$i) {
$temp = $Xl ^ $this->_P[$i];
if ($temp < 0) {
$temp += 4294967296;
}
$Xl = fmod((fmod($this->_S[0][($temp >> 24) & 255]
+ $this->_S[1][($temp >> 16) & 255], 4294967296)
^ $this->_S[2][($temp >> 8) & 255])
+ $this->_S[3][$temp & 255], 4294967296) ^ $Xr;
$Xr = $temp;
}
$Xr = $this->_binxor($Xl, $this->_P[16]);
$Xl = $this->_binxor($temp, $this->_P[17]);
}
/**
* Deciphers a single 64-bit block.
*
* @param int &$Xl
* @param int &$Xr
*/
protected function _decipher(&$Xl, &$Xr)
{
if ($Xl < 0) {
$Xl += 4294967296;
}
if ($Xr < 0) {
$Xr += 4294967296;
}
for ($i = 17; $i > 1; --$i) {
$temp = $Xl ^ $this->_P[$i];
if ($temp < 0) {
$temp += 4294967296;
}
$Xl = fmod((fmod($this->_S[0][($temp >> 24) & 255]
+ $this->_S[1][($temp >> 16) & 255], 4294967296)
^ $this->_S[2][($temp >> 8) & 255])
+ $this->_S[3][$temp & 255], 4294967296) ^ $Xr;
$Xr = $temp;
}
$Xr = $this->_binxor($Xl, $this->_P[1]);
$Xl = $this->_binxor($temp, $this->_P[0]);
}
}
@@ -1,73 +0,0 @@
<?php
/**
* Copyright 2005-2008 Matthew Fonda <[email protected]>
* Copyright 2008 Philippe Jausions <[email protected]>
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* PHP implementation of the Blowfish algorithm in CBC mode.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2008 Matthew Fonda
* @copyright 2008 Philippe Jausions
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
class Horde_Crypt_Blowfish_Php_Cbc extends Horde_Crypt_Blowfish_Php_Base
{
/**
*/
public function encrypt($text, $iv)
{
$cipherText = '';
$len = strlen($text);
list(, $Xl, $Xr) = unpack('N2', substr($text, 0, 8) ^ $iv);
$this->_encipher($Xl, $Xr);
$cipherText .= pack('N2', $Xl, $Xr);
for ($i = 8; $i < $len; $i += 8) {
list(, $Xl, $Xr) = unpack('N2', substr($text, $i, 8) ^ substr($cipherText, $i - 8, 8));
$this->_encipher($Xl, $Xr);
$cipherText .= pack('N2', $Xl, $Xr);
}
return $cipherText;
}
/**
*/
public function decrypt($text, $iv)
{
$plainText = '';
$len = strlen($text);
list(, $Xl, $Xr) = unpack('N2', substr($text, 0, 8));
$this->_decipher($Xl, $Xr);
$plainText .= (pack('N2', $Xl, $Xr) ^ $iv);
for ($i = 8; $i < $len; $i += 8) {
list(, $Xl, $Xr) = unpack('N2', substr($text, $i, 8));
$this->_decipher($Xl, $Xr);
$plainText .= (pack('N2', $Xl, $Xr) ^ substr($text, $i - 8, 8));
}
return $plainText;
}
}
@@ -1,65 +0,0 @@
<?php
/**
* Copyright 2005-2008 Matthew Fonda <[email protected]>
* Copyright 2008 Philippe Jausions <[email protected]>
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
/**
* PHP implementation of the Blowfish algorithm in ECB mode.
*
* @author Matthew Fonda <[email protected]>
* @author Philippe Jausions <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2008 Matthew Fonda
* @copyright 2008 Philippe Jausions
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Crypt_Blowfish
*/
class Horde_Crypt_Blowfish_Php_Ecb extends Horde_Crypt_Blowfish_Php_Base
{
/**
*/
public function encrypt($text, $iv)
{
$cipherText = '';
$len = strlen($text);
for ($i = 0; $i < $len; $i += 8) {
list(, $Xl, $Xr) = unpack('N2', substr($text, $i, 8));
$this->_encipher($Xl, $Xr);
$cipherText .= pack('N2', $Xl, $Xr);
}
return $cipherText;
}
/**
*/
public function decrypt($text, $iv)
{
$plainText = '';
$len = strlen($text);
for ($i = 0; $i < $len; $i += 8) {
list(, $Xl, $Xr) = unpack('N2', substr($text, $i, 8));
$this->_decipher($Xl, $Xr);
$plainText .= pack('N2', $Xl, $Xr);
}
return $plainText;
}
}
-341
View File
@@ -1,341 +0,0 @@
<?php
/**
* Copyright 2010-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2010-2017 Horde LLC
* @package Util
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/
/**
* Parse DOM data from HTML strings.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2010-2017 Horde LLC
* @package Util
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/
class Horde_Domhtml implements Iterator
{
/**
* DOM object.
*
* @var DOMDocument
*/
public $dom;
/**
* Iterator status.
*
* @var array
*/
protected $_iterator = null;
/**
* Original charset of data.
*
* @var string
*/
protected $_origCharset;
/**
* Encoding tag added to beginning of output.
*
* @var string
*/
protected $_xmlencoding = '';
/**
* Constructor.
*
* @param string $text The text of the HTML document.
* @param string $charset The charset of the HTML document.
*
* @throws Exception
*/
public function __construct($text, $charset = null)
{
if (!extension_loaded('dom')) {
throw new Exception('DOM extension is not available.');
}
// Bug #9616: Make sure we have valid HTML input.
if (!strlen($text)) {
$text = '<html></html>';
}
$old_error = libxml_use_internal_errors(true);
$this->dom = new DOMDocument();
if (is_null($charset)) {
/* If no charset given, charset is whatever libxml tells us the
* encoding should be defaulting to 'iso-8859-1'. */
$this->_loadHTML($text);
$this->_origCharset = $this->dom->encoding
? $this->dom->encoding
: 'iso-8859-1';
} else {
/* Convert/try with UTF-8 first. */
$this->_origCharset = Horde_String::lower($charset);
$this->_xmlencoding = '<?xml encoding="UTF-8"?>';
$this->_loadHTML(
$this->_xmlencoding . Horde_String::convertCharset($text, $charset, 'UTF-8')
);
if ($this->dom->encoding &&
(Horde_String::lower($this->dom->encoding) != 'utf-8')) {
/* Convert charset to what the HTML document says it SHOULD
* be. */
$this->_loadHTML(
Horde_String::convertCharset($text, $charset, $this->dom->encoding)
);
$this->_xmlencoding = '';
}
}
if ($old_error) {
libxml_use_internal_errors(false);
}
/* Sanity checking: make sure we have the documentElement object. */
if (!$this->dom->documentElement) {
$this->dom->appendChild($this->dom->createElement('html'));
}
/* Remove old charset information. */
$xpath = new DOMXPath($this->dom);
$domlist = $xpath->query('/html/head/meta[@http-equiv="content-type"]');
for ($i = $domlist->length; $i > 0; --$i) {
$meta = $domlist->item($i - 1);
$meta->parentNode->removeChild($meta);
}
}
/**
* Returns the HEAD element, or creates one if it doesn't exist.
*
* @return DOMElement HEAD element.
*/
public function getHead()
{
$head = $this->dom->getElementsByTagName('head');
if ($head->length) {
return $head->item(0);
}
$headelt = $this->dom->createElement('head');
$this->dom->documentElement->insertBefore($headelt, $this->dom->documentElement->firstChild);
return $headelt;
}
/**
* Returns the BODY element, or creates one if it doesn't exist.
*
* @since 2.2.0
*
* @return DOMElement BODY element.
*/
public function getBody()
{
$body = $this->dom->getElementsByTagName('body');
if ($body->length) {
return $body->item(0);
}
$bodyelt = $this->dom->createElement('body');
$this->dom->documentElement->appendChild($bodyelt);
return $bodyelt;
}
/**
* Returns the full HTML text in the original charset.
*
* @param array $opts Additional options: (since 2.1.0)
* - charset: (string) Return using this charset. If set but empty, will
* return as currently stored in the DOM object.
* - metacharset: (boolean) If true, will add a META tag containing the
* charset information.
*
* @return string HTML text.
*/
public function returnHtml(array $opts = array())
{
$curr_charset = $this->getCharset();
if (strcasecmp($curr_charset, 'US-ASCII') === 0) {
$curr_charset = 'UTF-8';
}
$charset = array_key_exists('charset', $opts)
? (empty($opts['charset']) ? $curr_charset : $opts['charset'])
: $this->_origCharset;
if (empty($opts['metacharset'])) {
$text = $this->dom->saveHTML();
} else {
/* Add placeholder for META tag. Can't add charset yet because DOM
* extension will alter output if it exists. */
$meta = $this->dom->createElement('meta');
$meta->setAttribute('http-equiv', 'content-type');
$meta->setAttribute('horde_dom_html_charset', '');
$head = $this->getHead();
$head->insertBefore($meta, $head->firstChild);
$text = str_replace(
'horde_dom_html_charset=""',
'content="text/html; charset=' . $charset . '"',
$this->dom->saveHTML()
);
$head->removeChild($meta);
}
if (strcasecmp($curr_charset, $charset) !== 0) {
$text = Horde_String::convertCharset($text, $curr_charset, $charset);
}
if (!$this->_xmlencoding ||
(($pos = strpos($text, $this->_xmlencoding)) === false)) {
return $text;
}
return substr_replace($text, '', $pos, strlen($this->_xmlencoding));
}
/**
* Returns the body text in the original charset.
*
* @return string HTML text.
*/
public function returnBody()
{
$body = $this->getBody();
$text = '';
if ($body->hasChildNodes()) {
foreach ($body->childNodes as $child) {
$text .= $this->dom->saveXML($child);
}
}
return Horde_String::convertCharset($text, 'UTF-8', $this->_origCharset);
}
/**
* Get the charset of the DOM data.
*
* @since 2.1.0
*
* @return string Charset of DOM data.
*/
public function getCharset()
{
return $this->dom->encoding
? $this->dom->encoding
: ($this->_xmlencoding ? 'UTF-8' : $this->_origCharset);
}
/**
* Loads the HTML data.
*
* @param string $html HTML data.
*/
protected function _loadHTML($html)
{
if (version_compare(PHP_VERSION, '5.4', '>=')) {
$mask = defined('LIBXML_PARSEHUGE')
? LIBXML_PARSEHUGE
: 0;
$mask |= defined('LIBXML_COMPACT')
? LIBXML_COMPACT
: 0;
$this->dom->loadHTML($html, $mask);
} else {
$this->dom->loadHTML($html);
}
}
/* Iterator methods. */
/**
*/
#[ReturnTypeWillChange]
public function current()
{
if ($this->_iterator instanceof DOMDocument) {
return $this->_iterator;
}
$curr = end($this->_iterator);
return $curr['list']->item($curr['i']);
}
/**
*/
#[ReturnTypeWillChange]
public function key()
{
return 0;
}
/**
*/
#[ReturnTypeWillChange]
public function next()
{
/* Iterate in the reverse direction through the node list. This allows
* alteration of the original list without breaking things (foreach()
* w/removeChild() may exit iteration after removal is complete. */
if ($this->_iterator instanceof DOMDocument) {
$this->_iterator = array();
$curr = array();
$node = $this->dom;
} elseif (empty($this->_iterator)) {
$this->_iterator = null;
return;
} else {
$curr = &$this->_iterator[count($this->_iterator) - 1];
$node = $curr['list']->item($curr['i']);
}
if (empty($curr['child']) &&
($node instanceof DOMNode) &&
$node->hasChildNodes()) {
$curr['child'] = true;
$this->_iterator[] = array(
'child' => false,
'i' => $node->childNodes->length - 1,
'list' => $node->childNodes
);
} elseif (--$curr['i'] < 0) {
array_pop($this->_iterator);
$this->next();
} else {
$curr['child'] = false;
}
}
/**
*/
#[ReturnTypeWillChange]
public function rewind()
{
$this->_iterator = $this->dom;
}
/**
*/
#[ReturnTypeWillChange]
public function valid()
{
return !is_null($this->_iterator);
}
}
-73
View File
@@ -1,73 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Exception
*/
/**
* Horde base exception class.
*
* @author
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Exception
*/
class Horde_Exception extends Exception
{
/**
* Error details that should not be part of the main exception message,
* e.g. any additional debugging information.
*
* @var string
*/
public $details;
/**
* Has this exception been logged?
*
* @var boolean
*/
public $logged = false;
/**
* The log level to use. A Horde_Log constant.
*
* @var integer
*/
protected $_logLevel = 0;
/**
* Get the log level.
*
* @return integer The Horde_Log constant for the log level.
*/
public function getLogLevel()
{
return $this->_logLevel;
}
/**
* Sets the log level.
*
* @param mixed $level The log level.
*/
public function setLogLevel($level = 0)
{
if (is_string($level)) {
$level = defined('Horde_Log::' . $level)
? constant('Horde_Log::' . $level)
: 0;
}
$this->_logLevel = $level;
}
}
@@ -1,55 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
/**
* Horde exception class that accepts output of error_get_last() as $code and
* mask itself as that error.
*
* @author
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
class Horde_Exception_LastError extends Horde_Exception
{
/**
* Exception constructor
*
* If $lasterror is passed the return value of error_get_last() (or a
* matching format), the exception will be rewritten to have its file and
* line parameters match that of the array, and any message in the array
* will be appended to $message.
*
* @param mixed $message The exception message, a PEAR_Error
* object, or an Exception object.
* @param mixed $code_or_lasterror Either a numeric error code, or
* an array from error_get_last().
*/
public function __construct($message = null, $code_or_lasterror = null)
{
if (is_array($code_or_lasterror)) {
if ($message) {
$message .= $code_or_lasterror['message'];
} else {
$message = $code_or_lasterror['message'];
}
parent::__construct($message, $code_or_lasterror['type']);
$this->file = $code_or_lasterror['file'];
$this->line = $code_or_lasterror['line'];
} else {
parent::__construct($message, $code_or_lasterror ?? 0);
}
}
}
@@ -1,41 +0,0 @@
<?php
/**
* Copyright 2010-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
/**
* Exception thrown if an object wasn't found.
*
* @author
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
class Horde_Exception_NotFound extends Horde_Exception
{
/**
* Constructor.
*
* @see Horde_Exception::__construct()
*
* @param mixed $message The exception message, a PEAR_Error
* object, or an Exception object.
* @param integer $code A numeric error code.
*/
public function __construct($message = null, $code = null)
{
if (is_null($message)) {
$message = Horde_Exception_Translation::t("Not Found");
}
parent::__construct($message, $code);
}
}
@@ -1,93 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
/**
* Horde exception class that converts PEAR errors to exceptions.
*
* @author
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
class Horde_Exception_Pear extends Horde_Exception
{
/**
* The class name for generated exceptions.
*
* @var string
*/
protected static $_class = __CLASS__;
/**
* Exception constructor.
*
* @param PEAR_Error $error The PEAR error.
*/
public function __construct(PEAR_Error $error)
{
parent::__construct($error->getMessage(), $error->getCode());
$this->details = $this->_getPearTrace($error);
}
/**
* Return a trace for the PEAR error.
*
* @param PEAR_Error $error The PEAR error.
*
* @return string The backtrace as a string.
*/
private function _getPearTrace(PEAR_Error $error)
{
$pear_error = '';
$backtrace = $error->getBacktrace();
if (!empty($backtrace)) {
$pear_error .= 'PEAR backtrace:' . "\n\n";
foreach ($backtrace as $frame) {
$pear_error .=
(isset($frame['class']) ? $frame['class'] : '')
. (isset($frame['type']) ? $frame['type'] : '')
. (isset($frame['function']) ? $frame['function'] : 'unkown') . ' '
. (isset($frame['file']) ? $frame['file'] : 'unkown') . ':'
. (isset($frame['line']) ? $frame['line'] : 'unkown') . "\n";
}
}
$userinfo = $error->getUserInfo();
if (!empty($userinfo)) {
$pear_error .= "\n" . 'PEAR user info:' . "\n\n";
if (is_string($userinfo)) {
$pear_error .= $userinfo;
} else {
$pear_error .= print_r($userinfo, true);
}
}
return $pear_error;
}
/**
* Exception handling.
*
* @param mixed $result The result to be checked for a PEAR_Error.
*
* @return mixed Returns the original result if it was no PEAR_Error.
*
* @throws Horde_Exception_Pear In case the result was a PEAR_Error.
*/
public static function catchError($result)
{
if ($result instanceof PEAR_Error) {
throw new self::$_class($result);
}
return $result;
}
}
@@ -1,41 +0,0 @@
<?php
/**
* Copyright 2010-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
/**
* Exception thrown if any access without sufficient permissions occured.
*
* @author
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
class Horde_Exception_PermissionDenied extends Horde_Exception
{
/**
* Constructor.
*
* @see Horde_Exception::__construct()
*
* @param string|Exception $message The exception message, a PEAR_Error
* object, or an Exception object.
* @param integer $code A numeric error code.
*/
public function __construct($message = null, $code = null)
{
if (is_null($message)) {
$message = Horde_Exception_Translation::t("Permission Denied");
}
parent::__construct($message, $code);
}
}
@@ -1,38 +0,0 @@
<?php
/**
* Copyright 2010-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Jan Schneider <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
/**
* Horde_Exception_Translation is the translation wrapper class for Horde_Exception.
*
* @author Jan Schneider <[email protected]>
* @category Horde
* @copyright 2010-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
class Horde_Exception_Translation extends Horde_Translation_Autodetect
{
/**
* The translation domain
*
* @var string
*/
protected static $_domain = 'Horde_Exception';
/**
* The absolute PEAR path to the translations for the default gettext handler.
*
* @var string
*/
protected static $_pearDirectory = '@data_dir@';
}
@@ -1,56 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
/**
* Horde exception class that can wrap and set its details from PEAR_Error,
* Exception, and other objects with similar interfaces.
*
* @author
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Exception
*/
class Horde_Exception_Wrapped extends Horde_Exception
{
/**
* Exception constructor.
*
* @param mixed $message The exception message, a PEAR_Error
* object, or an Exception object.
* @param int $code A numeric error code.
*/
public function __construct($message = null, $code = 0)
{
$previous = null;
if (is_object($message) &&
method_exists($message, 'getMessage')) {
if (empty($code) &&
method_exists($message, 'getCode')) {
$code = (int)$message->getCode();
}
if ($message instanceof Exception) {
$previous = $message;
}
if (method_exists($message, 'getUserinfo') &&
$details = $message->getUserinfo()) {
$this->details = $details;
} elseif (!empty($message->details)) {
$this->details = $message->details;
}
$message = (string)$message->getMessage();
}
parent::__construct($message, $code, $previous);
}
}
-178
View File
@@ -1,178 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (BSD). If you
* did not receive this file, see http://www.horde.org/licenses/bsd.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
*/
/**
* Provide normalized encoding/decoding support for IDNA strings.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
*/
class Horde_Idna
{
/**
* The backend to use.
*
* @var mixed
*/
protected static $_backend;
/**
* @throws Horde_Idna_Exception
*/
public static function encode($data)
{
switch ($backend = static::_getBackend()) {
case 'INTL':
if ($data === null) {
return false;
}
return idn_to_ascii($data);
case 'INTL_UTS46':
if ($data === null) {
return false;
}
$result = idn_to_ascii($data, 0, INTL_IDNA_VARIANT_UTS46, $info);
self::_checkForError($info);
return $result;
default:
return $backend->encode($data);
}
}
/**
* @throws Horde_Idna_Exception
*/
public static function decode($data)
{
switch ($backend = static::_getBackend()) {
case 'INTL':
case 'INTL_UTS46':
$parts = explode('.', $data);
foreach ($parts as &$part) {
if (strpos($part, 'xn--') === 0) {
switch ($backend) {
case 'INTL':
$part = idn_to_utf8($part);
break;
case 'INTL_UTS46':
$part = idn_to_utf8($part, 0, INTL_IDNA_VARIANT_UTS46, $info);
self::_checkForError($info);
break;
}
}
}
return implode('.', $parts);
default:
return $backend->decode($data);
}
}
/**
* Checks if the $idna_info parameter of idn_to_ascii() or idn_to_utf8()
* contains errors.
*
* @param array $info Fourth parameter to idn_to_ascii() or idn_to_utf8().
*
* @throws Horde_Idna_Exception
*/
protected static function _checkForError($info)
{
if (!isset($info['errors'])) {
return;
}
switch (true) {
case $info['errors'] & IDNA_ERROR_EMPTY_LABEL:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Domain name is empty"
));
case $info['errors'] & IDNA_ERROR_LABEL_TOO_LONG:
case $info['errors'] & IDNA_ERROR_DOMAIN_NAME_TOO_LONG:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Domain name is too long"
));
case $info['errors'] & IDNA_ERROR_LEADING_HYPHEN:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Starts with a hyphen"
));
case $info['errors'] & IDNA_ERROR_TRAILING_HYPHEN:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Ends with a hyphen"
));
case $info['errors'] & IDNA_ERROR_HYPHEN_3_4:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Contains hyphen in the third and fourth positions"
));
case $info['errors'] & IDNA_ERROR_LEADING_COMBINING_MARK:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Starts with a combining mark"
));
case $info['errors'] & IDNA_ERROR_DISALLOWED:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Contains disallowed characters"
));
case $info['errors'] & IDNA_ERROR_PUNYCODE:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Starts with \"xn--\" but does not contain valid Punycode"
));
case $info['errors'] & IDNA_ERROR_LABEL_HAS_DOT:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Contains a dot"
));
case $info['errors'] & IDNA_ERROR_INVALID_ACE_LABEL:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"ACE label does not contain a valid label string"
));
case $info['errors'] & IDNA_ERROR_BIDI:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Does not meet the IDNA BiDi requirements (for right-to-left characters)"
));
case $info['errors'] & IDNA_ERROR_CONTEXTJ:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Does not meet the IDNA CONTEXTJ requirements"
));
case $info['errors']:
throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
"Unknown error"
));
}
}
/**
* Return the IDNA backend.
*
* @return mixed IDNA backend (false if none available).
*/
protected static function _getBackend()
{
if (!isset(self::$_backend)) {
if (extension_loaded('intl')) {
/* Only available in PHP > 5.4.0 */
self::$_backend = defined('INTL_IDNA_VARIANT_UTS46')
? 'INTL_UTS46'
: 'INTL';
} else {
self::$_backend = new Horde_Idna_Punycode();
}
}
return self::$_backend;
}
}
@@ -1,24 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (BSD). If you
* did not receive this file, see http://www.horde.org/licenses/bsd.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
*/
/**
* Exception class for the Horde_Idna package.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
*/
class Horde_Idna_Exception extends Horde_Exception
{}
-354
View File
@@ -1,354 +0,0 @@
<?php
/**
* Copyright 2014 TrueServer B.V.
* Copyright 2015-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (BSD). If you
* did not receive this file, see http://www.horde.org/licenses/bsd.
*
* @author Renan Gonçalves <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
*/
/**
* Punycode implementation as described in RFC 3492.
*
* Original code (v1.0.1; released under the MIT License):
* https://github.com/true/php-punycode/
*
* @author Renan Gonçalves <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014 TrueServer B.V.
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
* @link http://tools.ietf.org/html/rfc3492
*/
class Horde_Idna_Punycode
{
/**
* Bootstring parameter values.
*/
const BASE = 36;
const TMIN = 1;
const TMAX = 26;
const SKEW = 38;
const DAMP = 700;
const INITIAL_BIAS = 72;
const INITIAL_N = 128;
const PREFIX = 'xn--';
const DELIMITER = '-';
/**
* Encode table.
*
* @param array
*/
protected static $_encodeTable = array(
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
);
/**
* Decode table.
*
* @param array
*/
protected static $_decodeTable = array(
'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5,
'g' => 6, 'h' => 7, 'i' => 8, 'j' => 9, 'k' => 10, 'l' => 11,
'm' => 12, 'n' => 13, 'o' => 14, 'p' => 15, 'q' => 16, 'r' => 17,
's' => 18, 't' => 19, 'u' => 20, 'v' => 21, 'w' => 22, 'x' => 23,
'y' => 24, 'z' => 25, '0' => 26, '1' => 27, '2' => 28, '3' => 29,
'4' => 30, '5' => 31, '6' => 32, '7' => 33, '8' => 34, '9' => 35
);
/**
* Encode a domain to its Punycode version.
*
* @param string $input Domain name in Unicde to be encoded.
*
* @return string Punycode representation in ASCII.
*/
public function encode($input)
{
$parts = explode('.', $input);
foreach ($parts as &$part) {
$part = $this->_encodePart($part);
}
return implode('.', $parts);
}
/**
* Encode a part of a domain name, such as tld, to its Punycode version.
*
* @param string $input Part of a domain name.
*
* @return string Punycode representation of a domain part.
*/
protected function _encodePart($input)
{
$codePoints = $this->_codePoints($input);
$n = static::INITIAL_N;
$bias = static::INITIAL_BIAS;
$delta = 0;
$h = $b = count($codePoints['basic']);
$output = '';
foreach ($codePoints['basic'] as $code) {
$output .= $this->_codePointToChar($code);
}
if ($input === $output) {
return $output;
}
if ($b > 0) {
$output .= static::DELIMITER;
}
$codePoints['nonBasic'] = array_unique($codePoints['nonBasic']);
sort($codePoints['nonBasic']);
$i = 0;
$length = Horde_String::length($input, 'UTF-8');
while ($h < $length) {
$m = $codePoints['nonBasic'][$i++];
$delta = $delta + ($m - $n) * ($h + 1);
$n = $m;
foreach ($codePoints['all'] as $c) {
if (($c < $n) || ($c < static::INITIAL_N)) {
++$delta;
}
if ($c === $n) {
$q = $delta;
for ($k = static::BASE; ; $k += static::BASE) {
$t = $this->_calculateThreshold($k, $bias);
if ($q < $t) {
break;
}
$code = $t + (($q - $t) % (static::BASE - $t));
$output .= static::$_encodeTable[$code];
$q = ($q - $t) / (static::BASE - $t);
}
$output .= static::$_encodeTable[$q];
$bias = $this->_adapt($delta, $h + 1, ($h === $b));
$delta = 0;
++$h;
}
}
++$delta;
++$n;
}
return static::PREFIX . $output;
}
/**
* Decode a Punycode domain name to its Unicode counterpart.
*
* @param string $input Domain name in Punycode
*
* @return string Unicode domain name.
*/
public function decode($input)
{
$parts = explode('.', $input);
foreach ($parts as &$part) {
if (strpos($part, static::PREFIX) === 0) {
$part = $this->_decodePart(
substr($part, strlen(static::PREFIX))
);
}
}
return implode('.', $parts);
}
/**
* Decode a part of domain name, such as tld.
*
* @param string $input Part of a domain name.
*
* @return string Unicode domain part.
*/
protected function _decodePart($input)
{
$n = static::INITIAL_N;
$i = 0;
$bias = static::INITIAL_BIAS;
$output = '';
$pos = strrpos($input, static::DELIMITER);
if ($pos !== false) {
$output = substr($input, 0, $pos++);
} else {
$pos = 0;
}
$outputLength = strlen($output);
$inputLength = strlen($input);
/* Punycode lookup is case-insensitive. */
$input = Horde_String::lower($input);
while ($pos < $inputLength) {
$oldi = $i;
$w = 1;
for ($k = static::BASE; ; $k += static::BASE) {
$digit = static::$_decodeTable[$input[$pos++]];
$i = $i + ($digit * $w);
$t = $this->_calculateThreshold($k, $bias);
if ($digit < $t) {
break;
}
$w = $w * (static::BASE - $t);
}
$bias = $this->_adapt($i - $oldi, ++$outputLength, ($oldi === 0));
$n = $n + (int) ($i / $outputLength);
$i = $i % ($outputLength);
$output = Horde_String::substr($output, 0, $i, 'UTF-8') .
$this->_codePointToChar($n) .
Horde_String::substr($output, $i, $outputLength - 1, 'UTF-8');
++$i;
}
return $output;
}
/**
* Calculate the bias threshold to fall between TMIN and TMAX.
*
* @param integer $k
* @param integer $bias
*
* @return integer
*/
protected function _calculateThreshold($k, $bias)
{
if ($k <= ($bias + static::TMIN)) {
return static::TMIN;
} elseif ($k >= ($bias + static::TMAX)) {
return static::TMAX;
}
return $k - $bias;
}
/**
* Bias adaptation.
*
* @param integer $delta
* @param integer $numPoints
* @param boolean $firstTime
*
* @return integer
*/
protected function _adapt($delta, $numPoints, $firstTime)
{
$delta = (int) (
($firstTime)
? $delta / static::DAMP
: $delta / 2
);
$delta += (int) ($delta / $numPoints);
$k = 0;
while ($delta > ((static::BASE - static::TMIN) * static::TMAX) / 2) {
$delta = (int) ($delta / (static::BASE - static::TMIN));
$k = $k + static::BASE;
}
$k = $k + (int) (((static::BASE - static::TMIN + 1) * $delta) / ($delta + static::SKEW));
return $k;
}
/**
* List code points for a given input.
*
* @param string $input
*
* @return array Multi-dimension array with basic, non-basic and
* aggregated code points.
*/
protected function _codePoints($input)
{
$codePoints = array(
'all' => array(),
'basic' => array(),
'nonBasic' => array()
);
$len = Horde_String::length($input, 'UTF-8');
for ($i = 0; $i < $len; ++$i) {
$char = Horde_String::substr($input, $i, 1, 'UTF-8');
$code = $this->_charToCodePoint($char);
if ($code < 128) {
$codePoints['all'][] = $codePoints['basic'][] = $code;
} else {
$codePoints['all'][] = $codePoints['nonBasic'][] = $code;
}
}
return $codePoints;
}
/**
* Convert a single or multi-byte character to its code point.
*
* @param string $char
*
* @return integer
*/
protected function _charToCodePoint($char)
{
$code = ord($char[0]);
if ($code < 128) {
return $code;
} elseif ($code < 224) {
return (($code - 192) * 64) + (ord($char[1]) - 128);
} elseif ($code < 240) {
return (($code - 224) * 4096) + ((ord($char[1]) - 128) * 64) + (ord($char[2]) - 128);
}
return (($code - 240) * 262144) + ((ord($char[1]) - 128) * 4096) + ((ord($char[2]) - 128) * 64) + (ord($char[3]) - 128);
}
/**
* Convert a code point to its single or multi-byte character
*
* @param integer $code
*
* @return string
*/
protected function _codePointToChar($code)
{
if ($code <= 0x7F) {
return chr($code);
} elseif ($code <= 0x7FF) {
return chr(($code >> 6) + 192) . chr(($code & 63) + 128);
} elseif ($code <= 0xFFFF) {
return chr(($code >> 12) + 224) . chr((($code >> 6) & 63) + 128) . chr(($code & 63) + 128);
}
return chr(($code >> 18) + 240) . chr((($code >> 12) & 63) + 128) . chr((($code >> 6) & 63) + 128) . chr(($code & 63) + 128);
}
}
@@ -1,40 +0,0 @@
<?php
/**
* Copyright 2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (BSD). If you
* did not receive this file, see http://www.horde.org/licenses/bsd.
*
* @author Jan Schneider <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
*/
/**
* Horde_Idna_Translation is the translation wrapper class for
* Horde_Idna.
*
* @author Jan Schneider <[email protected]>
* @category Horde
* @copyright 2017 Horde LLC
* @license http://www.horde.org/licenses/bsd BSD
* @package Idna
* @since Horde_Idna 1.1.0
*/
class Horde_Idna_Translation extends Horde_Translation_Autodetect
{
/**
* The translation domain
*
* @var string
*/
protected static $_domain = 'Horde_Idna';
/**
* The absolute PEAR path to the translations for the default gettext handler.
*
* @var string
*/
protected static $_pearDirectory = '@data_dir@';
}
-203
View File
@@ -1,203 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Base class for Horde_Imap_Client package. Defines common constants for use
* in the package.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client
{
/* Constants for openMailbox() */
const OPEN_READONLY = 1;
const OPEN_READWRITE = 2;
const OPEN_AUTO = 3;
/* Constants for listMailboxes() */
const MBOX_SUBSCRIBED = 1;
const MBOX_SUBSCRIBED_EXISTS = 2;
const MBOX_UNSUBSCRIBED = 3;
const MBOX_ALL = 4;
/* @since 2.23.0 */
const MBOX_ALL_SUBSCRIBED = 5;
/* Constants for status() */
const STATUS_MESSAGES = 1;
const STATUS_RECENT = 2;
const STATUS_UIDNEXT = 4;
const STATUS_UIDVALIDITY = 8;
const STATUS_UNSEEN = 16;
const STATUS_ALL = 32;
const STATUS_FIRSTUNSEEN = 64;
const STATUS_FLAGS = 128;
const STATUS_PERMFLAGS = 256;
const STATUS_HIGHESTMODSEQ = 512;
const STATUS_SYNCMODSEQ = 1024;
const STATUS_SYNCFLAGUIDS = 2048;
const STATUS_UIDNOTSTICKY = 4096;
const STATUS_UIDNEXT_FORCE = 8192;
const STATUS_SYNCVANISHED = 16384;
/* @since 2.12.0 */
const STATUS_RECENT_TOTAL = 32768;
/* @since 2.14.0 */
const STATUS_FORCE_REFRESH = 65536;
/* Constants for search() */
const SORT_ARRIVAL = 1;
const SORT_CC = 2;
const SORT_DATE = 3;
const SORT_FROM = 4;
const SORT_REVERSE = 5;
const SORT_SIZE = 6;
const SORT_SUBJECT = 7;
const SORT_TO = 8;
/* SORT_THREAD provided for completeness - it is not a valid sort criteria
* for search() (use thread() instead). */
const SORT_THREAD = 9;
/* Sort criteria defined in RFC 5957 */
const SORT_DISPLAYFROM = 10;
const SORT_DISPLAYTO = 11;
/* SORT_SEQUENCE does a simple numerical sort on the returned
* UIDs/sequence numbers. */
const SORT_SEQUENCE = 12;
/* Fuzzy sort criteria defined in RFC 6203 */
const SORT_RELEVANCY = 13;
/* @since 2.4.0 */
const SORT_DISPLAYFROM_FALLBACK = 14;
/* @since 2.4.0 */
const SORT_DISPLAYTO_FALLBACK = 15;
/* Search results constants */
const SEARCH_RESULTS_COUNT = 1;
const SEARCH_RESULTS_MATCH = 2;
const SEARCH_RESULTS_MAX = 3;
const SEARCH_RESULTS_MIN = 4;
const SEARCH_RESULTS_SAVE = 5;
/* Fuzzy sort criteria defined in RFC 6203 */
const SEARCH_RESULTS_RELEVANCY = 6;
/* Constants for thread() */
const THREAD_ORDEREDSUBJECT = 1;
const THREAD_REFERENCES = 2;
const THREAD_REFS = 3;
/* Fetch criteria constants. */
const FETCH_STRUCTURE = 1;
const FETCH_FULLMSG = 2;
const FETCH_HEADERTEXT = 3;
const FETCH_BODYTEXT = 4;
const FETCH_MIMEHEADER = 5;
const FETCH_BODYPART = 6;
const FETCH_BODYPARTSIZE = 7;
const FETCH_HEADERS = 8;
const FETCH_ENVELOPE = 9;
const FETCH_FLAGS = 10;
const FETCH_IMAPDATE = 11;
const FETCH_SIZE = 12;
const FETCH_UID = 13;
const FETCH_SEQ = 14;
const FETCH_MODSEQ = 15;
/* @since 2.11.0 */
const FETCH_DOWNGRADED = 16;
/* Namespace constants. @deprecated */
const NS_PERSONAL = 1;
const NS_OTHER = 2;
const NS_SHARED = 3;
/* ACL constants (RFC 4314 [2.1]). */
const ACL_LOOKUP = 'l';
const ACL_READ = 'r';
const ACL_SEEN = 's';
const ACL_WRITE = 'w';
const ACL_INSERT = 'i';
const ACL_POST = 'p';
const ACL_CREATEMBOX = 'k';
const ACL_DELETEMBOX = 'x';
const ACL_DELETEMSGS = 't';
const ACL_EXPUNGE = 'e';
const ACL_ADMINISTER = 'a';
// Old constants (RFC 2086 [3]; RFC 4314 [2.1.1])
const ACL_CREATE = 'c';
const ACL_DELETE = 'd';
/* System flags. */
// RFC 3501 [2.3.2]
const FLAG_ANSWERED = '\\answered';
const FLAG_DELETED = '\\deleted';
const FLAG_DRAFT = '\\draft';
const FLAG_FLAGGED = '\\flagged';
const FLAG_RECENT = '\\recent';
const FLAG_SEEN = '\\seen';
// RFC 3503 [3.3]
const FLAG_MDNSENT = '$mdnsent';
// RFC 5550 [2.8]
const FLAG_FORWARDED = '$forwarded';
// RFC 5788 registered keywords:
// http://www.ietf.org/mail-archive/web/morg/current/msg00441.html
const FLAG_JUNK = '$junk';
const FLAG_NOTJUNK = '$notjunk';
/* Special-use mailbox attributes (RFC 6154 [2]). */
const SPECIALUSE_ALL = '\\All';
const SPECIALUSE_ARCHIVE = '\\Archive';
const SPECIALUSE_DRAFTS = '\\Drafts';
const SPECIALUSE_FLAGGED = '\\Flagged';
const SPECIALUSE_JUNK = '\\Junk';
const SPECIALUSE_SENT = '\\Sent';
const SPECIALUSE_TRASH = '\\Trash';
/* Constants for sync(). */
const SYNC_UIDVALIDITY = 0;
const SYNC_FLAGS = 1;
const SYNC_FLAGSUIDS = 2;
const SYNC_NEWMSGS = 4;
const SYNC_NEWMSGSUIDS = 8;
const SYNC_VANISHED = 16;
const SYNC_VANISHEDUIDS = 32;
const SYNC_ALL = 64;
/**
* Capability dependencies.
*
* @deprecated
*
* @var array
*/
public static $capability_deps = array(
// RFC 7162 [3.2]
'QRESYNC' => array(
// QRESYNC requires CONDSTORE, but the latter is implied and is
// not required to be listed.
'ENABLE'
),
// RFC 5182 [2.1]
'SEARCHRES' => array(
'ESEARCH'
),
// RFC 5255 [3.1]
'LANGUAGE' => array(
'NAMESPACE'
),
// RFC 5957 [1]
'SORT=DISPLAY' => array(
'SORT'
)
);
}
@@ -1,189 +0,0 @@
<?php
/**
* Copyright (c) 2002-2003 Richard Heyes
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* This code is based on the original code contained in the PEAR Auth_SASL
* package (v0.5.1):
* $Id: DigestMD5.php 294702 2010-02-07 16:03:55Z cweiske $
*
* That code is covered by the BSD 3-Clause license, as set forth below:
* +-----------------------------------------------------------------------+
* | Copyright (c) 2002-2003 Richard Heyes |
* | All rights reserved. |
* | |
* | Redistribution and use in source and binary forms, with or without |
* | modification, are permitted provided that the following conditions |
* | are met: |
* | |
* | o Redistributions of source code must retain the above copyright |
* | notice, this list of conditions and the following disclaimer. |
* | o Redistributions in binary form must reproduce the above copyright |
* | notice, this list of conditions and the following disclaimer in the |
* | documentation and/or other materials provided with the distribution.|
* | o The names of the authors may not be used to endorse or promote |
* | products derived from this software without specific prior written |
* | permission. |
* | |
* | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
* | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
* | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
* | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
* | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
* | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
* | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* +-----------------------------------------------------------------------+
*
* @category Horde
* @copyright 2002-2003 Richard Heyes
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Provides the code needed to authenticate via the DIGEST-MD5 SASL mechanism
* (defined in RFC 2831). This method has been obsoleted by RFC 6331, but
* still is in use on legacy servers.
*
* @author Richard Heyes <[email protected]>
* @author Michael Slusarz <[email protected]>
* @copyright 2002-2003 Richard Heyes
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Auth_DigestMD5
{
/**
* Digest response components.
*
* @var string
*/
protected $_response;
/**
* Generate the Digest-MD5 response.
*
* @param string $id Authentication id (username).
* @param string $pass Password.
* @param string $challenge The digest challenge sent by the server.
* @param string $hostname The hostname of the machine connecting to.
* @param string $service The service name (e.g. 'imap', 'pop3').
*
* @throws Horde_Imap_Client_Exception
*/
public function __construct($id, $pass, $challenge, $hostname, $service)
{
$challenge = $this->_parseChallenge($challenge);
$cnonce = $this->_getCnonce();
$digest_uri = sprintf('%s/%s', $service, $hostname);
/* Get response value. */
$A1 = sprintf('%s:%s:%s', pack('H32', hash('md5', sprintf('%s:%s:%s', $id, $challenge['realm'], $pass))), $challenge['nonce'], $cnonce);
$A2 = 'AUTHENTICATE:' . $digest_uri;
$response_value = hash('md5', sprintf('%s:%s:00000001:%s:auth:%s', hash('md5', $A1), $challenge['nonce'], $cnonce, hash('md5', $A2)));
$this->_response = array(
'cnonce' => '"' . $cnonce . '"',
'digest-uri' => '"' . $digest_uri . '"',
'maxbuf' => $challenge['maxbuf'],
'nc' => '00000001',
'nonce' => '"' . $challenge['nonce'] . '"',
'qop' => 'auth',
'response' => $response_value,
'username' => '"' . $id . '"'
);
if (strlen($challenge['realm'])) {
$this->_response['realm'] = '"' . $challenge['realm'] . '"';
}
}
/**
* Cooerce to string.
*
* @return string The digest response (not base64 encoded).
*/
public function __toString()
{
$out = array();
foreach ($this->_response as $key => $val) {
$out[] = $key . '=' . $val;
}
return implode(',', $out);
}
/**
* Return specific digest response directive.
*
* @return mixed Requested directive, or null if it does not exist.
*/
public function __get($name)
{
return isset($this->_response[$name])
? $this->_response[$name]
: null;
}
/**
* Parses and verifies the digest challenge.
*
* @param string $challenge The digest challenge
*
* @return array The parsed challenge as an array with directives as keys.
*
* @throws Horde_Imap_Client_Exception
*/
protected function _parseChallenge($challenge)
{
$tokens = array(
'maxbuf' => 65536,
'realm' => ''
);
preg_match_all('/([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches, PREG_SET_ORDER);
foreach ($matches as $val) {
$tokens[$val[1]] = trim($val[2], '"');
}
// Required directives.
if (!isset($tokens['nonce']) || !isset($tokens['algorithm'])) {
throw new Horde_Imap_Client_Exception(
Horde_Imap_Client_Translation::r("Authentication failure."),
Horde_Imap_Client_Exception::SERVER_CONNECT
);
}
return $tokens;
}
/**
* Creates the client nonce for the response
*
* @return string The cnonce value.
*/
protected function _getCnonce()
{
if ((@is_readable('/dev/urandom') &&
($fd = @fopen('/dev/urandom', 'r'))) ||
(@is_readable('/dev/random') &&
($fd = @fopen('/dev/random', 'r')))) {
$str = fread($fd, 32);
fclose($fd);
} else {
$str = '';
for ($i = 0; $i < 32; ++$i) {
$str .= chr(mt_rand(0, 255));
}
}
return base64_encode($str);
}
}
@@ -1,270 +0,0 @@
<?php
/**
* Copyright 2015-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Provides authentication via the SCRAM SASL mechanism (RFC 5802 [3]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2015-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.29.0
*/
class Horde_Imap_Client_Auth_Scram
{
/**
* AuthMessage (RFC 5802 [3]).
*
* @var string
*/
protected $_authmsg;
/**
* Hash name.
*
* @var string
*/
protected $_hash;
/**
* Number of Hi iterations (RFC 5802 [2]).
*
* @var integer
*/
protected $_iterations;
/**
* Nonce.
*
* @var string
*/
protected $_nonce;
/**
* Password.
*
* @var string
*/
protected $_pass;
/**
* Server salt.
*
* @var string
*/
protected $_salt;
/**
* Calculated server signature value.
*
* @var string
*/
protected $_serversig;
/**
* Username.
*
* @var string
*/
protected $_user;
/**
* Constructor.
*
* @param string $user Username.
* @param string $pass Password.
* @param string $hash Hash name.
*
* @throws Horde_Imap_Client_Exception
*/
public function __construct($user, $pass, $hash = 'SHA1')
{
$error = false;
$this->_hash = $hash;
try {
if (!class_exists('Horde_Stringprep') ||
!class_exists('Horde_Crypt_Blowfish_Pbkdf2')) {
throw new Exception();
}
Horde_Stringprep::autoload();
$saslprep = new Znerol\Component\Stringprep\Profile\SASLprep();
$this->_user = $saslprep->apply(
$user,
'UTF-8',
Znerol\Component\Stringprep\Profile::MODE_QUERY
);
$this->_pass = $saslprep->apply(
$pass,
'UTF-8',
Znerol\Component\Stringprep\Profile::MODE_STORE
);
} catch (Znerol\Component\Stringprep\ProfileException $e) {
$error = true;
} catch (Exception $e) {
$error = true;
}
if ($error) {
throw new Horde_Imap_Client_Exception(
Horde_Imap_Client_Translation::r("Authentication failure."),
Horde_Imap_Client_Exception::LOGIN_AUTHORIZATIONFAILED
);
}
/* Generate nonce. (Done here so this can be overwritten for
* testing purposes.) */
$this->_nonce = strval(new Horde_Support_Randomid());
}
/**
* Return the initial client message.
*
* @return string Initial client message.
*/
public function getClientFirstMessage()
{
/* n: client doesn't support channel binding,
* <empty>,
* n=<user>: SASLprepped username with "," and "=" escaped,
* r=<nonce>: Random nonce */
$this->_authmsg = 'n=' . str_replace(
array(',', '='),
array('=2C', '=3D'),
$this->_user
) . ',r=' . $this->_nonce;
return 'n,,' . $this->_authmsg;
}
/**
* Process the initial server message response.
*
* @param string $msg Initial server response.
*
* @return boolean False if authentication failed at this stage.
*/
public function parseServerFirstMessage($msg)
{
$i = $r = $s = false;
foreach (explode(',', $msg) as $val) {
list($attr, $aval) = array_map('trim', explode('=', $val, 2));
switch ($attr) {
case 'i':
$this->_iterations = intval($aval);
$i = true;
break;
case 'r':
/* Beginning of server-provided nonce MUST be the same as the
* nonce we provided. */
if (strpos($aval, $this->_nonce) !== 0) {
return false;
}
$this->_nonce = $aval;
$r = true;
break;
case 's':
$this->_salt = base64_decode($aval);
$s = true;
break;
}
}
if ($i && $r && $s) {
$this->_authmsg .= ',' . $msg;
return true;
}
return false;
}
/**
* Return the final client message.
*
* @return string Final client message.
*/
public function getClientFinalMessage()
{
$final_msg = 'c=biws,r=' . $this->_nonce;
/* Salted password. */
$s_pass = strval(new Horde_Crypt_Blowfish_Pbkdf2(
$this->_pass,
strlen(hash($this->_hash, '', true)),
array(
'algo' => $this->_hash,
'i_count' => $this->_iterations,
'salt' => $this->_salt
)
));
/* Client key. */
$c_key = hash_hmac($this->_hash, 'Client Key', $s_pass, true);
/* Stored key. */
$s_key = hash($this->_hash, $c_key, true);
/* Client signature. */
$auth_msg = $this->_authmsg . ',' . $final_msg;
$c_sig = hash_hmac($this->_hash, $auth_msg, $s_key, true);
/* Proof. */
$proof = $c_key ^ $c_sig;
/* Server signature. */
$this->_serversig = hash_hmac(
$this->_hash,
$auth_msg,
hash_hmac($this->_hash, 'Server Key', $s_pass, true),
true
);
/* c=biws: channel-binding ("biws" = base64('n,,')),
* p=<proof>: base64 encoded ClientProof,
* r=<nonce>: Nonce as returned from the server. */
return $final_msg . ',p=' . base64_encode($proof);
}
/**
* Process the final server message response.
*
* @param string $msg Final server response.
*
* @return boolean False if authentication failed.
*/
public function parseServerFinalMessage($msg)
{
foreach (explode(',', $msg) as $val) {
list($attr, $aval) = array_map('trim', explode('=', $val, 2));
switch ($attr) {
case 'e':
return false;
case 'v':
return (base64_decode($aval) === $this->_serversig);
}
}
return false;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,105 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Handle IMAP alerts sent from the server.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.24.0
*/
class Horde_Imap_Client_Base_Alerts
implements SplSubject
{
/**
* Alert data.
*
* @var object
*/
protected $_alert;
/**
* Observers.
*
* @var array
*/
protected $_observers = array();
/**
* Add an alert.
*
* @param string $alert The alert string.
* @param string $type The alert type.
*/
public function add($alert, $type = null)
{
$this->_alert = new stdClass;
$this->_alert->alert = $alert;
if (!is_null($type)) {
$this->_alert->type = $type;
}
$this->notify();
}
/**
* Returns the last alert received.
*
* @return object Alert information. Object with these properties:
* <pre>
* - alert: (string) Alert string.
* - type: (string) [OPTIONAL] Alert type.
* </pre>
*/
public function getLast()
{
return $this->_alert;
}
/* SplSubject methods. */
/**
*/
#[ReturnTypeWillChange]
public function attach(SplObserver $observer)
{
$this->detach($observer);
$this->_observers[] = $observer;
}
/**
*/
#[ReturnTypeWillChange]
public function detach(SplObserver $observer)
{
if (($key = array_search($observer, $this->_observers, true)) !== false) {
unset($this->_observers[$key]);
}
}
/**
* Notification is triggered internally whenever the object's internal
* data storage is altered.
*/
#[ReturnTypeWillChange]
public function notify()
{
foreach ($this->_observers as $val) {
$val->update($this);
}
}
}
@@ -1,152 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* An object allowing management of debugging output within a
* Horde_Imap_Client_Base object.
*
* NOTE: This class is NOT intended to be accessed outside of a Base object.
* There is NO guarantees that the API of this class will not change across
* versions.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @internal
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Base_Debug
{
/** Time, in seconds, to be labeled a slow command. */
const SLOW_CMD = 5;
/**
* Is debugging active?
*
* @var boolean
*/
public $debug = true;
/**
* The debug stream.
*
* @var resource
*/
protected $_stream;
/**
* Timestamp of last command.
*
* @var integer
*/
protected $_time = null;
/**
* Constructor.
*
* @param mixed $debug The debug target.
*/
public function __construct($debug)
{
$this->_stream = is_resource($debug)
? $debug
: @fopen($debug, 'a');
register_shutdown_function(array($this, 'shutdown'));
}
/**
* Shutdown function.
*/
public function shutdown()
{
if (is_resource($this->_stream)) {
fflush($this->_stream);
fclose($this->_stream);
$this->_stream = null;
}
}
/**
* Write client output to debug log.
*
* @param string $msg Debug message.
*/
public function client($msg)
{
$this->_write($msg . "\n", 'C: ');
}
/**
* Write informational message to debug log.
*
* @param string $msg Debug message.
*/
public function info($msg)
{
$this->_write($msg . "\n", '>> ');
}
/**
* Write server output to debug log.
*
* @param string $msg Debug message.
*/
public function raw($msg)
{
$this->_write($msg);
}
/**
* Write server output to debug log.
*
* @param string $msg Debug message.
*/
public function server($msg)
{
$this->_write($msg . "\n", 'S: ');
}
/**
* Write debug information to the output stream.
*
* @param string $msg Debug data.
*/
protected function _write($msg, $pre = null)
{
if (!$this->debug || !$this->_stream) {
return;
}
if (!is_null($pre)) {
$new_time = microtime(true);
if (is_null($this->_time)) {
fwrite(
$this->_stream,
str_repeat('-', 30) . "\n" . '>> ' . date('r') . "\n"
);
} elseif (($diff = ($new_time - $this->_time)) > self::SLOW_CMD) {
fwrite(
$this->_stream,
'>> Slow Command: ' . round($diff, 3) . " seconds\n"
);
}
$this->_time = $new_time;
}
fwrite($this->_stream, $pre . $msg);
}
}
@@ -1,109 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Class containing deprecated Horde_Imap_Client_Base methods that will be
* removed in version 3.0.
*
* NOTE: This class is NOT intended to be accessed outside of a Base object.
* There is NO guarantees that the API of this class will not change across
* versions.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @internal
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Base_Deprecated
{
/**
* Returns a unique identifier for the current mailbox status.
*
* @param Horde_Imap_Client_Base $base_ob The base driver object.
* @param mixed $mailbox A mailbox. Either a
* Horde_Imap_Client_Mailbox
* object or a string (UTF-8).
* @param boolean $condstore Is CONDSTORE enabled?
* @param array $addl Additional cache info to add to
* the cache ID string.
*
* @return string The cache ID string, which will change when the
* composition of the mailbox changes. The uidvalidity
* will always be the first element, and will be delimited
* by the '|' character.
*
* @throws Horde_Imap_Client_Exception
*/
public static function getCacheId($base_ob, $mailbox, $condstore,
array $addl = array())
{
$query = Horde_Imap_Client::STATUS_UIDVALIDITY | Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_UIDNEXT;
/* Use MODSEQ as cache ID if CONDSTORE extension is available. */
if ($condstore) {
$query |= Horde_Imap_Client::STATUS_HIGHESTMODSEQ;
} else {
$query |= Horde_Imap_Client::STATUS_UIDNEXT_FORCE;
}
$status = $base_ob->status($mailbox, $query);
if (empty($status['highestmodseq'])) {
$parts = array(
'V' . $status['uidvalidity'],
'U' . $status['uidnext'],
'M' . $status['messages']
);
} else {
$parts = array(
'V' . $status['uidvalidity'],
'H' . $status['highestmodseq']
);
}
return implode('|', array_merge($parts, $addl));
}
/**
* Parses a cacheID created by getCacheId().
*
* @param string $id The cache ID.
*
* @return array An array with the following information:
* - highestmodseq: (integer)
* - messages: (integer)
* - uidnext: (integer)
* - uidvalidity: (integer) Always present
*/
public static function parseCacheId($id)
{
$data = array(
'H' => 'highestmodseq',
'M' => 'messages',
'U' => 'uidnext',
'V' => 'uidvalidity'
);
$info = array();
foreach (explode('|', $id) as $part) {
if (isset($data[$part[0]])) {
$info[$data[$part[0]]] = intval(substr($part, 1));
}
}
return $info;
}
}
@@ -1,187 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* An object allowing management of mailbox state within a
* Horde_Imap_Client_Base object.
*
* NOTE: This class is NOT intended to be accessed outside of a Base object.
* There is NO guarantees that the API of this class will not change across
* versions.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @internal
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Base_Mailbox
{
/**
* Mapping object.
*
* @var Horde_Imap_Client_Ids_Map
*/
public $map;
/**
* Is mailbox opened?
*
* @var boolean
*/
public $open;
/**
* Is mailbox sync'd with remote server (via CONDSTORE/QRESYNC)?
*
* @var boolean
*/
public $sync;
/**
* Status information.
*
* @var array
*/
protected $_status = array();
/**
* Constructor.
*/
public function __construct()
{
$this->reset();
}
/**
* Get status information for the mailbox.
*
* @param integer $entry STATUS_* constant.
*
* @return mixed Status information.
*/
public function getStatus($entry)
{
if (isset($this->_status[$entry])) {
return $this->_status[$entry];
}
switch ($entry) {
case Horde_Imap_Client::STATUS_FLAGS:
case Horde_Imap_Client::STATUS_SYNCFLAGUIDS:
case Horde_Imap_Client::STATUS_SYNCVANISHED:
return array();
case Horde_Imap_Client::STATUS_FIRSTUNSEEN:
/* If we know there are no messages in the current mailbox, we
* know there are no unseen messages. */
return empty($this->_status[Horde_Imap_Client::STATUS_MESSAGES])
? false
: null;
case Horde_Imap_Client::STATUS_RECENT_TOTAL:
case Horde_Imap_Client::STATUS_SYNCMODSEQ:
return 0;
case Horde_Imap_Client::STATUS_PERMFLAGS:
/* If PERMFLAGS is not returned by server, must assume that all
* flags can be changed permanently (RFC 3501 [6.3.1]). */
$flags = isset($this->_status[Horde_Imap_Client::STATUS_FLAGS])
? $this->_status[Horde_Imap_Client::STATUS_FLAGS]
: array();
$flags[] = "\\*";
return $flags;
case Horde_Imap_Client::STATUS_UIDNOTSTICKY:
/* In the absence of explicit uidnotsticky identification, assume
* that UIDs are sticky. */
return false;
case Horde_Imap_Client::STATUS_UNSEEN:
/* If we know there are no messages in the current mailbox, we
* know there are no unseen messages . */
return empty($this->_status[Horde_Imap_Client::STATUS_MESSAGES])
? 0
: null;
default:
return null;
}
}
/**
* Set status information for the mailbox.
*
* @param integer $entry STATUS_* constant.
* @param mixed $value Status information.
*/
public function setStatus($entry, $value)
{
switch ($entry) {
case Horde_Imap_Client::STATUS_FIRSTUNSEEN:
case Horde_Imap_Client::STATUS_HIGHESTMODSEQ:
case Horde_Imap_Client::STATUS_MESSAGES:
case Horde_Imap_Client::STATUS_UNSEEN:
case Horde_Imap_Client::STATUS_UIDNEXT:
case Horde_Imap_Client::STATUS_UIDVALIDITY:
$value = intval($value);
break;
case Horde_Imap_Client::STATUS_RECENT:
/* Keep track of RECENT_TOTAL information. */
$this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL] = isset($this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL])
? ($this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL] + $value)
: intval($value);
break;
case Horde_Imap_Client::STATUS_SYNCMODSEQ:
/* This is only set once per access. */
if (isset($this->_status[$entry])) {
return;
}
$value = intval($value);
break;
case Horde_Imap_Client::STATUS_SYNCFLAGUIDS:
case Horde_Imap_Client::STATUS_SYNCVANISHED:
if (!isset($this->_status[$entry])) {
$this->_status[$entry] = array();
}
$this->_status[$entry] = array_merge($this->_status[$entry], $value);
return;
}
$this->_status[$entry] = $value;
}
/**
* Reset the mailbox information.
*/
public function reset()
{
$keep = array(
Horde_Imap_Client::STATUS_SYNCFLAGUIDS,
Horde_Imap_Client::STATUS_SYNCMODSEQ,
Horde_Imap_Client::STATUS_SYNCVANISHED
);
foreach (array_diff(array_keys($this->_status), $keep) as $val) {
unset($this->_status[$val]);
}
$this->map = new Horde_Imap_Client_Ids_Map();
$this->open = $this->sync = false;
}
}
@@ -1,33 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Interface to allow dynamic generation of server password.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.14.0
*/
interface Horde_Imap_Client_Base_Password
{
/**
* Return the password to use for the server connection.
*
* @return string The password.
*/
public function getPassword();
}
@@ -1,263 +0,0 @@
<?php
/**
* Copyright 2005-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2005-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* An interface to cache data retrieved from the IMAP server.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Cache
{
/**
* Base client object.
*
* @var Horde_Imap_Client_Base
*/
protected $_baseob;
/**
* Storage backend.
*
* @var Horde_Imap_Client_Cache_Backend
*/
protected $_backend;
/**
* Debug output.
*
* @var Horde_Imap_Client_Base_Debug
*/
protected $_debug = false;
/**
* The configuration params.
*
* @var array
*/
protected $_params = array();
/**
* Constructor.
*
* @param array $params Configuration parameters:
* <pre>
* - REQUIRED Parameters:
* - backend: (Horde_Imap_Client_Cache_Backend) The cache backend.
* - baseob: (Horde_Imap_Client_Base) The base client object.
*
* - Optional Parameters:
* - debug: (Horde_Imap_Client_Base_Debug) Debug object.
* DEFAULT: No debug output
* </pre>
*/
public function __construct(array $params = array())
{
$this->_backend = $params['backend'];
$this->_baseob = $params['baseob'];
$this->_backend->setParams(array(
'hostspec' => $this->_baseob->getParam('hostspec'),
'port' => $this->_baseob->getParam('port'),
'username' => $this->_baseob->getParam('username')
));
if (isset($params['debug']) &&
($params['debug'] instanceof Horde_Imap_Client_Base_Debug)) {
$this->_debug = $params['debug'];
$this->_debug->info(sprintf(
'CACHE: Using the %s storage driver.',
get_class($this->_backend)
));
}
}
/**
* Get information from the cache.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $uids The list of message UIDs to retrieve
* information for. If empty, returns the list
* of cached UIDs.
* @param array $fields An array of fields to retrieve. If empty,
* returns all cached fields.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*
* @return array An array of arrays with the UID of the message as the
* key (if found) and the fields as values (will be
* undefined if not found). If $uids is empty, returns the
* full (unsorted) list of cached UIDs.
*/
public function get($mailbox, array $uids = array(), $fields = array(),
$uidvalid = null)
{
$mailbox = strval($mailbox);
if (empty($uids)) {
$ret = $this->_backend->getCachedUids($mailbox, $uidvalid);
} else {
$ret = $this->_backend->get($mailbox, $uids, $fields, $uidvalid);
if ($this->_debug && !empty($ret)) {
$this->_debug->info(sprintf(
'CACHE: Retrieved messages (%s [%s; %s])',
empty($fields) ? 'ALL' : implode(',', $fields),
$mailbox,
$this->_baseob->getIdsOb(array_keys($ret))->tostring_sort
));
}
}
return $ret;
}
/**
* Store information in cache.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $data The list of data to save. The keys are the
* UIDs, the values are an array of information
* to save. If empty, do a check to make sure
* the uidvalidity is still valid.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*/
public function set($mailbox, $data, $uidvalid)
{
$mailbox = strval($mailbox);
if (empty($data)) {
$this->_backend->getMetaData($mailbox, $uidvalid, array('uidvalid'));
} else {
$this->_backend->set($mailbox, $data, $uidvalid);
if ($this->_debug) {
$this->_debug->info(sprintf(
'CACHE: Stored messages [%s; %s]',
$mailbox,
$this->_baseob->getIdsOb(array_keys($data))->tostring_sort
));
}
}
}
/**
* Get metadata information for a mailbox.
*
* @param string $mailbox An IMAP mailbox string.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
* @param array $entries An array of entries to return. If empty,
* returns all metadata.
*
* @return array The requested metadata. Requested entries that do not
* exist will be undefined. The following entries are
* defaults and always present:
* - uidvalid: (integer) The UIDVALIDITY of the mailbox.
*/
public function getMetaData($mailbox, $uidvalid = null,
array $entries = array())
{
return $this->_backend->getMetaData(strval($mailbox), $uidvalid, $entries);
}
/**
* Set metadata information for a mailbox.
*
* @param string $mailbox An IMAP mailbox string.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
* @param array $data The list of data to save. The keys are the
* metadata IDs, the values are the associated
* data. The following labels are reserved:
* 'uidvalid'.
*/
public function setMetaData($mailbox, $uidvalid, array $data = array())
{
unset($data['uidvalid']);
if (!empty($data)) {
if (!empty($uidvalid)) {
$data['uidvalid'] = $uidvalid;
}
$mailbox = strval($mailbox);
$this->_backend->setMetaData($mailbox, $data);
if ($this->_debug) {
$this->_debug->info(sprintf(
'CACHE: Stored metadata (%s [%s])',
implode(',', array_keys($data)),
$mailbox
));
}
}
}
/**
* Delete messages in the cache.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $uids The list of message UIDs to delete.
*/
public function deleteMsgs($mailbox, $uids)
{
if (empty($uids)) {
return;
}
$mailbox = strval($mailbox);
$this->_backend->deleteMsgs($mailbox, $uids);
if ($this->_debug) {
$this->_debug->info(sprintf(
'CACHE: Deleted messages [%s; %s]',
$mailbox,
$this->_baseob->getIdsOb($uids)->tostring_sort
));
}
}
/**
* Delete a mailbox from the cache.
*
* @param string $mbox The mailbox to delete.
*/
public function deleteMailbox($mbox)
{
$mbox = strval($mbox);
$this->_backend->deleteMailbox($mbox);
if ($this->_debug) {
$this->_debug->info(sprintf(
'CACHE: Deleted mailbox [%s]',
$mbox
));
}
}
/**
* Clear the cache.
*
* @since 2.9.0
*
* @param integer $lifetime Only delete entries older than this (in
* seconds). If null, deletes all entries.
*/
public function clear($lifetime = null)
{
$this->_backend->clear($lifetime);
}
}
@@ -1,177 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* The abstract backend class for storing IMAP cached data.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
abstract class Horde_Imap_Client_Cache_Backend implements Serializable
{
/**
* Configuration paramters.
* Values set by the base Cache object: hostspec, port, username
*
* @var array
*/
protected $_params = array();
/**
* Constructor.
*
* @param array $params Configuration parameters.
*/
public function __construct(array $params = array())
{
$this->setParams($params);
$this->_initOb();
}
/**
* Initialization tasks.
*/
protected function _initOb()
{
}
/**
* Add configuration parameters.
*
* @param array $params Configuration parameters.
*/
public function setParams(array $params = array())
{
$this->_params = array_merge($this->_params, $params);
}
/**
* Get information from the cache for a set of UIDs.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $uids The list of message UIDs to retrieve
* information for.
* @param array $fields An array of fields to retrieve. If empty,
* returns all cached fields.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*
* @return array An array of arrays with the UID of the message as the
* key (if found) and the fields as values (will be
* undefined if not found).
*/
abstract public function get($mailbox, $uids, $fields, $uidvalid);
/**
* Get the list of cached UIDs.
*
* @param string $mailbox An IMAP mailbox string.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*
* @return array The (unsorted) list of cached UIDs.
*/
abstract public function getCachedUids($mailbox, $uidvalid);
/**
* Store data in cache.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $data The list of data to save. The keys are the
* UIDs, the values are an array of information
* to save.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*/
abstract public function set($mailbox, $data, $uidvalid);
/**
* Get metadata information for a mailbox.
*
* @param string $mailbox An IMAP mailbox string.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
* @param array $entries An array of entries to return. If empty,
* returns all metadata.
*
* @return array The requested metadata. Requested entries that do not
* exist will be undefined. The following entries are
* defaults and always present:
* - uidvalid: (integer) The UIDVALIDITY of the mailbox.
*/
abstract public function getMetaData($mailbox, $uidvalid, $entries);
/**
* Set metadata information for a mailbox.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $data The list of data to save. The keys are the
* metadata IDs, the values are the associated
* data. (If present, uidvalidity appears as
* the 'uidvalid' key in $data.)
*/
abstract public function setMetaData($mailbox, $data);
/**
* Delete messages in the cache.
*
* @param string $mailbox An IMAP mailbox string.
* @param array $uids The list of message UIDs to delete.
*/
abstract public function deleteMsgs($mailbox, $uids);
/**
* Delete a mailbox from the cache.
*
* @param string $mailbox The mailbox to delete.
*/
abstract public function deleteMailbox($mailbox);
/**
* Clear the cache.
*
* @param integer $lifetime Only delete entries older than this (in
* seconds). If null, deletes all entries.
*/
abstract public function clear($lifetime);
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$this->__unserialize(unserialize($data));
}
/**
* @return array
*/
public function __serialize()
{
return $this->_params;
}
public function __unserialize(array $data)
{
$this->_params = $data;
}
}
@@ -1,514 +0,0 @@
<?php
/**
* Copyright 2005-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2005-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* A Horde_Cache implementation for caching IMAP/POP data.
* Requires the Horde_Cache package.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2005-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Cache_Backend_Cache
extends Horde_Imap_Client_Cache_Backend
{
/** Cache structure version. */
const VERSION = 3;
/**
* The cache object.
*
* @var Horde_Cache
*/
protected $_cache;
/**
* The working data for the current pageload. All changes take place to
* this data.
*
* @var array
*/
protected $_data = array();
/**
* The list of cache slices loaded.
*
* @var array
*/
protected $_loaded = array();
/**
* The mapping of UIDs to slices.
*
* @var array
*/
protected $_slicemap = array();
/**
* The list of items to update:
* - add: (array) List of IDs that were added.
* - slice: (array) List of slices that were modified.
* - slicemap: (boolean) Was slicemap info changed?
*
* @var array
*/
protected $_update = array();
/**
* Constructor.
*
* @param array $params Configuration parameters:
* <pre>
* - REQUIRED Parameters:
* - cacheob: (Horde_Cache) The cache object to use.
*
* - Optional Parameters:
* - lifetime: (integer) The lifetime of the cache data (in seconds).
* DEFAULT: 1 week (604800 seconds)
* - slicesize: (integer) The slicesize to use.
* DEFAULT: 50
* </pre>
*/
public function __construct(array $params = array())
{
// Default parameters.
$params = array_merge(array(
'lifetime' => 604800,
'slicesize' => 50
), array_filter($params));
if (!isset($params['cacheob'])) {
throw new InvalidArgumentException('Missing cacheob parameter.');
}
foreach (array('lifetime', 'slicesize') as $val) {
$params[$val] = intval($params[$val]);
}
parent::__construct($params);
}
/**
* Initialization tasks.
*/
protected function _initOb()
{
$this->_cache = $this->_params['cacheob'];
register_shutdown_function(array($this, 'save'));
}
/**
* Updates the cache.
*/
public function save()
{
$lifetime = $this->_params['lifetime'];
foreach ($this->_update as $mbox => $val) {
$s = &$this->_slicemap[$mbox];
try {
if (!empty($val['add'])) {
if ($s['c'] <= $this->_params['slicesize']) {
$val['slice'][] = $s['i'];
$this->_loadSlice($mbox, $s['i']);
}
$val['slicemap'] = true;
foreach (array_keys(array_flip($val['add'])) as $uid) {
if ($s['c']++ > $this->_params['slicesize']) {
$s['c'] = 0;
$val['slice'][] = ++$s['i'];
$this->_loadSlice($mbox, $s['i']);
}
$s['s'][$uid] = $s['i'];
}
}
if (!empty($val['slice'])) {
$d = &$this->_data[$mbox];
$val['slicemap'] = true;
foreach (array_keys(array_flip($val['slice'])) as $slice) {
$data = array();
foreach (moodle_array_keys_filter($s['s'], $slice) as $uid) {
$data[$uid] = is_array($d[$uid])
? serialize($d[$uid])
: $d[$uid];
}
$this->_cache->set($this->_getCid($mbox, $slice), serialize($data), $lifetime);
}
}
if (!empty($val['slicemap'])) {
$this->_cache->set($this->_getCid($mbox, 'slicemap'), serialize($s), $lifetime);
}
} catch (Horde_Exception $e) {
}
}
$this->_update = array();
}
/**
*/
public function get($mailbox, $uids, $fields, $uidvalid)
{
$ret = array();
$this->_loadUids($mailbox, $uids, $uidvalid);
if (empty($this->_data[$mailbox])) {
return $ret;
}
if (!empty($fields)) {
$fields = array_flip($fields);
}
$ptr = &$this->_data[$mailbox];
foreach (array_intersect($uids, array_keys($ptr)) as $val) {
if (is_string($ptr[$val])) {
try {
$ptr[$val] = @unserialize($ptr[$val]);
} catch (Exception $e) {}
}
$ret[$val] = (empty($fields) || empty($ptr[$val]))
? $ptr[$val]
: array_intersect_key($ptr[$val], $fields);
}
return $ret;
}
/**
*/
public function getCachedUids($mailbox, $uidvalid)
{
$this->_loadSliceMap($mailbox, $uidvalid);
return array_unique(array_merge(
array_keys($this->_slicemap[$mailbox]['s']),
(isset($this->_update[$mailbox]) ? $this->_update[$mailbox]['add'] : array())
));
}
/**
*/
public function set($mailbox, $data, $uidvalid)
{
$update = array_keys($data);
try {
$this->_loadUids($mailbox, $update, $uidvalid);
} catch (Horde_Imap_Client_Exception $e) {
// Ignore invalidity - just start building the new cache
}
$d = &$this->_data[$mailbox];
$s = &$this->_slicemap[$mailbox]['s'];
$add = $updated = array();
foreach ($data as $k => $v) {
if (isset($d[$k])) {
if (is_string($d[$k])) {
try {
$d[$k] = @unserialize($d[$k]);
} catch (Exception $e) {}
}
$d[$k] = is_array($d[$k])
? array_merge($d[$k], $v)
: $v;
if (isset($s[$k])) {
$updated[$s[$k]] = true;
}
} else {
$d[$k] = $v;
$add[] = $k;
}
}
$this->_toUpdate($mailbox, 'add', $add);
$this->_toUpdate($mailbox, 'slice', array_keys($updated));
}
/**
*/
public function getMetaData($mailbox, $uidvalid, $entries)
{
$this->_loadSliceMap($mailbox, $uidvalid);
return empty($entries)
? $this->_slicemap[$mailbox]['d']
: array_intersect_key($this->_slicemap[$mailbox]['d'], array_flip($entries));
}
/**
*/
public function setMetaData($mailbox, $data)
{
$this->_loadSliceMap($mailbox, isset($data['uidvalid']) ? $data['uidvalid'] : null);
$this->_slicemap[$mailbox]['d'] = array_merge($this->_slicemap[$mailbox]['d'], $data);
$this->_toUpdate($mailbox, 'slicemap', true);
}
/**
*/
public function deleteMsgs($mailbox, $uids)
{
if (empty($uids)) {
return;
}
$this->_loadSliceMap($mailbox);
$slicemap = &$this->_slicemap[$mailbox];
$deleted = array_intersect_key($slicemap['s'], array_flip($uids));
if (isset($this->_update[$mailbox])) {
$this->_update[$mailbox]['add'] = array_diff(
$this->_update[$mailbox]['add'],
$uids
);
}
if (empty($deleted)) {
return;
}
$this->_loadUids($mailbox, array_keys($deleted));
$d = &$this->_data[$mailbox];
foreach (array_keys($deleted) as $id) {
unset($d[$id], $slicemap['s'][$id]);
}
foreach (array_unique($deleted) as $slice) {
/* Get rid of slice if less than 10% of capacity. */
if (($slice != $slicemap['i']) &&
($slice_uids = moodle_array_keys_filter($slicemap['s'], $slice)) &&
($this->_params['slicesize'] * 0.1) > count($slice_uids)) {
$this->_toUpdate($mailbox, 'add', $slice_uids);
$this->_cache->expire($this->_getCid($mailbox, $slice));
foreach ($slice_uids as $val) {
unset($slicemap['s'][$val]);
}
} else {
$this->_toUpdate($mailbox, 'slice', array($slice));
}
}
}
/**
*/
public function deleteMailbox($mailbox)
{
$this->_loadSliceMap($mailbox);
$this->_deleteMailbox($mailbox);
}
/**
*/
public function clear($lifetime)
{
$this->_cache->clear();
$this->_data = $this->_loaded = $this->_slicemap = $this->_update = array();
}
/**
* Create the unique ID used to store the data in the cache.
*
* @param string $mailbox The mailbox to cache.
* @param string $slice The cache slice.
*
* @return string The cache ID.
*/
protected function _getCid($mailbox, $slice)
{
return implode('|', array(
'horde_imap_client',
$this->_params['username'],
$mailbox,
$this->_params['hostspec'],
$this->_params['port'],
$slice,
self::VERSION
));
}
/**
* Delete a mailbox from the cache.
*
* @param string $mbox The mailbox to delete.
*/
protected function _deleteMailbox($mbox)
{
foreach (array_merge(array_keys(array_flip($this->_slicemap[$mbox]['s'])), array('slicemap')) as $slice) {
$cid = $this->_getCid($mbox, $slice);
$this->_cache->expire($cid);
unset($this->_loaded[$cid]);
}
unset(
$this->_data[$mbox],
$this->_slicemap[$mbox],
$this->_update[$mbox]
);
}
/**
* Load UIDs by regenerating from the cache.
*
* @param string $mailbox The mailbox to load.
* @param array $uids The UIDs to load.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*/
protected function _loadUids($mailbox, $uids, $uidvalid = null)
{
if (!isset($this->_data[$mailbox])) {
$this->_data[$mailbox] = array();
}
$this->_loadSliceMap($mailbox, $uidvalid);
if (!empty($uids)) {
foreach (array_unique(array_intersect_key($this->_slicemap[$mailbox]['s'], array_flip($uids))) as $slice) {
$this->_loadSlice($mailbox, $slice);
}
}
}
/**
* Load UIDs from a cache slice.
*
* @param string $mailbox The mailbox to load.
* @param integer $slice The slice to load.
*/
protected function _loadSlice($mailbox, $slice)
{
$cache_id = $this->_getCid($mailbox, $slice);
if (!empty($this->_loaded[$cache_id])) {
return;
}
if (($data = $this->_cache->get($cache_id, 0)) !== false) {
try {
$data = @unserialize($data);
} catch (Exception $e) {}
}
if (($data !== false) && is_array($data)) {
$this->_data[$mailbox] += $data;
$this->_loaded[$cache_id] = true;
} else {
$ptr = &$this->_slicemap[$mailbox];
// Slice data is corrupt; remove from slicemap.
foreach (moodle_array_keys_filter($ptr['s'], $slice) as $val) {
unset($ptr['s'][$val]);
}
if ($slice == $ptr['i']) {
$ptr['c'] = 0;
}
}
}
/**
* Load the slicemap for a given mailbox. The slicemap contains
* the uidvalidity information, the UIDs->slice lookup table, and any
* metadata that needs to be saved for the mailbox.
*
* @param string $mailbox The mailbox.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*/
protected function _loadSliceMap($mailbox, $uidvalid = null)
{
if (!isset($this->_slicemap[$mailbox]) &&
(($data = $this->_cache->get($this->_getCid($mailbox, 'slicemap'), 0)) !== false)) {
try {
if (($slice = @unserialize($data)) &&
is_array($slice)) {
$this->_slicemap[$mailbox] = $slice;
}
} catch (Exception $e) {}
}
if (isset($this->_slicemap[$mailbox])) {
$ptr = &$this->_slicemap[$mailbox];
if (is_null($ptr['d']['uidvalid'])) {
$ptr['d']['uidvalid'] = $uidvalid;
return;
} elseif (!is_null($uidvalid) &&
($ptr['d']['uidvalid'] != $uidvalid)) {
$this->_deleteMailbox($mailbox);
} else {
return;
}
}
$this->_slicemap[$mailbox] = array(
// Tracking count for purposes of determining slices
'c' => 0,
// Metadata storage
// By default includes UIDVALIDITY of mailbox.
'd' => array('uidvalid' => $uidvalid),
// The ID of the last slice.
'i' => 0,
// The slice list.
's' => array()
);
}
/**
* Add update entry for a mailbox.
*
* @param string $mailbox The mailbox.
* @param string $type 'add', 'slice', or 'slicemap'.
* @param mixed $data The data to update.
*/
protected function _toUpdate($mailbox, $type, $data)
{
if (!isset($this->_update[$mailbox])) {
$this->_update[$mailbox] = array(
'add' => array(),
'slice' => array()
);
}
$this->_update[$mailbox][$type] = ($type == 'slicemap')
? $data
: array_merge($this->_update[$mailbox][$type], $data);
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return $this->__serialize();
}
/**
* @return array
*/
public function __serialize()
{
$this->save();
return parent::__serialize();
}
}
@@ -1,407 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* A SQL database implementation for caching IMAP/POP data.
* Requires the Horde_Db package.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Cache_Backend_Db
extends Horde_Imap_Client_Cache_Backend
{
/** SQL table names. */
const BASE_TABLE = 'horde_imap_client_data';
const MD_TABLE = 'horde_imap_client_metadata';
const MSG_TABLE = 'horde_imap_client_message';
/**
* Handle for the database connection.
*
* @var Horde_Db_Adapter
*/
protected $_db;
/**
* Constructor.
*
* @param array $params Configuration parameters:
* <pre>
* - REQUIRED Parameters:
* - db: (Horde_Db_Adapter) DB object.
* </pre>
*/
public function __construct(array $params = array())
{
if (!isset($params['db'])) {
throw new InvalidArgumentException('Missing db parameter.');
}
parent::__construct($params);
}
/**
*/
protected function _initOb()
{
$this->_db = $this->_params['db'];
}
/**
*/
public function get($mailbox, $uids, $fields, $uidvalid)
{
$this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
$query = $this->_baseSql($mailbox, self::MSG_TABLE);
$query[0] = 'SELECT t.data, t.msguid ' . $query[0];
$uid_query = array();
foreach ($uids as $val) {
$uid_query[] = 't.msguid = ?';
$query[1][] = strval($val);
}
$query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')';
$compress = new Horde_Compress_Fast();
$out = array();
try {
$columns = $this->_db->columns(self::MSG_TABLE);
$res = $this->_db->select($query[0], $query[1]);
foreach ($res as $row) {
try {
$out[$row['msguid']] = @unserialize($compress->decompress(
$columns['data']->binaryToString($row['data'])
));
} catch (Exception $e) {}
}
} catch (Horde_Db_Exception $e) {}
return $out;
}
/**
*/
public function getCachedUids($mailbox, $uidvalid)
{
$this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
$query = $this->_baseSql($mailbox, self::MSG_TABLE);
$query[0] = 'SELECT DISTINCT t.msguid ' . $query[0];
try {
return $this->_db->selectValues($query[0], $query[1]);
} catch (Horde_Db_Exception $e) {
return array();
}
}
/**
*/
public function set($mailbox, $data, $uidvalid)
{
if ($uid = $this->_getUid($mailbox)) {
$res = $this->get($mailbox, array_keys($data), array(), $uidvalid);
} else {
$res = array();
$uid = $this->_createUid($mailbox);
}
$compress = new Horde_Compress_Fast();
foreach ($data as $key => $val) {
if (isset($res[$key])) {
try {
/* Update */
$this->_db->updateBlob(
self::MSG_TABLE,
array('data' => new Horde_Db_Value_Binary($compress->compress(serialize(array_merge($res[$key], $val))))),
array(
'messageid = ? AND msguid = ?',
array($uid, strval($key))
)
);
} catch (Horde_Db_Exception $e) {}
} else {
/* Insert */
try {
$this->_db->insertBlob(
self::MSG_TABLE,
array(
'data' => new Horde_Db_Value_Binary($compress->compress(serialize($val))),
'msguid' => strval($key),
'messageid' => $uid
)
);
} catch (Horde_Db_Exception $e) {}
}
}
/* Update modified time. */
try {
$this->_db->update(
sprintf(
'UPDATE %s SET modified = ? WHERE messageid = ?',
self::BASE_TABLE
),
array(time(), $uid)
);
} catch (Horde_Db_Exception $e) {}
/* Update uidvalidity. */
$this->setMetaData($mailbox, array('uidvalid' => $uidvalid));
}
/**
*/
public function getMetaData($mailbox, $uidvalid, $entries)
{
$query = $this->_baseSql($mailbox, self::MD_TABLE);
$query[0] = 'SELECT t.field, t.data ' . $query[0];
if (!empty($entries)) {
$entries[] = 'uidvalid';
$entry_query = array();
foreach (array_unique($entries) as $val) {
$entry_query[] = 't.field = ?';
$query[1][] = $val;
}
$query[0] .= ' AND (' . implode(' OR ', $entry_query) . ')';
}
try {
if ($res = $this->_db->selectAssoc($query[0], $query[1])) {
$columns = $this->_db->columns(self::MD_TABLE);
foreach ($res as $key => $val) {
switch ($key) {
case 'uidvalid':
$res[$key] = $columns['data']->binaryToString($val);
break;
default:
try {
$res[$key] = @unserialize(
$columns['data']->binaryToString($val)
);
} catch (Exception $e) {}
break;
}
}
if (is_null($uidvalid) ||
!isset($res['uidvalid']) ||
($res['uidvalid'] == $uidvalid)) {
return $res;
}
$this->deleteMailbox($mailbox);
}
} catch (Horde_Db_Exception $e) {}
return array();
}
/**
*/
public function setMetaData($mailbox, $data)
{
if (!($uid = $this->_getUid($mailbox))) {
$uid = $this->_createUid($mailbox);
}
$query = sprintf('SELECT field FROM %s where messageid = ?', self::MD_TABLE);
$values = array($uid);
try {
$fields = $this->_db->selectValues($query, $values);
} catch (Horde_Db_Exception $e) {
return;
}
foreach ($data as $key => $val) {
$val = new Horde_Db_Value_Binary(($key == 'uidvalid') ? $val : serialize($val));
if (in_array($key, $fields)) {
/* Update */
try {
$this->_db->updateBlob(
self::MD_TABLE,
array('data' => $val),
array('field = ? AND messageid = ?', array($key, $uid))
);
} catch (Horde_Db_Exception $e) {}
} else {
/* Insert */
try {
$this->_db->insertBlob(
self::MD_TABLE,
array('data' => $val, 'field' => $key, 'messageid' => $uid)
);
} catch (Horde_Db_Exception $e) {}
}
}
}
/**
*/
public function deleteMsgs($mailbox, $uids)
{
if (empty($uids)) {
return;
}
$query = $this->_baseSql($mailbox);
$query[0] = sprintf(
'DELETE FROM %s WHERE messageid IN (SELECT messageid ' . $query[0] . ')',
self::MSG_TABLE
);
$uid_query = array();
foreach ($uids as $val) {
$uid_query[] = 'msguid = ?';
$query[1][] = strval($val);
}
$query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')';
try {
$this->_db->delete($query[0], $query[1]);
} catch (Horde_Db_Exception $e) {}
}
/**
*/
public function deleteMailbox($mailbox)
{
if (is_null($uid = $this->_getUid($mailbox))) {
return;
}
foreach (array(self::BASE_TABLE, self::MD_TABLE, self::MSG_TABLE) as $val) {
try {
$this->_db->delete(
sprintf('DELETE FROM %s WHERE messageid = ?', $val),
array($uid)
);
} catch (Horde_Db_Exception $e) {}
}
}
/**
*/
public function clear($lifetime)
{
if (is_null($lifetime)) {
try {
$this->_db->delete(sprintf('DELETE FROM %s', self::BASE_TABLE));
$this->_db->delete(sprintf('DELETE FROM %s', self::MD_TABLE));
$this->_db->delete(sprintf('DELETE FROM %s', self::MSG_TABLE));
} catch (Horde_Db_Exception $e) {}
return;
}
$purge = time() - $lifetime;
$sql = 'DELETE FROM %s WHERE messageid IN (SELECT messageid FROM %s WHERE modified < ?)';
foreach (array(self::MD_TABLE, self::MSG_TABLE) as $val) {
try {
$this->_db->delete(
sprintf($sql, $val, self::BASE_TABLE),
array($purge)
);
} catch (Horde_Db_Exception $e) {
}
}
try {
$this->_db->delete(
sprintf('DELETE FROM %s WHERE modified < ?', self::BASE_TABLE),
array($purge)
);
} catch (Horde_Db_Exception $e) {
}
}
/**
* Prepare the base SQL query.
*
* @param string $mailbox The mailbox.
* @param string $join The table to join with the base table.
*
* @return array SQL query and bound parameters.
*/
protected function _baseSql($mailbox, $join = null)
{
$sql = sprintf('FROM %s d', self::BASE_TABLE);
if (!is_null($join)) {
$sql .= sprintf(' INNER JOIN %s t ON d.messageid = t.messageid', $join);
}
return array(
$sql . ' WHERE d.hostspec = ? AND d.port = ? AND d.username = ? AND d.mailbox = ?',
array(
$this->_params['hostspec'],
$this->_params['port'],
$this->_params['username'],
$mailbox
)
);
}
/**
* @param string $mailbox
*
* @return string UID from base table.
*/
protected function _getUid($mailbox)
{
$query = $this->_baseSql($mailbox);
$query[0] = 'SELECT d.messageid ' . $query[0];
try {
return $this->_db->selectValue($query[0], $query[1]);
} catch (Horde_Db_Exception $e) {
return null;
}
}
/**
* @param string $mailbox
*
* @return string UID from base table.
*/
protected function _createUid($mailbox)
{
return $this->_db->insert(
sprintf(
'INSERT INTO %s (hostspec, mailbox, port, username) ' .
'VALUES (?, ?, ?, ?)',
self::BASE_TABLE
),
array(
$this->_params['hostspec'],
$mailbox,
$this->_params['port'],
$this->_params['username']
)
);
}
}
@@ -1,421 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* A Horde_HashTable implementation for caching IMAP/POP data.
* Requires the Horde_HashTable and Horde_Pack packages.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.17.0
*/
class Horde_Imap_Client_Cache_Backend_Hashtable
extends Horde_Imap_Client_Cache_Backend
{
/** Separator for CID between mailbox and UID. */
const CID_SEPARATOR = '|';
/**
* The working data for the current pageload. All changes take place to
* this data.
*
* @var array
*/
protected $_data = array();
/**
* HashTable object.
*
* @var Horde_HashTable
*/
protected $_hash;
/**
* Mailbox level data.
*
* @var array
*/
protected $_mbox = array();
/**
* Horde_Pack singleton object.
*
* @var Horde_Pack
*/
protected $_pack;
/**
* List of mailbox/UIDs to update.
* Keys are mailboxes. Values are arrays with three possible keys:
* <pre>
* - d: UIDs to delete
* - m: Was metadata updated?
* - u: UIDs to update
* </pre>
*
* @var array
*/
protected $_update = array();
/**
* Constructor.
*
* @param array $params Configuration parameters:
* <pre>
* - REQUIRED parameters:
* - hashtable: (Horde_HashTable) A HashTable object.
*
* - Optional Parameters:
* - lifetime: (integer) The lifetime of the cache data (in seconds).
* DEFAULT: 604800 seconds (1 week) [@since 2.19.0]
* </pre>
*/
public function __construct(array $params = array())
{
if (!isset($params['hashtable'])) {
throw new InvalidArgumentException('Missing hashtable parameter.');
}
parent::__construct(array_merge(array(
'lifetime' => 604800
), $params));
}
/**
*/
protected function _initOb()
{
$this->_hash = $this->_params['hashtable'];
$this->_pack = new Horde_Pack();
register_shutdown_function(array($this, 'save'));
}
/**
*/
public function get($mailbox, $uids, $fields, $uidvalid)
{
$ret = array();
if (empty($uids)) {
return $ret;
}
$this->_loadUids($mailbox, $uids, $uidvalid);
if (empty($this->_data[$mailbox])) {
return $ret;
}
if (!empty($fields)) {
$fields = array_flip($fields);
}
$ptr = &$this->_data[$mailbox];
$to_delete = array();
foreach ($uids as $val) {
if (isset($ptr[$val])) {
if (is_string($ptr[$val])) {
try {
$ptr[$val] = $this->_pack->unpack($ptr[$val]);
} catch (Horde_Pack_Exception $e) {
$to_delete[] = $val;
continue;
}
}
$ret[$val] = (empty($fields) || empty($ptr[$val]))
? $ptr[$val]
: array_intersect_key($ptr[$val], $fields);
} else {
$to_delete[] = $val;
}
}
$this->deleteMsgs($mailbox, $to_delete);
return $ret;
}
/**
*/
public function getCachedUids($mailbox, $uidvalid)
{
$this->_loadMailbox($mailbox, $uidvalid);
return $this->_mbox[$mailbox]['u']->ids;
}
/**
*/
public function set($mailbox, $data, $uidvalid)
{
$this->_loadUids($mailbox, array_keys($data), $uidvalid);
$d = &$this->_data[$mailbox];
$to_add = array();
foreach ($data as $k => $v) {
if (isset($d[$k]) && is_string($d[$k])) {
try {
$d[$k] = $this->_pack->unpack($d[$k]);
} catch (Horde_Pack_Exception $e) {
continue;
}
}
$d[$k] = (isset($d[$k]) && is_array($d[$k]))
? array_merge($d[$k], $v)
: $v;
$this->_update[$mailbox]['u'][$k] = true;
unset($this->_update[$mailbox]['d'][$k]);
$to_add[] = $k;
}
if (!empty($to_add)) {
$this->_mbox[$mailbox]['u']->add($to_add);
$this->_update[$mailbox]['m'] = true;
}
}
/**
*/
public function getMetaData($mailbox, $uidvalid, $entries)
{
$this->_loadMailbox($mailbox, $uidvalid);
return empty($entries)
? $this->_mbox[$mailbox]['d']
: array_intersect_key($this->_mbox[$mailbox]['d'], array_flip($entries));
}
/**
*/
public function setMetaData($mailbox, $data)
{
$this->_loadMailbox($mailbox, isset($data['uidvalid']) ? $data['uidvalid'] : null);
$this->_mbox[$mailbox]['d'] = array_merge(
$this->_mbox[$mailbox]['d'],
$data
);
$this->_update[$mailbox]['m'] = true;
}
/**
*/
public function deleteMsgs($mailbox, $uids)
{
if (empty($uids)) {
return;
}
$this->_loadMailbox($mailbox);
foreach ($uids as $val) {
unset(
$this->_data[$mailbox][$val],
$this->_update[$mailbox]['u'][$val]
);
$this->_update[$mailbox]['d'][$val] = true;
}
$this->_mbox[$mailbox]['u']->remove($uids);
$this->_update[$mailbox]['m'] = true;
}
/**
*/
public function deleteMailbox($mailbox)
{
/* Do this action immediately, instead of at shutdown. Makes coding
* simpler. */
$this->_loadMailbox($mailbox);
$this->_hash->delete(array_merge(
array($this->_getCid($mailbox)),
array_values($this->_getMsgCids($mailbox, $this->_mbox[$mailbox]['u']))
));
unset(
$this->_data[$mailbox],
$this->_mbox[$mailbox],
$this->_update[$mailbox]
);
}
/**
*/
public function clear($lifetime)
{
/* Only can clear mailboxes we know about. */
foreach (array_keys($this->_mbox) as $val) {
$this->deleteMailbox($val);
}
$this->_data = $this->_mbox = $this->_update = array();
}
/**
* Updates the cache.
*/
public function save()
{
foreach ($this->_update as $mbox => $val) {
try {
if (!empty($val['u'])) {
$ptr = &$this->_data[$mbox];
foreach ($this->_getMsgCids($mbox, array_keys($val['u'])) as $k2 => $v2) {
try {
$this->_hash->set(
$v2,
$this->_pack->pack($ptr[$k2]),
array('expire' => $this->_params['lifetime'])
);
} catch (Horde_Pack_Exception $e) {
$this->deleteMsgs($mbox, array($v2));
$val['d'][] = $v2;
}
}
}
if (!empty($val['d'])) {
$this->_hash->delete(array_values(
$this->_getMsgCids($mbox, $val['d'])
));
}
if (!empty($val['m'])) {
try {
$this->_hash->set(
$this->_getCid($mbox),
$this->_pack->pack($this->_mbox[$mbox]),
array('expire' => $this->_params['lifetime'])
);
} catch (Horde_Pack_Exception $e) {}
}
} catch (Horde_Exception $e) {
}
}
$this->_update = array();
}
/**
* Loads basic mailbox information.
*
* @param string $mailbox The mailbox to load.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*/
protected function _loadMailbox($mailbox, $uidvalid = null)
{
if (!isset($this->_mbox[$mailbox]) &&
($ob = $this->_hash->get($this->_getCid($mailbox)))) {
try {
$this->_mbox[$mailbox] = $this->_pack->unpack($ob);
} catch (Horde_Pack_Exception $e) {}
}
if (isset($this->_mbox[$mailbox])) {
if (is_null($uidvalid) ||
($uidvalid == $this->_mbox[$mailbox]['d']['uidvalid'])) {
return;
}
$this->deleteMailbox($mailbox);
}
$this->_mbox[$mailbox] = array(
// Metadata storage
// By default includes UIDVALIDITY of mailbox.
'd' => array('uidvalid' => $uidvalid),
// List of UIDs
'u' => new Horde_Imap_Client_Ids()
);
}
/**
* Load UIDs by regenerating from the cache.
*
* @param string $mailbox The mailbox to load.
* @param array $uids The UIDs to load.
* @param integer $uidvalid The IMAP uidvalidity value of the mailbox.
*/
protected function _loadUids($mailbox, $uids, $uidvalid = null)
{
if (!isset($this->_data[$mailbox])) {
$this->_data[$mailbox] = array();
}
$this->_loadMailbox($mailbox, $uidvalid);
if (empty($uids)) {
return;
}
$ptr = &$this->_data[$mailbox];
$load = array_flip(
array_diff_key(
$this->_getMsgCids(
$mailbox,
array_unique(array_intersect($this->_mbox[$mailbox]['u']->ids, $uids))
),
$this->_data[$mailbox]
)
);
foreach (array_filter($this->_hash->get(array_keys($load))) as $key => $val) {
$ptr[$load[$key]] = $val;
}
}
/**
* Create the unique ID used to store the mailbox data in the cache.
*
* @param string $mailbox The mailbox to cache.
*
* @return string The cache ID.
*/
protected function _getCid($mailbox)
{
return implode(self::CID_SEPARATOR, array(
'horde_imap_client',
$this->_params['username'],
$mailbox,
$this->_params['hostspec'],
$this->_params['port']
));
}
/**
* Return a list of cache IDs for mailbox/UID pairs.
*
* @param string $mailbox The mailbox to cache.
* @param array $ids The UID list.
*
* @return array List of UIDs => cache IDs.
*/
protected function _getMsgCids($mailbox, $ids)
{
$cid = $this->_getCid($mailbox);
$out = array();
foreach ($ids as $val) {
$out[$val] = $cid . self::CID_SEPARATOR . $val;
}
return $out;
}
}
@@ -1,440 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* A MongoDB database implementation for caching IMAP/POP data.
*
* Requires the Horde_Mongo class.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Cache_Backend_Mongo
extends Horde_Imap_Client_Cache_Backend
implements Horde_Mongo_Collection_Index
{
/** Mongo collection names. */
const BASE = 'horde_imap_client_cache_data';
const MD = 'horde_imap_client_cache_metadata';
const MSG = 'horde_imap_client_cache_message';
/** Mongo field names: BASE collection. */
const BASE_HOSTSPEC = 'hostspec';
const BASE_MAILBOX = 'mailbox';
const BASE_MODIFIED = 'modified';
const BASE_PORT = 'port';
const BASE_UID = 'data';
const BASE_USERNAME = 'username';
/** Mongo field names: MD collection. */
const MD_DATA = 'data';
const MD_FIELD = 'field';
const MD_UID = 'uid';
/** Mongo field names: MSG collection. */
const MSG_DATA = 'data';
const MSG_MSGUID = 'msguid';
const MSG_UID = 'uid';
/**
* The MongoDB object for the cache data.
*
* @var MongoDB
*/
protected $_db;
/**
* The list of indices.
*
* @var array
*/
protected $_indices = array(
self::BASE => array(
'base_index_1' => array(
self::BASE_HOSTSPEC => 1,
self::BASE_MAILBOX => 1,
self::BASE_PORT => 1,
self::BASE_USERNAME => 1,
)
),
self::MSG => array(
'msg_index_1' => array(
self::MSG_MSGUID => 1,
self::MSG_UID => 1
)
)
);
/**
* Constructor.
*
* @param array $params Configuration parameters:
* <pre>
* - REQUIRED parameters:
* - mongo_db: (Horde_Mongo_Client) A MongoDB client object.
* </pre>
*/
public function __construct(array $params = array())
{
if (!isset($params['mongo_db'])) {
throw new InvalidArgumentException('Missing mongo_db parameter.');
}
parent::__construct($params);
}
/**
*/
protected function _initOb()
{
$this->_db = $this->_params['mongo_db']->selectDB(null);
}
/**
*/
public function get($mailbox, $uids, $fields, $uidvalid)
{
$this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
if (!($uid = $this->_getUid($mailbox))) {
return array();
}
$out = array();
$query = array(
self::MSG_MSGUID => array('$in' => array_map('strval', $uids)),
self::MSG_UID => $uid
);
try {
$cursor = $this->_db->selectCollection(self::MSG)->find(
$query,
array(self::MSG_DATA => true, self::MSG_MSGUID => true)
);
foreach ($cursor as $val) {
try {
$out[$val[self::MSG_MSGUID]] = $this->_value($val[self::MSG_DATA]);
} catch (Exception $e) {}
}
} catch (MongoException $e) {}
return $out;
}
/**
*/
public function getCachedUids($mailbox, $uidvalid)
{
$this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
if (!($uid = $this->_getUid($mailbox))) {
return array();
}
$out = array();
$query = array(
self::MSG_UID => $uid
);
try {
$cursor = $this->_db->selectCollection(self::MSG)->find(
$query, array(self::MSG_MSGUID => true)
);
foreach ($cursor as $val) {
$out[] = $val[self::MSG_MSGUID];
}
} catch (MongoException $e) {}
return $out;
}
/**
*/
public function set($mailbox, $data, $uidvalid)
{
if ($uid = $this->_getUid($mailbox)) {
$res = $this->get($mailbox, array_keys($data), array(), $uidvalid);
} else {
$res = array();
$uid = $this->_createUid($mailbox);
}
$coll = $this->_db->selectCollection(self::MSG);
foreach ($data as $key => $val) {
try {
if (isset($res[$key])) {
$coll->update(array(
self::MSG_MSGUID => strval($key),
self::MSG_UID => $uid
), array(
self::MSG_DATA => $this->_value(array_merge($res[$key], $val)),
self::MSG_MSGUID => strval($key),
self::MSG_UID => $uid
));
} else {
$doc = array(
self::MSG_DATA => $this->_value($val),
self::MSG_MSGUID => strval($key),
self::MSG_UID => $uid
);
$coll->insert($doc);
}
} catch (MongoException $e) {}
}
/* Update modified time. */
try {
$this->_db->selectCollection(self::BASE)->update(
array(self::BASE_UID => $uid),
array(self::BASE_MODIFIED => time())
);
} catch (MongoException $e) {}
/* Update uidvalidity. */
$this->setMetaData($mailbox, array('uidvalid' => $uidvalid));
}
/**
*/
public function getMetaData($mailbox, $uidvalid, $entries)
{
if (!($uid = $this->_getUid($mailbox))) {
return array();
}
$out = array();
$query = array(
self::MD_UID => $uid
);
if (!empty($entries)) {
$entries[] = 'uidvalid';
$query[self::MD_FIELD] = array(
'$in' => array_unique($entries)
);
}
try {
$cursor = $this->_db->selectCollection(self::MD)->find(
$query,
array(self::MD_DATA => true, self::MD_FIELD => true)
);
foreach ($cursor as $val) {
try {
$out[$val[self::MD_FIELD]] = $this->_value($val[self::MD_DATA]);
} catch (Exception $e) {}
}
if (is_null($uidvalid) ||
!isset($out['uidvalid']) ||
($out['uidvalid'] == $uidvalid)) {
return $out;
}
$this->deleteMailbox($mailbox);
} catch (MongoException $e) {}
return array();
}
/**
*/
public function setMetaData($mailbox, $data)
{
if (!($uid = $this->_getUid($mailbox))) {
$uid = $this->_createUid($mailbox);
}
$coll = $this->_db->selectCollection(self::MD);
foreach ($data as $key => $val) {
try {
$coll->update(
array(
self::MD_FIELD => $key,
self::MD_UID => $uid
),
array(
self::MD_DATA => $this->_value($val),
self::MD_FIELD => $key,
self::MD_UID => $uid
),
array('upsert' => true)
);
} catch (MongoException $e) {}
}
}
/**
*/
public function deleteMsgs($mailbox, $uids)
{
if (!empty($uids) && ($uid = $this->_getUid($mailbox))) {
try {
$this->_db->selectCollection(self::MSG)->remove(array(
self::MSG_MSGUID => array(
'$in' => array_map('strval', $uids)
),
self::MSG_UID => $uid
));
} catch (MongoException $e) {}
}
}
/**
*/
public function deleteMailbox($mailbox)
{
if (!($uid = $this->_getUid($mailbox))) {
return;
}
foreach (array(self::BASE, self::MD, self::MSG) as $val) {
try {
$this->_db->selectCollection($val)
->remove(array('uid' => $uid));
} catch (MongoException $e) {}
}
}
/**
*/
public function clear($lifetime)
{
if (is_null($lifetime)) {
foreach (array(self::BASE, self::MD, self::MSG) as $val) {
$this->_db->selectCollection($val)->drop();
}
return;
}
$query = array(
self::BASE_MODIFIED => array('$lt' => (time() - $lifetime))
);
$uids = array();
try {
$cursor = $this->_db->selectCollection(self::BASE)->find($query);
foreach ($cursor as $val) {
$uids[] = strval($val['_id']);
}
} catch (MongoException $e) {}
if (empty($uids)) {
return;
}
foreach (array(self::BASE, self::MD, self::MSG) as $val) {
try {
$this->_db->selectCollection($val)
->remove(array('uid' => array('$in' => $uids)));
} catch (MongoException $e) {}
}
}
/**
* Return the UID for a mailbox/user/server combo.
*
* @param string $mailbox Mailbox name.
*
* @return string UID from base table.
*/
protected function _getUid($mailbox)
{
$query = array(
self::BASE_HOSTSPEC => $this->_params['hostspec'],
self::BASE_MAILBOX => $mailbox,
self::BASE_PORT => $this->_params['port'],
self::BASE_USERNAME => $this->_params['username']
);
try {
if ($result = $this->_db->selectCollection(self::BASE)->findOne($query)) {
return strval($result['_id']);
}
} catch (MongoException $e) {}
return null;
}
/**
* Create and return the UID for a mailbox/user/server combo.
*
* @param string $mailbox Mailbox name.
*
* @return string UID from base table.
*/
protected function _createUid($mailbox)
{
$doc = array(
self::BASE_HOSTSPEC => $this->_params['hostspec'],
self::BASE_MAILBOX => $mailbox,
self::BASE_PORT => $this->_params['port'],
self::BASE_USERNAME => $this->_params['username']
);
$this->_db->selectCollection(self::BASE)->insert($doc);
return $this->_getUid($mailbox);
}
/**
* Convert data from/to storage format.
*
* @param mixed|MongoBinData $data The data object.
*
* @return mixed|MongoBinData The converted data.
*/
protected function _value($data)
{
static $compress;
if (!isset($compress)) {
$compress = new Horde_Compress_Fast();
}
return ($data instanceof MongoBinData)
? @unserialize($compress->decompress($data->bin))
: new MongoBinData(
$compress->compress(serialize($data)), MongoBinData::BYTE_ARRAY
);
}
/* Horde_Mongo_Collection_Index methods. */
/**
*/
public function checkMongoIndices()
{
foreach ($this->_indices as $key => $val) {
if (!$this->_params['mongo_db']->checkIndices($key, $val)) {
return false;
}
}
return true;
}
/**
*/
public function createMongoIndices()
{
foreach ($this->_indices as $key => $val) {
$this->_params['mongo_db']->createIndices($key, $val);
}
}
}
@@ -1,79 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* A null backend class for storing cached IMAP/POP data.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Cache_Backend_Null
extends Horde_Imap_Client_Cache_Backend
{
/**
*/
public function get($mailbox, $uids, $fields, $uidvalid)
{
return array();
}
/**
*/
public function getCachedUids($mailbox, $uidvalid)
{
return array();
}
/**
*/
public function set($mailbox, $data, $uidvalid)
{
}
/**
*/
public function getMetaData($mailbox, $uidvalid, $entries)
{
return array(
'uidvalid' => 0
);
}
/**
*/
public function setMetaData($mailbox, $data)
{
}
/**
*/
public function deleteMsgs($mailbox, $uids)
{
}
/**
*/
public function deleteMailbox($mailbox)
{
}
/**
*/
public function clear($lifetime)
{
}
}
@@ -1,188 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* ACL rights for a mailbox (see RFC 2086/4314).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Acl extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, IteratorAggregate, Serializable
{
/**
* ACL rights.
*
* @var array
*/
protected $_rights;
/**
* Constructor.
*
* @param string $rights The rights (see RFC 4314 [2.1]).
*/
public function __construct($rights = '')
{
$this->_rights = str_split($rights);
$this->_normalize();
}
/**
* String representation of the ACL.
*
* @return string String representation (RFC 4314 compliant).
*/
public function __toString()
{
return implode('', $this->_rights);
}
/**
* Computes the difference to another rights string.
* Virtual rights are ignored.
*
* @param string $rights The rights to compute against.
*
* @return array Two element array: added and removed.
*/
public function diff($rights)
{
$rlist = array_diff(str_split($rights), array_keys($this->_virtual));
return array(
'added' => implode('', array_diff($rlist, $this->_rights)),
'removed' => implode('', array_diff($this->_rights, $rlist))
);
}
/**
* Normalize virtual rights (see RFC 4314 [2.1.1]).
*/
protected function _normalize()
{
/* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
* and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
* to handle these rights when dealing with RFC 2086 servers since
* we are abstracting out use of ACL_CREATE/ACL_DELETE to their
* component RFC 4314 rights. */
foreach ($this->_virtual as $key => $val) {
foreach ($val as $right) {
if ($this[$right]) {
foreach (array_keys($this->_virtual) as $virtual) {
unset($this[$virtual]);
}
return;
}
}
}
foreach ($this->_virtual as $key => $val) {
if ($this[$key]) {
unset($this[$key]);
$this->_rights = array_unique(array_merge($this->_rights, $val));
}
}
}
/* ArrayAccess methods. */
/**
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return $this[$offset];
}
/**
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return in_array($offset, $this->_rights);
}
/**
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if ($value) {
if (!$this[$offset]) {
$this->_rights[] = $offset;
$this->_normalize();
}
} elseif ($this[$offset]) {
if (isset($this->_virtual[$offset])) {
foreach ($this->_virtual[$offset] as $val) {
unset($this[$val]);
}
}
unset($this[$offset]);
}
}
/**
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
$this->_rights = array_values(array_diff($this->_rights, array($offset)));
}
/* IteratorAggregate method. */
#[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->_rights);
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version changed.');
}
$this->__unserialize($data);
}
/**
* @return array
*/
public function __serialize()
{
return array(
'rights' => $this->_rights
);
}
public function __unserialize(array $data)
{
$this->_rights = $data['rights'];
}
}
@@ -1,72 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Provides common methods shared in all ACL classes (see RFC 2086/4314).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_AclCommon
{
/** Constants for getString(). */
const RFC_2086 = 1;
const RFC_4314 = 2;
/**
* List of virtual rights (RFC 4314 [2.1.1]).
*
* @var array
*/
protected $_virtual = array(
Horde_Imap_Client::ACL_CREATE => array(
Horde_Imap_Client::ACL_CREATEMBOX,
Horde_Imap_Client::ACL_DELETEMBOX
),
Horde_Imap_Client::ACL_DELETE => array(
Horde_Imap_Client::ACL_DELETEMSGS,
// Don't put this first - we do checks on the existence of the
// first element in this array to determine the RFC type, and this
// is duplicate of right contained in ACL_CREATE.
Horde_Imap_Client::ACL_DELETEMBOX,
Horde_Imap_Client::ACL_EXPUNGE
)
);
/**
* Returns the raw string to use in IMAP server calls.
*
* @param integer $type The RFC type to use (RFC_* constant).
*
* @return string The string representation of the ACL.
*/
public function getString($type = self::RFC_4314)
{
$acl = strval($this);
if ($type == self::RFC_2086) {
foreach ($this->_virtual as $key => $val) {
$acl = str_replace($val, '', $acl, $count);
if ($count) {
$acl .= $key;
}
}
}
return $acl;
}
}
@@ -1,25 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* ACL *negative* rights for a mailbox (see RFC 2086/4314 [2]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_AclNegative extends Horde_Imap_Client_Data_Acl
{
}
@@ -1,231 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Available ACL rights for a mailbox/identifier (see RFC 2086/4314).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_AclRights extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, Iterator, Serializable
{
/**
* ACL optional rights.
*
* @var array
*/
protected $_optional = array();
/**
* ACL required rights.
*
* @var array
*/
protected $_required = array();
/**
* Constructor.
*
* @param array $required The required rights (see RFC 4314 [2.1]).
* @param array $optional The optional rights (see RFC 4314 [2.1]).
*/
public function __construct(array $required = array(),
array $optional = array())
{
$this->_required = $required;
foreach ($optional as $val) {
foreach (str_split($val) as $right) {
$this->_optional[$right] = $val;
}
}
$this->_normalize();
}
/**
* String representation of the ACL.
*
* @return string String representation (RFC 4314 compliant).
*
*/
public function __toString()
{
return implode('', array_keys(array_flip(array_merge(array_values($this->_required), array_keys($this->_optional)))));
}
/**
* Normalize virtual rights (see RFC 4314 [2.1.1]).
*/
protected function _normalize()
{
/* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
* and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
* to handle these rights when dealing with RFC 2086 servers since
* we are abstracting out use of ACL_CREATE/ACL_DELETE to their
* component RFC 4314 rights. */
foreach ($this->_virtual as $key => $val) {
if (isset($this->_optional[$key])) {
unset($this->_optional[$key]);
foreach ($val as $val2) {
$this->_optional[$val2] = implode('', $val);
}
} elseif (($pos = array_search($key, $this->_required)) !== false) {
unset($this->_required[$pos]);
$this->_required = array_unique(array_merge($this->_required, $val));
}
}
}
/* ArrayAccess methods. */
/**
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return (bool)$this[$offset];
}
/**
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
if (isset($this->_optional[$offset])) {
return $this->_optional[$offset];
}
$pos = array_search($offset, $this->_required);
return ($pos === false)
? null
: $this->_required[$pos];
}
/**
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->_optional[$offset] = $value;
$this->_normalize();
}
/**
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->_optional[$offset]);
$this->_required = array_values(array_diff($this->_required, array($offset)));
if (isset($this->_virtual[$offset])) {
foreach ($this->_virtual[$offset] as $val) {
unset($this[$val]);
}
}
}
/* Iterator methods. */
/**
*/
#[ReturnTypeWillChange]
public function current()
{
$val = current($this->_required);
return is_null($val)
? current($this->_optional)
: $val;
}
/**
*/
#[ReturnTypeWillChange]
public function key()
{
$key = key($this->_required);
return is_null($key)
? key($this->_optional)
: $key;
}
/**
*/
#[ReturnTypeWillChange]
public function next()
{
if (key($this->_required) === null) {
next($this->_optional);
} else {
next($this->_required);
}
}
/**
*/
#[ReturnTypeWillChange]
public function rewind()
{
reset($this->_required);
reset($this->_optional);
}
/**
*/
#[ReturnTypeWillChange]
public function valid()
{
return ((key($this->_required) !== null) ||
(key($this->_optional) !== null));
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version changed.');
}
$this->__unserialize($data);
}
/**
* @return array
*/
public function __serialize()
{
return [$this->_required, $this->_optional];
}
public function __unserialize(array $data)
{
list($this->_required, $this->_optional) = $data;
}
}
@@ -1,231 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* getBaseSubject() code adapted from imap-base-subject.c (Dovecot 1.2)
* Original code released under the LGPL-2.1
* Copyright (c) 2002-2008 Timo Sirainen <[email protected]>
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2002-2008 Timo Sirainen
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Determines the "base subject" of a string (RFC 5256 [2.1]).
*
* @author Timo Sirainen <[email protected]>
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2002-2008 Timo Sirainen
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_BaseSubject
{
/**
* The base subject.
*
* @var string
*/
protected $_subject;
/**
* Constructor.
*
* @param string $str The subject string.
* @param array $opts Additional options:
* - keepblob: (boolean) Don't remove any "blob" information (i.e. text
* leading text between square brackets) from string.
*
* @return string The cleaned up subject string.
*/
public function __construct($str, array $opts = array())
{
// Rule 1a: MIME decode.
$str = Horde_Mime::decode($str);
// Rule 1b: Remove superfluous whitespace.
$str = preg_replace("/[\t\r\n ]+/", ' ', $str);
do {
/* (2) Remove all trailing text of the subject that matches the
* the subj-trailer ABNF, repeat until no more matches are
* possible. */
$str = preg_replace("/(?:\s*\(fwd\)\s*)+$/i", '', $str);
do {
/* (3) Remove all prefix text of the subject that matches the
* subj-leader ABNF. */
$found = $this->_removeSubjLeader($str, !empty($opts['keepblob']));
/* (4) If there is prefix text of the subject that matches
* the subj-blob ABNF, and removing that prefix leaves a
* non-empty subj-base, then remove the prefix text. */
$found = (empty($opts['keepblob']) && $this->_removeBlobWhenNonempty($str)) || $found;
/* (5) Repeat (3) and (4) until no matches remain. */
} while ($found);
/* (6) If the resulting text begins with the subj-fwd-hdr ABNF and
* ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and
* subj-fwd-trl and repeat from step (2). */
} while ($this->_removeSubjFwdHdr($str));
$this->_subject = strval($str);
}
/**
* Return the "base subject" defined in RFC 5256 [2.1].
*
* @return string The base subject.
*/
public function __toString()
{
return $this->_subject;
}
/**
* Remove all prefix text of the subject that matches the subj-leader
* ABNF.
*
* @param string &$str The subject string.
* @param boolean $keepblob Remove blob information?
*
* @return boolean True if string was altered.
*/
protected function _removeSubjLeader(&$str, $keepblob = false)
{
$ret = false;
if (!strlen($str)) {
return $ret;
}
if ($len = strspn($str, " \t")) {
$str = substr($str, $len);
$ret = true;
}
$i = 0;
if (!$keepblob) {
while (isset($str[$i]) && ($str[$i] === '[')) {
if (($i = $this->_removeBlob($str, $i)) === false) {
return $ret;
}
}
}
if (stripos($str, 're', $i) === 0) {
$i += 2;
} elseif (stripos($str, 'fw', $i) === 0) {
$i += (stripos($str, 'fwd', $i) === 0) ? 3 : 2;
} else {
return $ret;
}
$i += strspn($str, " \t", $i);
if (!$keepblob) {
while (isset($str[$i]) && ($str[$i] === '[')) {
if (($i = $this->_removeBlob($str, $i)) === false) {
return $ret;
}
}
}
if (!isset($str[$i]) || ($str[$i] !== ':')) {
return $ret;
}
$str = substr($str, ++$i);
return true;
}
/**
* Remove "[...]" text.
*
* @param string $str The subject string.
* @param integer $i Current position.
*
* @return boolean|integer False if blob was not found, otherwise the
* string position of the first non-blob char.
*/
protected function _removeBlob($str, $i)
{
if ($str[$i] !== '[') {
return false;
}
++$i;
for ($cnt = strlen($str); $i < $cnt; ++$i) {
if ($str[$i] === ']') {
break;
}
if ($str[$i] === '[') {
return false;
}
}
if ($i === ($cnt - 1)) {
return false;
}
++$i;
if ($str[$i] === ' ') {
++$i;
}
return $i;
}
/**
* Remove "[...]" text if it doesn't result in the subject becoming
* empty.
*
* @param string &$str The subject string.
*
* @return boolean True if string was altered.
*/
protected function _removeBlobWhenNonempty(&$str)
{
if ($str &&
($str[0] === '[') &&
(($i = $this->_removeBlob($str, 0)) !== false) &&
($i !== strlen($str))) {
$str = substr($str, $i);
return true;
}
return false;
}
/**
* Remove a "[fwd: ... ]" string.
*
* @param string &$str The subject string.
*
* @return boolean True if string was altered.
*/
protected function _removeSubjFwdHdr(&$str)
{
if ((stripos($str, '[fwd:') !== 0) || (substr($str, -1) !== ']')) {
return false;
}
$str = substr($str, 5, -1);
return true;
}
}
@@ -1,234 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Query the capabilities of a server.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.24.0
*/
class Horde_Imap_Client_Data_Capability
implements Serializable, SplSubject
{
/**
* Capability data.
*
* @var array
*/
protected $_data = array();
/**
* Observers.
*
* @var array
*/
protected $_observers = array();
/**
* Add a capability (and optional parameters).
*
* @param string $capability The capability to add.
* @param mixed $params A parameter (or array of parameters) to add.
*/
public function add($capability, $params = null)
{
$capability = Horde_String::upper($capability);
if (is_null($params)) {
if (isset($this->_data[$capability])) {
return;
}
$params = true;
} else {
if (!is_array($params)) {
$params = array($params);
}
$params = array_map('Horde_String::upper', $params);
if (isset($this->_data[$capability]) &&
is_array($this->_data[$capability])) {
$params = array_merge($this->_data[$capability], $params);
}
}
$this->_data[$capability] = $params;
$this->notify();
}
/**
* Remove a capability.
*
* @param string $capability The capability to remove.
* @param string $params A parameter (or array of parameters) to
* remove from the capability.
*/
public function remove($capability, $params = null)
{
$capability = Horde_String::upper($capability);
if (is_null($params)) {
unset($this->_data[$capability]);
} elseif (isset($this->_data[$capability])) {
if (!is_array($params)) {
$params = array($params);
}
$params = array_map('Horde_String::upper', $params);
$this->_data[$capability] = is_array($this->_data[$capability])
? array_diff($this->_data[$capability], $params)
: array();
if (empty($this->_data[$capability])) {
unset($this->_data[$capability]);
}
}
$this->notify();
}
/**
* Returns whether the server supports the given capability.
*
* @param string $capability The capability string to query.
* @param string $parameter If set, require the parameter to exist.
*
* @return boolean True if the capability (and parameter) exist.
*/
public function query($capability, $parameter = null)
{
$capability = Horde_String::upper($capability);
if (!isset($this->_data[$capability])) {
return false;
}
return is_null($parameter) ?:
(is_array($this->_data[$capability]) &&
in_array(Horde_String::upper($parameter), $this->_data[$capability]));
}
/**
* Return the list of parameters for an extension.
*
* @param string $capability The capability string to query.
*
* @return array An array of parameters if the extension exists and
* supports parameters. Otherwise, an empty array.
*/
public function getParams($capability)
{
return ($this->query($capability) && is_array($out = $this->_data[Horde_String::upper($capability)]))
? $out
: array();
}
/**
* Is the extension enabled?
*
* @param string $capability The extension (+ parameter) to query. If
* null, returns all enabled extensions.
*
* @return mixed If $capability is null, return all enabled extensions.
* Otherwise, true if the extension (+ parameter) is
* enabled.
*/
public function isEnabled($capability = null)
{
return is_null($capability)
? array()
: false;
}
/**
* Returns the raw data.
*
* @deprecated
*
* @return array Capability data.
*/
public function toArray()
{
return $this->_data;
}
/* SplSubject methods. */
/**
*/
#[ReturnTypeWillChange]
public function attach(SplObserver $observer)
{
$this->detach($observer);
$this->_observers[] = $observer;
}
/**
*/
#[ReturnTypeWillChange]
public function detach(SplObserver $observer)
{
if (($key = array_search($observer, $this->_observers, true)) !== false) {
unset($this->_observers[$key]);
}
}
/**
* Notification is triggered internally whenever the object's internal
* data storage is altered.
*/
#[ReturnTypeWillChange]
public function notify()
{
foreach ($this->_observers as $val) {
$val->update($this);
}
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version change.');
}
$this->__unserialize();
}
/**
* @return array
*/
public function __serialize()
{
return $this->_data;
}
public function __unserialize(array $data)
{
$this->_data = $data;
}
}
@@ -1,117 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Query the capabilities of an IMAP server.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.24.0
*
* @property-read integer $cmdlength Allowable command length (in octets).
*/
class Horde_Imap_Client_Data_Capability_Imap
extends Horde_Imap_Client_Data_Capability
{
/**
* The list of enabled extensions.
*
* @var array
*/
protected $_enabled = array();
/**
*/
public function __get($name)
{
switch ($name) {
case 'cmdlength':
/* RFC 2683 [3.2.1.5] originally recommended that lines should
* be limited to "approximately 1000 octets". However, servers
* should allow a command line of at least "8000 octets".
* RFC 7162 [4] updates the recommendation to 8192 octets.
* As a compromise, assume all modern IMAP servers handle
* ~2000 octets and, if CONDSTORE/QRESYNC is supported, assume
* they can handle ~8000 octets. (Don't need dependency support
* checks here - the simple presence of CONDSTORE/QRESYNC is
* enough to trigger.) */
return (isset($this->_data['CONDSTORE']) || isset($this->_data['QRESYNC']))
? 8000
: 2000;
}
}
/**
*/
public function query($capability, $parameter = null)
{
if (parent::query($capability, $parameter)) {
return true;
}
switch (Horde_String::upper($capability)) {
case 'CONDSTORE':
case 'ENABLE':
/* RFC 7162 [3.2.3] - QRESYNC implies CONDSTORE and ENABLE. */
return (is_null($parameter) && $this->query('QRESYNC'));
case 'UTF8':
/* RFC 6855 [3] - UTF8=ONLY implies UTF8=ACCEPT. */
return ((Horde_String::upper($parameter) === 'ACCEPT') &&
$this->query('UTF8', 'ONLY'));
}
return false;
}
/**
*/
public function isEnabled($capability = null)
{
return is_null($capability)
? $this->_enabled
: in_array(Horde_String::upper($capability), $this->_enabled);
}
/**
* Set a capability as enabled/disabled.
*
* @param array $capability A capability (+ parameter).
* @param boolean $enable If true, enables the capability.
*/
public function enable($capability, $enable = true)
{
$capability = Horde_String::upper($capability);
$enabled = $this->isEnabled($capability);
if ($enable && !$enabled) {
switch ($capability) {
case 'QRESYNC':
/* RFC 7162 [3.2.3] - Enabling QRESYNC also implies enabling
* of CONDSTORE. */
$this->enable('CONDSTORE');
break;
}
$this->_enabled[] = $capability;
$this->notify();
} elseif (!$enable && $enabled) {
$this->_enabled = array_diff($this->_enabled, array($capability));
$this->notify();
}
}
}
@@ -1,238 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Envelope data as returned by the IMAP FETCH command (RFC 3501 [7.4.2]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*
* @todo $date should return null if it doesn't exist.
*
* @property Horde_Mail_Rfc822_List $bcc Bcc address(es).
* @property Horde_Mail_Rfc822_List $cc Cc address(es).
* @property Horde_Imap_Client_DateTime $date Message date.
* @property Horde_Mail_Rfc822_List $from From address(es).
* @property string $in_reply_to Message-ID of the message replied to.
* @property string $message_id Message-ID of the message.
* @property Horde_Mail_Rfc822_List $reply_to Reply-to address(es).
* @property Horde_Mail_Rfc822_List $sender Sender address.
* @property string $subject Subject.
* @property Horde_Mail_Rfc822_List $to To address(es).
*/
class Horde_Imap_Client_Data_Envelope implements Serializable
{
/* Serializable version. */
const VERSION = 3;
/**
* Data object.
*
* @var Horde_Mime_Headers
*/
protected $_data;
/**
* Constructor.
*
* @var array $data An array of property names (keys) and values to set
* in this object.
*/
public function __construct(array $data = array())
{
$this->_data = new Horde_Mime_Headers();
foreach ($data as $key => $val) {
$this->$key = $val;
}
}
/**
*/
public function __get($name)
{
$name = $this->_normalizeProperty($name);
switch ($name) {
case 'bcc':
case 'cc':
case 'from':
case 'reply-to':
case 'sender':
case 'to':
if ($h = $this->_data[$name]) {
return $h->getAddressList(true);
}
if (in_array($name, array('sender', 'reply-to'))) {
return $this->from;
}
break;
case 'date':
if ($val = $this->_data['date']) {
return new Horde_Imap_Client_DateTime($val->value);
}
break;
case 'in-reply-to':
case 'message-id':
case 'subject':
if ($val = $this->_data[$name]) {
return $val->value;
}
break;
}
// Default values.
switch ($name) {
case 'bcc':
case 'cc':
case 'from':
case 'to':
return new Horde_Mail_Rfc822_List();
case 'date':
return new Horde_Imap_Client_DateTime();
case 'in-reply-to':
case 'message-id':
case 'subject':
return '';
}
return null;
}
/**
*/
public function __set($name, $value)
{
if (!strlen($value)) {
return;
}
$name = $this->_normalizeProperty($name);
switch ($name) {
case 'bcc':
case 'cc':
case 'date':
case 'from':
case 'in-reply-to':
case 'message-id':
case 'reply-to':
case 'sender':
case 'subject':
case 'to':
switch ($name) {
case 'from':
if ($this->reply_to->match($value)) {
unset($this->_data['reply-to']);
}
if ($this->sender->match($value)) {
unset($this->_data['sender']);
}
break;
case 'reply-to':
case 'sender':
if ($this->from->match($value)) {
unset($this->_data[$name]);
return;
}
break;
}
$this->_data->addHeader($name, $value);
break;
}
}
/**
*/
public function __isset($name)
{
$name = $this->_normalizeProperty($name);
switch ($name) {
case 'reply-to':
case 'sender':
if (isset($this->_data[$name])) {
return true;
}
$name = 'from';
break;
}
return isset($this->_data[$name]);
}
/**
*/
protected function _normalizeProperty($name)
{
switch ($name) {
case 'in_reply_to':
return 'in-reply-to';
case 'message_id':
return 'message-id';
case 'reply_to':
return 'reply-to';
}
return $name;
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$this->__unserialize(@unserialize($data));
}
/**
* @return array
*/
public function __serialize()
{
return array(
'd' => $this->_data,
'v' => self::VERSION,
);
}
public function __unserialize(array $data)
{
if (empty($data['v']) || $data['v'] != self::VERSION) {
throw new Exception('Cache version change');
}
$this->_data = $data['d'];
}
}
@@ -1,666 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object containing data returned by the Horde_Imap_Client_Base#fetch()
* command.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Fetch
{
/** Header formatting constants. */
const HEADER_PARSE = 1;
const HEADER_STREAM = 2;
/**
* Internal data array.
*
* @var array
*/
protected $_data = array();
/**
*/
public function __clone()
{
$this->_data = unserialize(serialize($this->_data));
}
/**
* Set the full message property.
*
* @param mixed $msg The full message text, as either a string or stream
* resource.
*/
public function setFullMsg($msg)
{
$this->_data[Horde_Imap_Client::FETCH_FULLMSG] = $this->_setMixed($msg);
}
/**
* Returns the full message.
*
* @param boolean $stream Return as a stream?
*
* @return mixed The full text of the entire message.
*/
public function getFullMsg($stream = false)
{
return $this->_msgText(
$stream,
isset($this->_data[Horde_Imap_Client::FETCH_FULLMSG])
? $this->_data[Horde_Imap_Client::FETCH_FULLMSG]
: null
);
}
/**
* Set the message structure.
*
* @param Horde_Mime_Part $structure The base MIME part of the message.
*/
public function setStructure(Horde_Mime_Part $structure)
{
$this->_data[Horde_Imap_Client::FETCH_STRUCTURE] = $structure;
}
/**
* Get the message structure.
*
* @return Horde_Mime_Part $structure The base MIME part of the message.
*/
public function getStructure()
{
return isset($this->_data[Horde_Imap_Client::FETCH_STRUCTURE])
? clone $this->_data[Horde_Imap_Client::FETCH_STRUCTURE]
: new Horde_Mime_Part();
}
/**
* Set a header entry.
*
* @param string $label The search label.
* @param mixed $data Either a Horde_Mime_Headers object or the raw
* header text.
*/
public function setHeaders($label, $data)
{
if ($data instanceof Horde_Stream) {
$data = $data->stream;
}
$this->_data[Horde_Imap_Client::FETCH_HEADERS][$label] = $data;
}
/**
* Get a header entry.
*
* @param string $label The search label.
* @param integer $format The return format. If self::HEADER_PARSE,
* returns a Horde_Mime_Headers object. If
* self::HEADER_STREAM, returns a stream.
* Otherwise, returns header text.
*
* @return mixed See $format.
*/
public function getHeaders($label, $format = 0)
{
return $this->_getHeaders(
$label,
$format,
Horde_Imap_Client::FETCH_HEADERS
);
}
/**
* Set a header text entry.
*
* @param string $id The MIME ID.
* @param mixed $text The header text, as either a string or stream
* resource.
*/
public function setHeaderText($id, $text)
{
$this->_data[Horde_Imap_Client::FETCH_HEADERTEXT][$id] = $this->_setMixed($text);
}
/**
* Get a header text entry.
*
* @param string $id The MIME ID.
* @param integer $format The return format. If self::HEADER_PARSE,
* returns a Horde_Mime_Headers object. If
* self::HEADER_STREAM, returns a stream.
* Otherwise, returns header text.
*
* @return mixed See $format.
*/
public function getHeaderText($id = 0, $format = 0)
{
return $this->_getHeaders(
$id,
$format,
Horde_Imap_Client::FETCH_HEADERTEXT
);
}
/**
* Set a MIME header entry.
*
* @param string $id The MIME ID.
* @param mixed $text The header text, as either a string or stream
* resource.
*/
public function setMimeHeader($id, $text)
{
$this->_data[Horde_Imap_Client::FETCH_MIMEHEADER][$id] = $this->_setMixed($text);
}
/**
* Get a MIME header entry.
*
* @param string $id The MIME ID.
* @param integer $format The return format. If self::HEADER_PARSE,
* returns a Horde_Mime_Headers object. If
* self::HEADER_STREAM, returns a stream.
* Otherwise, returns header text.
*
* @return mixed See $format.
*/
public function getMimeHeader($id, $format = 0)
{
return $this->_getHeaders(
$id,
$format,
Horde_Imap_Client::FETCH_MIMEHEADER
);
}
/**
* Set a body part entry.
*
* @param string $id The MIME ID.
* @param mixed $text The body part text, as either a string or stream
* resource.
* @param string $decode Either '8bit', 'binary', or null.
*/
public function setBodyPart($id, $text, $decode = null)
{
$this->_data[Horde_Imap_Client::FETCH_BODYPART][$id] = array(
'd' => $decode,
't' => $this->_setMixed($text)
);
}
/**
* Get a body part entry.
*
* @param string $id The MIME ID.
* @param boolean $stream Return as a stream?
*
* @return mixed The full text of the body part.
*/
public function getBodyPart($id, $stream = false)
{
return $this->_msgText(
$stream,
isset($this->_data[Horde_Imap_Client::FETCH_BODYPART][$id])
? $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]['t']
: null
);
}
/**
* Determines if/how a body part was MIME decoded on the server.
*
* @param string $id The MIME ID.
*
* @return string Either '8bit', 'binary', or null.
*/
public function getBodyPartDecode($id)
{
return isset($this->_data[Horde_Imap_Client::FETCH_BODYPART][$id])
? $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]['d']
: null;
}
/**
* Set the body part size for a body part.
*
* @param string $id The MIME ID.
* @param integer $size The size (in bytes).
*/
public function setBodyPartSize($id, $size)
{
$this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id] = intval($size);
}
/**
* Returns the body part size, if returned by the server.
*
* @param string $id The MIME ID.
*
* @return integer The body part size, in bytes.
*/
public function getBodyPartSize($id)
{
return isset($this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id])
? $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id]
: null;
}
/**
* Set a body text entry.
*
* @param string $id The MIME ID.
* @param mixed $text The body part text, as either a string or stream
* resource.
*/
public function setBodyText($id, $text)
{
$this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] = $this->_setMixed($text);
}
/**
* Get a body text entry.
*
* @param string $id The MIME ID.
* @param boolean $stream Return as a stream?
*
* @return mixed The full text of the body text.
*/
public function getBodyText($id = 0, $stream = false)
{
return $this->_msgText(
$stream,
isset($this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id])
? $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id]
: null
);
}
/**
* Set envelope data.
*
* @param array $data The envelope data to pass to the Envelope object
* constructor, or an Envelope object.
*/
public function setEnvelope($data)
{
$this->_data[Horde_Imap_Client::FETCH_ENVELOPE] = is_array($data)
? new Horde_Imap_Client_Data_Envelope($data)
: $data;
}
/**
* Get envelope data.
*
* @return Horde_Imap_Client_Data_Envelope An envelope object.
*/
public function getEnvelope()
{
return isset($this->_data[Horde_Imap_Client::FETCH_ENVELOPE])
? clone $this->_data[Horde_Imap_Client::FETCH_ENVELOPE]
: new Horde_Imap_Client_Data_Envelope();
}
/**
* Set IMAP flags.
*
* @param array $flags An array of IMAP flags.
*/
public function setFlags(array $flags)
{
$this->_data[Horde_Imap_Client::FETCH_FLAGS] = array_map(
'Horde_String::lower',
array_map('trim', $flags)
);
}
/**
* Get IMAP flags.
*
* @return array An array of IMAP flags (all flags in lowercase).
*/
public function getFlags()
{
return isset($this->_data[Horde_Imap_Client::FETCH_FLAGS])
? $this->_data[Horde_Imap_Client::FETCH_FLAGS]
: array();
}
/**
* Set IMAP internal date.
*
* @param mixed $date Either a Horde_Imap_Client_DateTime object or a
* date string.
*/
public function setImapDate($date)
{
$this->_data[Horde_Imap_Client::FETCH_IMAPDATE] = is_object($date)
? $date
: new Horde_Imap_Client_DateTime($date);
}
/**
* Get internal IMAP date.
*
* @return Horde_Imap_Client_DateTime A date object.
*/
public function getImapDate()
{
return isset($this->_data[Horde_Imap_Client::FETCH_IMAPDATE])
? clone $this->_data[Horde_Imap_Client::FETCH_IMAPDATE]
: new Horde_Imap_Client_DateTime();
}
/**
* Set message size.
*
* @param integer $size The size of the message, in bytes.
*/
public function setSize($size)
{
$this->_data[Horde_Imap_Client::FETCH_SIZE] = intval($size);
}
/**
* Get message size.
*
* @return integer The size of the message, in bytes.
*/
public function getSize()
{
return isset($this->_data[Horde_Imap_Client::FETCH_SIZE])
? $this->_data[Horde_Imap_Client::FETCH_SIZE]
: 0;
}
/**
* Set UID.
*
* @param integer $uid The message UID.
*/
public function setUid($uid)
{
$this->_data[Horde_Imap_Client::FETCH_UID] = intval($uid);
}
/**
* Get UID.
*
* @return integer The message UID.
*/
public function getUid()
{
return isset($this->_data[Horde_Imap_Client::FETCH_UID])
? $this->_data[Horde_Imap_Client::FETCH_UID]
: null;
}
/**
* Set message sequence number.
*
* @param integer $seq The message sequence number.
*/
public function setSeq($seq)
{
$this->_data[Horde_Imap_Client::FETCH_SEQ] = intval($seq);
}
/**
* Get message sequence number.
*
* @return integer The message sequence number.
*/
public function getSeq()
{
return isset($this->_data[Horde_Imap_Client::FETCH_SEQ])
? $this->_data[Horde_Imap_Client::FETCH_SEQ]
: null;
}
/**
* Set the modified sequence value for the message.
*
* @param integer $modseq The modseq value.
*/
public function setModSeq($modseq)
{
$this->_data[Horde_Imap_Client::FETCH_MODSEQ] = intval($modseq);
}
/**
* Get the modified sequence value for the message.
*
* @return integer The modseq value.
*/
public function getModSeq()
{
return isset($this->_data[Horde_Imap_Client::FETCH_MODSEQ])
? $this->_data[Horde_Imap_Client::FETCH_MODSEQ]
: null;
}
/**
* Set the internationalized downgraded status for the message.
*
* @since 2.11.0
*
* @param boolean $downgraded True if at least one message component has
* been downgraded.
*/
public function setDowngraded($downgraded)
{
if ($downgraded) {
$this->_data[Horde_Imap_Client::FETCH_DOWNGRADED] = true;
} else {
unset($this->_data[Horde_Imap_Client::FETCH_DOWNGRADED]);
}
}
/**
* Does the message contain internationalized downgraded data (i.e. it
* is a "surrogate" message)?
*
* @since 2.11.0
*
* @return boolean True if at least one message components has been
* downgraded.
*/
public function isDowngraded()
{
return !empty($this->_data[Horde_Imap_Client::FETCH_DOWNGRADED]);
}
/**
* Return the internal representation of the data.
*
* @return array The data array.
*/
public function getRawData()
{
return $this->_data;
}
/**
* Merge a fetch object into this one.
*
* @param Horde_Imap_Client_Data_Fetch $data A fetch object.
*/
public function merge(Horde_Imap_Client_Data_Fetch $data)
{
$this->_data = array_replace_recursive(
$this->_data,
$data->getRawData()
);
}
/**
* Does this object containing cacheable data of the given type?
*
* @param integer $type The type to query.
*
* @return boolean True if the type is cacheable.
*/
public function exists($type)
{
return isset($this->_data[$type]);
}
/**
* Does this object contain only default values for all fields?
*
* @return boolean True if object contains default data.
*/
public function isDefault()
{
return empty($this->_data);
}
/**
* Return text representation of a field.
*
* @param boolean $stream Return as a stream?
* @param mixed $data The field data (string or resource) or null if
* field does not exist.
*
* @return mixed Requested text representation.
*/
protected function _msgText($stream, $data)
{
if ($data instanceof Horde_Stream) {
if ($stream) {
$data->rewind();
return $data->stream;
}
return strval($data);
}
if (is_resource($data)) {
rewind($data);
return $stream
? $data
: stream_get_contents($data);
}
if (!$stream) {
return strval($data);
}
$tmp = fopen('php://temp', 'w+');
if (!is_null($data)) {
fwrite($tmp, $data);
rewind($tmp);
}
return $tmp;
}
/**
* Return representation of a header field.
*
* @param string $id The header id.
* @param integer $format The return format. If self::HEADER_PARSE,
* returns a Horde_Mime_Headers object. If
* self::HEADER_STREAM, returns a stream.
* Otherwise, returns header text.
* @param integer $key The array key where the data is stored in the
* internal array.
*
* @return mixed The data in the format specified by $format.
*/
protected function _getHeaders($id, $format, $key)
{
switch ($format) {
case self::HEADER_STREAM:
if (!isset($this->_data[$key][$id])) {
$data = null;
} elseif (is_object($this->_data[$key][$id])) {
switch ($key) {
case Horde_Imap_Client::FETCH_HEADERS:
$data = $this->_getHeaders($id, 0, $key);
break;
case Horde_Imap_Client::FETCH_HEADERTEXT:
case Horde_Imap_Client::FETCH_MIMEHEADER:
$data = $this->_data[$key][$id];
break;
}
} else {
$data = $this->_data[$key][$id];
}
return $this->_msgText(true, $data);
case self::HEADER_PARSE:
if (!isset($this->_data[$key][$id])) {
return new Horde_Mime_Headers();
} elseif (is_object($this->_data[$key][$id])) {
switch ($key) {
case Horde_Imap_Client::FETCH_HEADERS:
return clone $this->_data[$key][$id];
case Horde_Imap_Client::FETCH_HEADERTEXT:
case Horde_Imap_Client::FETCH_MIMEHEADER:
return Horde_Mime_Headers::parseHeaders($this->_data[$key][$id]);
}
} else {
$hdrs = $this->_getHeaders($id, self::HEADER_STREAM, $key);
$parsed = Horde_Mime_Headers::parseHeaders($hdrs);
fclose($hdrs);
return $parsed;
}
}
if (!isset($this->_data[$key][$id])) {
return '';
}
if (is_object($this->_data[$key][$id])) {
switch ($key) {
case Horde_Imap_Client::FETCH_HEADERS:
return $this->_data[$key][$id]->toString(
array('nowrap' => true)
);
case Horde_Imap_Client::FETCH_HEADERTEXT:
case Horde_Imap_Client::FETCH_MIMEHEADER:
return strval($this->_data[$key][$id]);
}
}
return $this->_msgText(false, $this->_data[$key][$id]);
}
/**
* Converts mixed input (string or resource) to the correct internal
* representation.
*
* @param mixed $data Mixed data (string, resource, Horde_Stream object).
*
* @return mixed The internal representation of that data.
*/
protected function _setMixed($data)
{
return is_resource($data)
? new Horde_Stream_Existing(array('stream' => $data))
: $data;
}
}
@@ -1,36 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object containg POP3 fetch data.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Fetch_Pop3 extends Horde_Imap_Client_Data_Fetch
{
/**
* Set UID.
*
* @param string $uid The message UID. Unlike IMAP, this UID does not
* have to be an integer.
*/
public function setUid($uid)
{
$this->_data[Horde_Imap_Client::FETCH_UID] = strval($uid);
}
}
@@ -1,83 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP data format (RFC 3501 [4]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format
{
/**
* Data.
*
* @var mixed
*/
protected $_data;
/**
* Constructor.
*
* @param mixed $data Data.
*/
public function __construct($data)
{
$this->_data = is_resource($data)
? stream_get_contents($data, -1, 0)
: $data;
}
/**
* Returns the string value of the raw data.
*
* @return string String value.
*/
public function __toString()
{
return strval($this->_data);
}
/**
* Returns the raw data.
*
* @return mixed Raw data.
*/
public function getData()
{
return $this->_data;
}
/**
* Returns the data formatted for output to the IMAP server.
*
* @return string IMAP escaped string.
*/
public function escape()
{
return strval($this);
}
/**
* Verify the data.
*
* @throws Horde_Imap_Client_Data_Format_Exception
*/
public function verify()
{
}
}
@@ -1,32 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP astring (atom or string) (RFC 3501 [4.3]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Astring extends Horde_Imap_Client_Data_Format_String
{
/**
*/
public function quoted()
{
return $this->_filter->quoted || !$this->_data->length();
}
}
@@ -1,28 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Extend astring element to allow for non-ASCII characters.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.25.0
*/
class Horde_Imap_Client_Data_Format_Astring_Nonascii
extends Horde_Imap_Client_Data_Format_Astring
implements Horde_Imap_Client_Data_Format_String_Support_Nonascii
{
}
@@ -1,57 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP atom (RFC 3501 [4.1]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Atom extends Horde_Imap_Client_Data_Format
{
/**
*/
public function escape()
{
return strlen($this->_data)
? parent::escape()
: '""';
}
/**
*/
public function verify()
{
if (strlen($this->_data) !== strlen($this->stripNonAtomCharacters())) {
throw new Horde_Imap_Client_Data_Format_Exception('Illegal character in IMAP atom.');
}
}
/**
* Strip out any characters that are not allowed in an IMAP atom.
*
* @return string The atom data disallowed characters removed.
*/
public function stripNonAtomCharacters()
{
return str_replace(
array('(', ')', '{', ' ', '%', '*', '"', '\\', ']'),
'',
preg_replace('/[^\x20-\x7e]/', '', $this->_data)
);
}
}
@@ -1,49 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP date string (RFC 3501 [9]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Date extends Horde_Imap_Client_Data_Format
{
/**
* Constructor.
*
* @param mixed $data Either a DateTime object, or a date format that
* can be converted to a DateTime object.
*
* @throws Exception
*/
public function __construct($data)
{
if (!($data instanceof DateTime)) {
$data = new Horde_Imap_Client_DateTime($data);
}
parent::__construct($data);
}
/**
*/
public function __toString()
{
return $this->_data->format('j-M-Y');
}
}
@@ -1,39 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP date-time string (RFC 3501 [9]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_DateTime extends Horde_Imap_Client_Data_Format_Date
{
/**
*/
public function __toString()
{
return $this->_data->format('j-M-Y H:i:s O');
}
/**
*/
public function escape()
{
return '"' . strval($this) . '"';
}
}
@@ -1,25 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception object for IMAP data format errors.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Exception extends Horde_Exception_Wrapped
{
}
@@ -1,66 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Stream filter to output a quoted IMAP string.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Filter_Quote extends php_user_filter
{
/**
* Has the initial quote been prepended?
*
* @var boolean
*/
protected $_prepend;
/**
*/
#[ReturnTypeWillChange]
public function onCreate()
{
$this->_prepend = false;
}
/**
* @see stream_filter_register()
*/
#[ReturnTypeWillChange]
public function filter($in, $out, &$consumed, $closing)
{
if (!$this->_prepend) {
stream_bucket_append($out, stream_bucket_new($this->stream, '"'));
$this->_prepend = true;
}
while ($bucket = stream_bucket_make_writeable($in)) {
$consumed += $bucket->datalen;
$bucket->data = addcslashes($bucket->data, '"\\');
stream_bucket_append($out, $bucket);
}
/* feof() call needed due to:
* http://news.php.net/php.internals/80363 */
if ($closing || feof($this->stream)) {
stream_bucket_append($out, stream_bucket_new($this->stream, '"'));
}
return PSFS_PASS_ON;
}
}
@@ -1,122 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Stream filter to analyze an IMAP string to determine how to send to the
* server.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Filter_String extends php_user_filter
{
/**
* Skip status.
*
* @var boolean
*/
protected $_skip = false;
/**
* @see stream_filter_register()
*/
#[ReturnTypeWillChange]
public function onCreate()
{
$this->params->binary = false;
$this->params->literal = false;
$this->params->nonascii = false;
// no_quote_list is used below as a config option
$this->params->quoted = false;
return true;
}
/**
* @see stream_filter_register()
*/
#[ReturnTypeWillChange]
public function filter($in, $out, &$consumed, $closing)
{
$p = $this->params;
while ($bucket = stream_bucket_make_writeable($in)) {
if (!$this->_skip) {
$len = $bucket->datalen;
$str = $bucket->data;
for ($i = 0; $i < $len; ++$i) {
$chr = ord($str[$i]);
switch ($chr) {
case 0: // null
$p->binary = true;
$p->literal = true;
// No need to scan input anymore.
$this->_skip = true;
break 2;
case 10: // LF
case 13: // CR
$p->literal = true;
break;
case 32: // SPACE
case 34: // "
case 40: // (
case 41: // )
case 92: // \
case 123: // {
case 127: // DEL
// These are all invalid ATOM characters.
$p->quoted = true;
break;
case 37: // %
case 42: // *
// These are not quoted if being used as wildcards.
if (empty($p->no_quote_list)) {
$p->quoted = true;
}
break;
default:
if ($chr < 32) {
// CTL characters must be, at a minimum, quoted.
$p->quoted = true;
} elseif ($chr > 127) {
$p->nonascii = true;
// 8-bit chars must be in a literal.
$p->literal = true;
}
break;
}
}
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
if ($p->literal) {
$p->quoted = false;
}
return PSFS_PASS_ON;
}
}
@@ -1,108 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP parenthesized list (RFC 3501 [4.4]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_List extends Horde_Imap_Client_Data_Format implements Countable, IteratorAggregate
{
/**
* @see add()
*/
public function __construct($data = null)
{
parent::__construct(array());
if (!is_null($data)) {
$this->add($data);
}
}
/**
* Add an element to the list.
*
* @param mixed $data The data element(s) to add. Either a
* Horde_Imap_Client_Data_Format object, a string
* value that will be treated as an IMAP atom, or
* an array (or iterable object) of objects to add.
* @param boolean $merge Merge the contents of any container objects,
* instead of adding the objects themselves?
*
* @return Horde_Imap_Client_Data_Format_List This object to allow for
* chainable calls (since
* 2.10.0).
*/
public function add($data, $merge = false)
{
if (is_array($data) || ($merge && ($data instanceof Traversable))) {
foreach ($data as $val) {
$this->add($val);
}
} elseif (is_object($data)) {
$this->_data[] = $data;
} elseif (!is_null($data)) {
$this->_data[] = new Horde_Imap_Client_Data_Format_Atom($data);
}
return $this;
}
/**
*/
public function __toString()
{
$out = '';
foreach ($this as $val) {
if ($val instanceof $this) {
$out .= '(' . $val->escape() . ') ';
} elseif (($val instanceof Horde_Imap_Client_Data_Format_String) &&
$val->literal()) {
/* ERROR: Requires literal output. */
return '';
} else {
$out .= $val->escape() . ' ';
}
}
return rtrim($out);
}
/* Countable methods. */
/**
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->_data);
}
/* IteratorAggregate method. */
/**
* Iterator loops through the data elements contained in this list.
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->_data);
}
}
@@ -1,38 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP mailbox string used in a LIST command.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_ListMailbox
extends Horde_Imap_Client_Data_Format_Mailbox
{
/**
*/
protected function _filterParams()
{
$ob = parent::_filterParams();
/* Don't quote % or * characters. */
$ob->no_quote_list = true;
return $ob;
}
}
@@ -1,39 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP mailbox string used in a LIST command
* when UTF8=ACCEPT is supported/enabled on the server (RFC 6855 [3]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_ListMailbox_Utf8
extends Horde_Imap_Client_Data_Format_Mailbox_Utf8
{
/**
*/
protected function _filterParams()
{
$ob = parent::_filterParams();
/* Don't quote % or * characters. */
$ob->no_quote_list = true;
return $ob;
}
}
@@ -1,99 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP mailbox string (RFC 3501 [9]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Mailbox
extends Horde_Imap_Client_Data_Format_Astring
{
/**
* Mailbox encoding.
*
* @var string
*/
protected $_encoding = 'utf7imap';
/**
* Mailbox object.
*
* @var Horde_Imap_Client_Mailbox
*/
protected $_mailbox;
/**
* @param mixed $data Either a mailbox object or a UTF-8 mailbox name.
*/
public function __construct($data)
{
$this->_mailbox = Horde_Imap_Client_Mailbox::get($data);
parent::__construct($this->_mailbox->{$this->_encoding});
}
/**
*/
public function __toString()
{
return strval($this->_mailbox);
}
/**
*/
public function getData()
{
return $this->_mailbox;
}
/**
* @throws Horde_Imap_Client_Exception
*/
public function binary()
{
if (parent::binary()) {
// Mailbox data can NEVER be sent as binary.
/* @todo: Disable until Horde_Imap_Client 3.0 */
// throw new Horde_Imap_Client_Exception(
// 'Client error: can not send mailbox to IMAP server as binary data.'
// );
// Temporary fix: send a blank mailbox string.
$this->_mailbox = Horde_Imap_Client_Mailbox::get('');
}
return false;
}
/**
*/
public function length()
{
return strlen($this->_mailbox->{$this->_encoding});
}
/**
*/
public function getStream()
{
$stream = new Horde_Stream_Temp();
$stream->add($this->_mailbox->{$this->_encoding});
return $stream;
}
}
@@ -1,57 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP mailbox string allowed when UTF8=ACCEPT
* is supported/enabled on the server (RFC 6855 [3]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Mailbox_Utf8
extends Horde_Imap_Client_Data_Format_Mailbox
implements Horde_Imap_Client_Data_Format_String_Support_Nonascii
{
/**
*/
protected $_encoding = 'utf8';
/**
*/
public function __construct($data)
{
parent::__construct($data);
/* RFC 3501 allows any US-ASCII character, except null (\0), in
* mailbox data.
* RFC 6855 [3] institutes additional limitations on valid mailbox
* characters, to comply with RFC 5198 [2] (Net-Unicode Definition):
* "MUST NOT contain control characters (U+0000-U+001F and
* U+0080-U+009F), a delete character (U+007F), a line separator
* (U+2028), or a paragraph separator (U+2029)." */
if ($this->quoted() &&
preg_match('/[\x00-\x1f\x7f\x80-\x9f\x{2028}\x{2029}]/u', strval($this))) {
throw new Horde_Imap_Client_Data_Format_Exception(
'Invalid character found in mailbox data.'
);
}
if ($this->literal()) {
$this->forceQuoted();
}
}
}
@@ -1,46 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP NIL (RFC 3501 [4.5]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Nil extends Horde_Imap_Client_Data_Format
{
/**
*/
public function __construct($data = null)
{
// Don't store any data in object.
}
/**
*/
public function __toString()
{
return '';
}
/**
*/
public function escape()
{
return 'NIL';
}
}
@@ -1,93 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP nstring (NIL or string) (RFC 3501 [4.5]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Nstring extends Horde_Imap_Client_Data_Format_String
{
/**
*/
public function __construct($data = null)
{
/* Data can be null (NIL) here. */
if (is_null($data)) {
$this->_data = null;
} else {
parent::__construct($data);
}
}
/**
*/
public function __toString()
{
return is_null($this->_data)
? ''
: parent::__toString();
}
/**
*/
public function escape()
{
return is_null($this->_data)
? 'NIL'
: parent::escape();
}
public function escapeStream()
{
if (is_null($this->_data)) {
$stream = new Horde_Stream_Temp();
$stream->add('NIL', true);
return $stream->stream;
}
return parent::escapeStream();
}
/**
*/
public function quoted()
{
return is_null($this->_data)
? false
: parent::quoted();
}
/**
*/
public function length()
{
return is_null($this->_data)
? 0
: parent::length();
}
/**
*/
public function getStream()
{
return is_null($this->_data)
? new Horde_Stream_Temp()
: parent::getStream();
}
}
@@ -1,28 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Extend nstring element to allow for non-ASCII characters.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.25.0
*/
class Horde_Imap_Client_Data_Format_Nstring_Nonascii
extends Horde_Imap_Client_Data_Format_Nstring
implements Horde_Imap_Client_Data_Format_String_Support_Nonascii
{
}
@@ -1,41 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP number (RFC 3501 [4.2]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_Number extends Horde_Imap_Client_Data_Format
{
/**
*/
public function __toString()
{
return strval(intval($this->_data));
}
/**
*/
public function verify()
{
if (!is_numeric($this->_data)) {
throw new Horde_Imap_Client_Data_Format_Exception('Illegal character in IMAP number.');
}
}
}
@@ -1,219 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representation of an IMAP string (RFC 3501 [4.3]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Format_String
extends Horde_Imap_Client_Data_Format
{
/**
* String filter parameters.
*
* @var string
*/
protected $_filter;
/**
* @param array $opts Additional options:
* - eol: (boolean) If true, normalize EOLs in input. @since 2.2.0
* - skipscan: (boolean) If true, don't scan input for
* binary/literal/quoted data. @since 2.2.0
*
* @throws Horde_Imap_Client_Data_Format_Exception
*/
public function __construct($data, array $opts = array())
{
/* String data is stored in a stream. */
$this->_data = new Horde_Stream_Temp();
if (empty($opts['skipscan'])) {
$this->_filter = $this->_filterParams();
stream_filter_register('horde_imap_client_string', 'Horde_Imap_Client_Data_Format_Filter_String');
$res = stream_filter_append($this->_data->stream, 'horde_imap_client_string', STREAM_FILTER_WRITE, $this->_filter);
} else {
$res = null;
}
if (empty($opts['eol'])) {
$res2 = null;
} else {
stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
$res2 = stream_filter_append($this->_data->stream, 'horde_eol', STREAM_FILTER_WRITE);
}
$this->_data->add($data);
if (!is_null($res)) {
stream_filter_remove($res);
}
if (!is_null($res2)) {
stream_filter_remove($res2);
}
if (isset($this->_filter) &&
$this->_filter->nonascii &&
!($this instanceof Horde_Imap_Client_Data_Format_String_Support_Nonascii)) {
throw new Horde_Imap_Client_Data_Format_Exception(
'String contains non-ASCII characters.'
);
}
}
/**
* Return the base string filter parameters.
*
* @return object Filter parameters.
*/
protected function _filterParams()
{
return new stdClass;
}
/**
*/
public function __toString()
{
return $this->_data->getString(0);
}
/**
*/
public function escape()
{
if ($this->literal()) {
throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
}
return $this->quoted()
? stream_get_contents($this->escapeStream())
: $this->_data->getString(0);
}
/**
* Return the escaped string as a stream.
*
* @return resource The IMAP escaped stream.
*/
public function escapeStream()
{
if ($this->literal()) {
throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
}
rewind($this->_data->stream);
$stream = new Horde_Stream_Temp();
$stream->add($this->_data, true);
stream_filter_register('horde_imap_client_string_quote', 'Horde_Imap_Client_Data_Format_Filter_Quote');
stream_filter_append($stream->stream, 'horde_imap_client_string_quote', STREAM_FILTER_READ);
return $stream->stream;
}
/**
* Does this data item require quoted string output?
*
* @return boolean True if quoted output is required.
*/
public function quoted()
{
/* IMAP strings MUST be quoted if they are not a literal. */
return (!isset($this->_filter) || !$this->_filter->literal);
}
/**
* Force item to be output quoted.
*/
public function forceQuoted()
{
$this->_filter = $this->_filterParams();
$this->_filter->binary = false;
$this->_filter->literal = false;
$this->_filter->quoted = true;
}
/**
* Does this data item require literal string output?
*
* @return boolean True if literal output is required.
*/
public function literal()
{
return (isset($this->_filter) && $this->_filter->literal);
}
/**
* Force item to be output as a literal.
*/
public function forceLiteral()
{
$this->_filter = $this->_filterParams();
// Keep binary status, if set
$this->_filter->literal = true;
$this->_filter->quoted = false;
}
/**
* If literal output, is the data binary?
*
* @return boolean True if the literal output is binary.
*/
public function binary()
{
return (isset($this->_filter) && !empty($this->_filter->binary));
}
/**
* Force item to be output as a binary literal.
*/
public function forceBinary()
{
$this->_filter = $this->_filterParams();
$this->_filter->binary = true;
$this->_filter->literal = true;
$this->_filter->quoted = false;
}
/**
* Return the length of the data.
*
* @since 2.2.0
*
* @return integer Data length.
*/
public function length()
{
return $this->_data->length();
}
/**
* Return the contents of the string as a stream object.
*
* @since 2.3.0
*
* @return Horde_Stream The stream object.
*/
public function getStream()
{
return $this->_data;
}
}
@@ -1,28 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Extend string element to allow for non-ASCII characters.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.25.0
*/
class Horde_Imap_Client_Data_Format_String_Nonascii
extends Horde_Imap_Client_Data_Format_String
implements Horde_Imap_Client_Data_Format_String_Support_Nonascii
{
}
@@ -1,27 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Interface a Horde_Imap_Client_Data_Format_String object should extend if
* it supports non-ASCII output (RFC 3501 [4.3.1]).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.25.0
*/
interface Horde_Imap_Client_Data_Format_String_Support_Nonascii
{
}
@@ -1,160 +0,0 @@
<?php
/**
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Namespace data.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.21.0
*
* @property-read string $base The namespace base ($name without trailing
* delimiter) (UTF-8).
* @property string $delimiter The namespace delimiter.
* @property boolean $hidden Is this a hidden namespace?
* @property string $name The namespace name (UTF-8).
* @property string $translation Returns the translated name of the namespace
* (UTF-8).
* @property integer $type The namespace type. Either self::NS_PERSONAL,
* self::NS_OTHER, or self::NS_SHARED.
*/
class Horde_Imap_Client_Data_Namespace implements Serializable
{
/* Namespace type constants. */
const NS_PERSONAL = 1;
const NS_OTHER = 2;
const NS_SHARED = 3;
/**
* Data object.
*
* @var array
*/
protected $_data = array();
/**
* Strips namespace information from the given mailbox name.
*
* @param string $mbox Mailbox name.
*
* @return string Mailbox name with namespace prefix stripped.
*/
public function stripNamespace($mbox)
{
$mbox = strval($mbox);
$name = $this->name;
return (strlen($name) && (strpos($mbox, $name) === 0))
? substr($mbox, strlen($name))
: $mbox;
}
/**
*/
public function __get($name)
{
if (isset($this->_data[$name])) {
return $this->_data[$name];
}
switch ($name) {
case 'base':
return rtrim($this->name, $this->delimiter);
case 'delimiter':
case 'name':
case 'translation':
return '';
case 'hidden':
return false;
case 'type':
return self::NS_PERSONAL;
}
return null;
}
/**
*/
public function __set($name, $value)
{
switch ($name) {
case 'delimiter':
case 'name':
case 'translation':
$this->_data[$name] = strval($value);
break;
case 'hidden':
$this->_data[$name] = (bool)$value;
break;
case 'type':
$this->_data[$name] = intval($value);
break;
}
}
/**
*/
public function __isset($name)
{
return isset($this->_data[$name]);
}
/**
*/
public function __toString()
{
return $this->name;
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version change.');
}
$this->__unserialize($data);
}
/**
* @return array
*/
public function __serialize()
{
return $this->_data;
}
public function __unserialize(array $data)
{
$this->_data = $data;
}
}
@@ -1,198 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Query the search charsets available on a server.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.24.0
*
* @property-read array $charsets The list of valid charsets that have been
* discovered on the server.
*/
class Horde_Imap_Client_Data_SearchCharset
implements Serializable, SplSubject
{
/**
* Base client object.
*
* @var Horde_Imap_Client_Base
*/
protected $_baseob;
/**
* Charset data.
*
* @var array
*/
protected $_charsets = array(
'US-ASCII' => true
);
/**
* Observers.
*
* @var array
*/
protected $_observers = array();
/**
*/
public function __get($name)
{
switch ($name) {
case 'charsets':
return array_keys(array_filter($this->_charsets));
}
}
/**
*/
public function setBaseOb(Horde_Imap_Client_Base $ob)
{
$this->_baseob = $ob;
}
/**
* Query the validity of a charset.
*
* @param string $charset The charset to query.
* @param boolean $cached If true, only query cached values.
*
* @return boolean True if the charset is valid for searching.
*/
public function query($charset, $cached = false)
{
$charset = Horde_String::upper($charset);
if (isset($this->_charsets[$charset])) {
return $this->_charsets[$charset];
} elseif ($cached) {
return null;
}
if (!$this->_baseob) {
throw new RuntimeException(
'Base object needs to be defined to query for charset.'
);
}
/* Use a dummy search query and search for BADCHARSET response. */
$query = new Horde_Imap_Client_Search_Query();
$query->charset($charset, false);
$query->ids($this->_baseob->getIdsOb(1, true));
$query->text('a');
try {
$this->_baseob->search('INBOX', $query, array(
'nocache' => true,
'sequence' => true
));
$this->_charsets[$charset] = true;
} catch (Horde_Imap_Client_Exception $e) {
$this->_charsets[$charset] = ($e->getCode() !== Horde_Imap_Client_Exception::BADCHARSET);
}
$this->notify();
return $this->_charsets[$charset];
}
/**
* Set the validity of a given charset.
*
* @param string $charset The charset.
* @param boolean $valid Is charset valid?
*/
public function setValid($charset, $valid = true)
{
$charset = Horde_String::upper($charset);
$valid = (bool)$valid;
if (!isset($this->_charsets[$charset]) ||
($this->_charsets[$charset] !== $valid)) {
$this->_charsets[$charset] = $valid;
$this->notify();
}
}
/* SplSubject methods. */
/**
*/
#[ReturnTypeWillChange]
public function attach(SplObserver $observer)
{
$this->detach($observer);
$this->_observers[] = $observer;
}
/**
*/
#[ReturnTypeWillChange]
public function detach(SplObserver $observer)
{
if (($key = array_search($observer, $this->_observers, true)) !== false) {
unset($this->_observers[$key]);
}
}
/**
* Notification is triggered internally whenever the object's internal
* data storage is altered.
*/
#[ReturnTypeWillChange]
public function notify()
{
foreach ($this->_observers as $val) {
$val->update($this);
}
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version change');
}
$this->__unserialize($data);
}
/**
* @return array
*/
public function __serialize()
{
return $this->_charsets;
}
public function __unserialize(array $data)
{
$this->_charsets = $data;
}
}
@@ -1,78 +0,0 @@
<?php
/**
* Copyright 2014-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Query the search charsets available on a server that supports the UTF-8
* IMAP extension (RFC 6855).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2014-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.24.0
*/
class Horde_Imap_Client_Data_SearchCharset_Utf8
extends Horde_Imap_Client_Data_SearchCharset
{
/**
* Charset data.
*
* @var array
*/
protected $_charsets = array(
'US-ASCII' => true,
'UTF-8' => true
);
/**
*/
public function query($charset, $cached = false)
{
return isset($this->_charsets[Horde_String::upper($charset)]);
}
/**
*/
public function setValid($charset, $valid = true)
{
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return '';
}
/**
*/
public function unserialize($data)
{
}
/**
* @return array
*/
public function __serialize()
{
return array();
}
public function __unserialize(array $data)
{
}
}
@@ -1,267 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Mailbox synchronization results.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.2.0
*
* @property-read Horde_Imap_Client_Ids $flagsuids List of messages with flag
* changes.
* @property-read Horde_Imap_Client_Ids $newmsgsuids List of new messages.
* @property-read Horde_Imap_Client_Ids $vanisheduids List of messages that
* have vanished.
*/
class Horde_Imap_Client_Data_Sync
{
/**
* Mappings of status() values to sync keys.
*
* @since 2.8.0
*
* @var array
*/
public static $map = array(
'H' => 'highestmodseq',
'M' => 'messages',
'U' => 'uidnext',
'V' => 'uidvalidity'
);
/**
* Are there messages that have had flag changes?
*
* @var boolean
*/
public $flags = null;
/**
* The previous value of HIGHESTMODSEQ.
*
* @since 2.8.0
*
* @var integer
*/
public $highestmodseq = null;
/**
* The synchronized mailbox.
*
* @var Horde_Imap_Client_Mailbox
*/
public $mailbox;
/**
* The previous number of messages in the mailbox.
*
* @since 2.8.0
*
* @var integer
*/
public $messages = null;
/**
* Are there new messages?
*
* @var boolean
*/
public $newmsgs = null;
/**
* The previous value of UIDNEXT.
*
* @since 2.8.0
*
* @var integer
*/
public $uidnext = null;
/**
* The previous value of UIDVALIDITY.
*
* @since 2.8.0
*
* @var integer
*/
public $uidvalidity = null;
/**
* The UIDs of messages that are guaranteed to have vanished. This list is
* only guaranteed to be available if the server supports QRESYNC or a
* list of known UIDs is passed to the sync() method.
*
* @var Horde_Imap_Client_Ids
*/
public $vanished = null;
/**
* UIDs of messages that have had flag changes.
*
* @var Horde_Imap_Client_Ids
*/
protected $_flagsuids;
/**
* UIDs of new messages.
*
* @var Horde_Imap_Client_Ids
*/
protected $_newmsgsuids;
/**
* UIDs of messages that have vanished.
*
* @var Horde_Imap_Client_Ids
*/
protected $_vanisheduids;
/**
* Constructor.
*
* @param Horde_Imap_Client_Base $base_ob Base driver object.
* @param mixed $mailbox Mailbox to sync.
* @param array $sync Token sync data.
* @param array $curr Current sync data.
* @param integer $criteria Mask of criteria to return.
* @param Horde_Imap_Client_Ids $ids List of known UIDs.
*
* @throws Horde_Imap_Client_Exception
* @throws Horde_Imap_Client_Exception_Sync
*/
public function __construct(Horde_Imap_Client_Base $base_ob, $mailbox,
$sync, $curr, $criteria, $ids)
{
foreach (self::$map as $key => $val) {
if (isset($sync[$key])) {
$this->$val = $sync[$key];
}
}
/* Check uidvalidity. */
if (!$this->uidvalidity || ($curr['V'] != $this->uidvalidity)) {
throw new Horde_Imap_Client_Exception_Sync('UIDs in cached mailbox have changed.', Horde_Imap_Client_Exception_Sync::UIDVALIDITY_CHANGED);
}
$this->mailbox = $mailbox;
/* This was a UIDVALIDITY check only. */
if (!$criteria) {
return;
}
$sync_all = ($criteria & Horde_Imap_Client::SYNC_ALL);
/* New messages. */
if ($sync_all ||
($criteria & Horde_Imap_Client::SYNC_NEWMSGS) ||
($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS)) {
$this->newmsgs = empty($this->uidnext)
? !empty($curr['U'])
: (!empty($curr['U']) && ($curr['U'] > $this->uidnext));
if ($this->newmsgs &&
($sync_all ||
($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS))) {
$new_ids = empty($this->uidnext)
? Horde_Imap_Client_Ids::ALL
: ($this->uidnext . ':' . $curr['U']);
$squery = new Horde_Imap_Client_Search_Query();
$squery->ids(new Horde_Imap_Client_Ids($new_ids));
$sres = $base_ob->search($mailbox, $squery);
$this->_newmsgsuids = $sres['match'];
}
}
/* Do single status call to get all necessary data. */
if ($this->highestmodseq &&
($sync_all ||
($criteria & Horde_Imap_Client::SYNC_FLAGS) ||
($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS) ||
($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS))) {
$status_sync = $base_ob->status($mailbox, Horde_Imap_Client::STATUS_SYNCMODSEQ | Horde_Imap_Client::STATUS_SYNCFLAGUIDS | Horde_Imap_Client::STATUS_SYNCVANISHED);
if (!is_null($ids)) {
$ids = $base_ob->resolveIds($mailbox, $ids);
}
}
/* Flag changes. */
if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGS)) {
$this->flags = $this->highestmodseq
? ($this->highestmodseq != $curr['H'])
: true;
}
if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS)) {
if ($this->highestmodseq) {
if ($this->highestmodseq == $status_sync['syncmodseq']) {
$this->_flagsuids = is_null($ids)
? $status_sync['syncflaguids']
: $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncflaguids']->ids));
} else {
$squery = new Horde_Imap_Client_Search_Query();
$squery->modseq($this->highestmodseq + 1);
$sres = $base_ob->search($mailbox, $squery, array(
'ids' => $ids
));
$this->_flagsuids = $sres['match'];
}
} else {
/* Without MODSEQ, need to mark all FLAGS as changed. */
$this->_flagsuids = $base_ob->resolveIds($mailbox, is_null($ids) ? $base_ob->getIdsOb(Horde_Imap_Client_Ids::ALL) : $ids);
}
}
/* Vanished messages. */
if ($sync_all ||
($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS)) {
if ($this->highestmodseq &&
($this->highestmodseq == $status_sync['syncmodseq'])) {
$vanished = is_null($ids)
? $status_sync['syncvanished']
: $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncvanished']->ids));
} else {
$vanished = $base_ob->vanished($mailbox, $this->highestmodseq ? $this->highestmodseq : 1, array(
'ids' => $ids
));
}
$this->vanished = (bool)count($vanished);
$this->_vanisheduids = $vanished;
}
}
/**
*/
public function __get($name)
{
switch ($name) {
case 'flagsuids':
case 'newmsgsuids':
case 'vanisheduids':
return empty($this->{'_' . $name})
? new Horde_Imap_Client_Ids()
: $this->{'_' . $name};
}
}
}
@@ -1,231 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Object representing the threaded sort results from
* Horde_Imap_Client_Base#thread().
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Data_Thread implements Countable, Serializable
{
/**
* Internal thread data structure. Keys are base values, values are arrays
* with keys as the ID and values as the level.
*
* @var array
*/
protected $_thread = array();
/**
* The index type.
*
* @var string
*/
protected $_type;
/**
* Constructor.
*
* @param array $data See $_thread.
* @param string $type Either 'sequence' or 'uid'.
*/
public function __construct($data, $type)
{
$this->_thread = $data;
$this->_type = $type;
}
/**
* Return the ID type.
*
* @return string Either 'sequence' or 'uid'.
*/
public function getType()
{
return $this->_type;
}
/**
* Return the sorted list of messages indices.
*
* @return Horde_Imap_Client_Ids The sorted list of messages.
*/
public function messageList()
{
return new Horde_Imap_Client_Ids($this->_getAllIndices(), $this->getType() == 'sequence');
}
/**
* Returns the list of messages in a thread.
*
* @param integer $index An index contained in the thread.
*
* @return array Keys are indices, values are objects with the following
* properties:
* - base: (integer) Base ID of the thread. If null, thread is a single
* message.
* - last: (boolean) If true, this is the last index in the sublevel.
* - level: (integer) The sublevel of the index.
*/
public function getThread($index)
{
foreach ($this->_thread as $v) {
if (isset($v[$index])) {
reset($v);
$ob = new stdClass;
$ob->base = (count($v) > 1) ? key($v) : null;
$ob->last = false;
$levels = $out = array();
$last = 0;
while (($v2 = current($v)) !== false) {
$k2 = key($v);
$ob2 = clone $ob;
$ob2->level = $v2;
$out[$k2] = $ob2;
if (($last < $v2) && isset($levels[$v2])) {
$out[$levels[$v2]]->last = true;
}
$levels[$v2] = $k2;
$last = $v2;
next($v);
}
foreach ($levels as $v) {
$out[$v]->last = true;
}
return $out;
}
}
return array();
}
/**
* Returns array of all threads.
*
* @return array Keys of thread arrays are indices, values are objects with the following
* properties:
* - base: (integer) Base ID of the thread. If null, thread is a single
* message.
* - last: (boolean) If true, this is the last index in the sublevel.
* - level: (integer) The sublevel of the index.
*/
public function getThreads()
{
$data = array();
foreach ($this->_thread as $v) {
reset($v);
$ob = new stdClass;
$ob->base = (count($v) > 1) ? key($v) : null;
$ob->last = false;
$levels = $out = array();
$last = 0;
while (($v2 = current($v)) !== false) {
$k2 = key($v);
$ob2 = clone $ob;
$ob2->level = $v2;
$out[$k2] = $ob2;
if (($last < $v2) && isset($levels[$v2])) {
$out[$levels[$v2]]->last = true;
}
$levels[$v2] = $k2;
$last = $v2;
next($v);
}
foreach ($levels as $v) {
$out[$v]->last = true;
}
$data[] = $out;
}
return $data;
}
/* Countable methods. */
/**
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->_getAllIndices());
}
/* Serializable methods. */
/**
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
*/
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version changed.');
}
$this->__unserialize($data);
}
/**
* @return array
*/
public function __serialize()
{
return [$this->_thread, $this->_type];
}
public function __unserialize(array $data)
{
list($this->_thread, $this->_type) = $data;
}
/* Protected methods. */
/**
* Return all indices.
*
* @return array An array of indices.
*/
protected function _getAllIndices()
{
$out = array();
foreach ($this->_thread as $val) {
$out += $val;
}
return array_keys($out);
}
}
@@ -1,119 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* A wrapper around PHP's native DateTime class that handles improperly
* formatted dates and adds a few features missing from the base object
* (string representation; doesn't fail on bad date input).
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_DateTime extends DateTime
{
/**
*/
public function __construct($time = null, $tz = null)
{
/* See https://bugs.php.net/bug.php?id=67118 */
$bug_67118 = (version_compare(PHP_VERSION, '5.6', '>=')) ||
in_array(PHP_VERSION, array('5.4.29', '5.5.13'));
$tz = new DateTimeZone('UTC');
/* Bug #14381 Catch malformed offset - which doesn't cause
DateTime to throw exception. */
if ($time !== null && substr(rtrim($time), -5) === ' 0000') {
$time = substr(trim($time), 0, strlen(trim($time)) - 5) . ' +0000';
try {
if ($bug_67118) {
new DateTime($time, $tz);
}
parent::__construct($time, $tz);
return;
} catch (Exception $e) {}
}
try {
if ($bug_67118) {
new DateTime($time === null ? 'now' : $time, $tz);
}
parent::__construct($time === null ? 'now' : $time, $tz);
return;
} catch (Exception $e) {}
/* Check for malformed day-of-week parts, usually incorrectly
* localized. E.g. Fr, 15 Apr 2016 15:15:09 +0000 */
if ($time !== null && !preg_match("/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun),/", $time)) {
$time = preg_replace("/^(\S*,)/", '', $time, 1, $i);
if ($i) {
try {
if ($bug_67118) {
new DateTime($time, $tz);
}
parent::__construct($time, $tz);
return;
} catch (Exception $e) {}
}
}
/* Bug #5717 - Check for UT vs. UTC. */
if ($time !== null && substr(rtrim($time), -3) === ' UT') {
try {
if ($bug_67118) {
new DateTime($time . 'C', $tz);
}
parent::__construct($time . 'C', $tz);
return;
} catch (Exception $e) {}
}
/* Bug #9847 - Catch paranthesized timezone information at end of date
* string. */
$date = preg_replace("/\s*\([^\)]+\)\s*$/", '', $time, -1, $i);
if ($i) {
try {
if ($bug_67118) {
new DateTime($date, $tz);
}
parent::__construct($date, $tz);
return;
} catch (Exception $e) {}
}
parent::__construct('@-1', $tz);
}
/**
* String representation: UNIX timestamp.
*/
public function __toString()
{
return $this->error()
? '0'
: strval($this->getTimestamp());
}
/**
* Was this an unparseable date?
*
* @return boolean True if unparseable.
*/
public function error()
{
return ($this->getTimestamp() === -1);
}
}
@@ -1,303 +0,0 @@
<?php
/**
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception handler for the Horde_Imap_Client package.
*
* Additional server debug information MAY be found in the $details
* property.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2008-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Exception extends Horde_Exception_Wrapped
{
/* Error message codes. */
/**
* Unspecified error (DEFAULT).
*/
const UNSPECIFIED = 0;
/**
* There was an unrecoverable error in UTF7IMAP -> UTF8 conversion.
*/
const UTF7IMAP_CONVERSION = 3;
/**
* The server ended the connection.
*/
const DISCONNECT = 4;
/**
* The charset used in the search query is not supported on the
* server. */
const BADCHARSET = 5;
/**
* There were errors parsing the MIME/RFC 2822 header of the part.
*/
const PARSEERROR = 6;
/**
* The server could not decode the MIME part (see RFC 3516).
*/
const UNKNOWNCTE = 7;
/**
* The comparator specified by setComparator() was not recognized by the
* IMAP server
*/
const BADCOMPARATOR = 9;
/**
* RFC 7162 [3.1.2.2] - All mailboxes are not required to support
* mod-sequences.
*/
const MBOXNOMODSEQ = 10;
/**
* Thrown if server denies the network connection.
*/
const SERVER_CONNECT = 11;
/**
* Thrown if read error for server response.
*/
const SERVER_READERROR = 12;
/**
* Thrown if write error in server interaction.
*/
const SERVER_WRITEERROR = 16;
/**
* Thrown on CATENATE if the URL is invalid.
*/
const CATENATE_BADURL = 13;
/**
* Thrown on CATENATE if the message was too big.
*/
const CATENATE_TOOBIG = 14;
/**
* Thrown on CREATE if special-use attribute is not supported.
*/
const USEATTR = 15;
/**
* The user did not have permissions to carry out the operation.
*/
const NOPERM = 17;
/**
* The operation was not successful because another user is holding
* a necessary resource. The operation may succeed if attempted later.
*/
const INUSE = 18;
/**
* The operation failed because data on the server was corrupt.
*/
const CORRUPTION = 19;
/**
* The operation failed because it exceeded some limit on the server.
*/
const LIMIT = 20;
/**
* The operation failed because the user is over their quota.
*/
const OVERQUOTA = 21;
/**
* The operation failed because the requested creation object already
* exists.
*/
const ALREADYEXISTS = 22;
/**
* The operation failed because the requested deletion object did not
* exist.
*/
const NONEXISTENT = 23;
/**
* Setting metadata failed because the size of its value is too large.
* The maximum octet count the server is willing to accept will be
* in the exception message string.
*/
const METADATA_MAXSIZE = 24;
/**
* Setting metadata failed because the maximum number of allowed
* annotations has already been reached.
*/
const METADATA_TOOMANY = 25;
/**
* Setting metadata failed because the server does not support private
* annotations on one of the specified mailboxes.
*/
const METADATA_NOPRIVATE = 26;
/**
* Invalid metadata entry.
*/
const METADATA_INVALID = 27;
// Login failures
/**
* Could not start mandatory TLS connection.
*/
const LOGIN_TLSFAILURE = 100;
/**
* Could not find an available authentication method.
*/
const LOGIN_NOAUTHMETHOD = 101;
/**
* Generic authentication failure.
*/
const LOGIN_AUTHENTICATIONFAILED = 102;
/**
* Remote server is unavailable.
*/
const LOGIN_UNAVAILABLE = 103;
/**
* Authentication succeeded, but authorization failed.
*/
const LOGIN_AUTHORIZATIONFAILED = 104;
/**
* Authentication is no longer permitted with this passphrase.
*/
const LOGIN_EXPIRED = 105;
/**
* Login requires privacy.
*/
const LOGIN_PRIVACYREQUIRED = 106;
/**
* Server verification failed (SCRAM authentication).
*/
const LOGIN_SERVER_VERIFICATION_FAILED = 107;
// Mailbox access failures
/**
* Could not open/access mailbox
*/
const MAILBOX_NOOPEN = 200;
/**
* Could not complete the command because the mailbox is read-only
*/
const MAILBOX_READONLY = 201;
// POP3 specific error codes
/**
* Temporary issue. Generally, there is no need to alarm the user for
* errors of this type.
*/
const POP3_TEMP_ERROR = 300;
/**
* Permanent error indicated by server.
*/
const POP3_PERM_ERROR = 301;
// Unsupported feature error codes
/**
* Function/feature is not supported on this server.
*/
const NOT_SUPPORTED = 400;
/**
* Raw error message (in English).
*
* @since 2.18.0
*
* @var string
*/
public $raw_msg = '';
/**
* Constructor.
*
* @param string $message Error message (non-translated).
* @param int $code Error code.
*/
public function __construct($message = null, $code = null)
{
parent::__construct($message, $code);
$this->raw_msg = $this->message;
try {
$this->message = Horde_Imap_Client_Translation::t($this->message);
} catch (Horde_Translation_Exception $e) {}
}
/**
* Allow the error message to be altered.
*
* @param string $msg Error message.
*/
public function setMessage($msg)
{
$this->message = strval($msg);
}
/**
* Allow the error code to be altered.
*
* @param integer $code Error code.
*/
public function setCode($code)
{
$this->code = intval($code);
}
/**
* Perform substitution of variables in the error message.
*
* Needed to allow for correct translation of error message.
*
* @since 2.22.0
*
* @param array $args Arguments used for substitution.
*/
public function messagePrintf(array $args = array())
{
$this->raw_msg = vsprintf($this->raw_msg, $args);
$this->message = vsprintf($this->message, $args);
}
}
@@ -1,54 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception thrown for non-supported server extensions.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Exception_NoSupportExtension
extends Horde_Imap_Client_Exception
{
/**
* The extension not supported on the server.
*
* @var string
*/
public $extension;
/**
* Constructor.
*
* @param string $extension The extension not supported on the server.
* @param string $msg A non-standard error message to use instead
* of the default.
*/
public function __construct($extension, $msg = null)
{
$this->extension = $extension;
if (is_null($msg)) {
$msg = sprintf(
Horde_Imap_Client_Translation::r("The server does not support the %s extension."),
$extension
);
}
parent::__construct($msg, self::NOT_SUPPORTED);
}
}
@@ -1,40 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception thrown for non-supported IMAP features on POP3 servers.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Exception_NoSupportPop3
extends Horde_Imap_Client_Exception
{
/**
* Constructor.
*
* @param string $feature The feature not supported in POP3.
*/
public function __construct($feature)
{
parent::__construct(
Horde_Imap_Client_Translation::r("%s not supported on POP3 servers."),
self::NOT_SUPPORTED
);
$this->messagePrintf(array($feature));
}
}
@@ -1,50 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception thrown if search query text cannot be converted to different
* charset.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Exception_SearchCharset
extends Horde_Imap_Client_Exception
{
/**
* Charset that was attempted to be converted to.
*
* @var string
*/
public $charset;
/**
* Constructor.
*
* @param string $charset The charset that was attempted to be converted
* to.
*/
public function __construct($charset)
{
$this->charset = $charset;
parent::__construct(
Horde_Imap_Client_Translation::r("Cannot convert search query text to new charset"),
self::BADCHARSET
);
}
}
@@ -1,88 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception thrown for server error responses.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*
* @property-read string $command The command that caused the BAD/NO error
* status.
* @property-read array $resp_data The response data array.
* @property-read integer $status Server error status.
*/
class Horde_Imap_Client_Exception_ServerResponse extends Horde_Imap_Client_Exception
{
/**
* Pipeline object.
*
* @var Horde_Imap_Client_Interaction_Pipeline
*/
protected $_pipeline;
/**
* Server response object.
*
* @var Horde_Imap_Client_Interaction_Server
*/
protected $_server;
/**
* Constructor.
*
* @param string|null $msg Error message.
* @param integer $code Error code.
* @param Horde_Imap_Client_Interaction_Server $server Server ob.
* @param Horde_Imap_Client_Interaction_Pipeline $pipeline Pipeline ob.
*/
public function __construct(
$msg,
$code,
Horde_Imap_Client_Interaction_Server $server,
Horde_Imap_Client_Interaction_Pipeline $pipeline
)
{
$this->details = strval($server->token);
$this->_pipeline = $pipeline;
$this->_server = $server;
parent::__construct($msg, $code);
}
/**
*/
public function __get($name)
{
switch ($name) {
case 'command':
return ($this->_server instanceof Horde_Imap_Client_Interaction_Server_Tagged)
? $this->_pipeline->getCmd($this->_server->tag)->getCommand()
: null;
case 'resp_data':
return $this->_pipeline->data;
case 'status':
return $this->_server->status;
default:
return parent::__get($name);
}
}
}
@@ -1,37 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Exception thrown for mailbox synchronization errors.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Exception_Sync extends Horde_Exception_Wrapped
{
/* Error message codes. */
/**
* Token could not be parsed.
*/
const BAD_TOKEN = 1;
/**
* UIDVALIDITY of the mailbox changed.
*/
const UIDVALIDITY_CHANGED = 2;
}
@@ -1,393 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Fetch query object for use with Horde_Imap_Client_Base#fetch().
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Fetch_Query implements ArrayAccess, Countable, Iterator
{
/**
* Internal data array.
*
* @var array
*/
protected $_data = array();
/**
* Get the full text of the message.
*
* @param array $opts The following options are available:
* - length: (integer) The length of the substring to return.
* DEFAULT: The entire text is returned.
* - peek: (boolean) If set, does not set the '\Seen' flag on the
* message.
* DEFAULT: The seen flag is set.
* - start: (integer) If a portion of the full text is desired to be
* returned, the starting position is identified here.
* DEFAULT: The entire text is returned.
*/
public function fullText(array $opts = array())
{
$this->_data[Horde_Imap_Client::FETCH_FULLMSG] = $opts;
}
/**
* Return header text.
*
* Header text is defined only for the base RFC 2822 message or
* message/rfc822 parts.
*
* @param array $opts The following options are available:
* - id: (string) The MIME ID to obtain the header text for.
* DEFAULT: The header text for the base message will be
* returned.
* - length: (integer) The length of the substring to return.
* DEFAULT: The entire text is returned.
* - peek: (boolean) If set, does not set the '\Seen' flag on the
* message.
* DEFAULT: The seen flag is set.
* - start: (integer) If a portion of the full text is desired to be
* returned, the starting position is identified here.
* DEFAULT: The entire text is returned.
*/
public function headerText(array $opts = array())
{
$id = isset($opts['id'])
? $opts['id']
: 0;
$this->_data[Horde_Imap_Client::FETCH_HEADERTEXT][$id] = $opts;
}
/**
* Return body text.
*
* Body text is defined only for the base RFC 2822 message or
* message/rfc822 parts.
*
* @param array $opts The following options are available:
* - id: (string) The MIME ID to obtain the body text for.
* DEFAULT: The body text for the entire message will be
* returned.
* - length: (integer) The length of the substring to return.
* DEFAULT: The entire text is returned.
* - peek: (boolean) If set, does not set the '\Seen' flag on the
* message.
* DEFAULT: The seen flag is set.
* - start: (integer) If a portion of the full text is desired to be
* returned, the starting position is identified here.
* DEFAULT: The entire text is returned.
*/
public function bodyText(array $opts = array())
{
$id = isset($opts['id'])
? $opts['id']
: 0;
$this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] = $opts;
}
/**
* Return MIME header text.
*
* MIME header text is defined only for non-RFC 2822 messages and
* non-message/rfc822 parts.
*
* @param string $id The MIME ID to obtain the MIME header text for.
* @param array $opts The following options are available:
* - length: (integer) The length of the substring to return.
* DEFAULT: The entire text is returned.
* - peek: (boolean) If set, does not set the '\Seen' flag on the
* message.
* DEFAULT: The seen flag is set.
* - start: (integer) If a portion of the full text is desired to be
* returned, the starting position is identified here.
* DEFAULT: The entire text is returned.
*/
public function mimeHeader($id, array $opts = array())
{
$this->_data[Horde_Imap_Client::FETCH_MIMEHEADER][$id] = $opts;
}
/**
* Return the body part data for a MIME ID.
*
* @param string $id The MIME ID to obtain the body part text for.
* @param array $opts The following options are available:
* - decode: (boolean) Attempt to server-side decode the bodypart data
* if it is MIME transfer encoded.
* DEFAULT: false
* - length: (integer) The length of the substring to return.
* DEFAULT: The entire text is returned.
* - peek: (boolean) If set, does not set the '\Seen' flag on the
* message.
* DEFAULT: The seen flag is set.
* - start: (integer) If a portion of the full text is desired to be
* returned, the starting position is identified here.
* DEFAULT: The entire text is returned.
*/
public function bodyPart($id, array $opts = array())
{
$this->_data[Horde_Imap_Client::FETCH_BODYPART][$id] = $opts;
}
/**
* Returns the decoded body part size for a MIME ID.
*
* @param string $id The MIME ID to obtain the decoded body part size
* for.
*/
public function bodyPartSize($id)
{
$this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id] = true;
}
/**
* Returns RFC 2822 header text that matches a search string.
*
* This header search work only with the base RFC 2822 message or
* message/rfc822 parts.
*
* @param string $label A unique label associated with this particular
* search. This is how the results are stored.
* @param array $search The search string(s) (case-insensitive).
* @param array $opts The following options are available:
* - cache: (boolean) If true, and 'peek' is also true, will cache
* the result of this call.
* DEFAULT: false
* - id: (string) The MIME ID to search.
* DEFAULT: The base message part
* - length: (integer) The length of the substring to return.
* DEFAULT: The entire text is returned.
* - notsearch: (boolean) Do a 'NOT' search on the headers.
* DEFAULT: false
* - peek: (boolean) If set, does not set the '\Seen' flag on the
* message.
* DEFAULT: The seen flag is set.
* - start: (integer) If a portion of the full text is desired to be
* returned, the starting position is identified here.
* DEFAULT: The entire text is returned.
*/
public function headers($label, $search, array $opts = array())
{
$this->_data[Horde_Imap_Client::FETCH_HEADERS][$label] = array_merge(
$opts,
array(
'headers' => array_map('strval', $search)
)
);
}
/**
* Return MIME structure information.
*/
public function structure()
{
$this->_data[Horde_Imap_Client::FETCH_STRUCTURE] = true;
}
/**
* Return envelope header data.
*/
public function envelope()
{
$this->_data[Horde_Imap_Client::FETCH_ENVELOPE] = true;
}
/**
* Return flags set for the message.
*/
public function flags()
{
$this->_data[Horde_Imap_Client::FETCH_FLAGS] = true;
}
/**
* Return the internal (IMAP) date of the message.
*/
public function imapDate()
{
$this->_data[Horde_Imap_Client::FETCH_IMAPDATE] = true;
}
/**
* Return the size (in bytes) of the message.
*/
public function size()
{
$this->_data[Horde_Imap_Client::FETCH_SIZE] = true;
}
/**
* Return the unique ID of the message.
*/
public function uid()
{
$this->_data[Horde_Imap_Client::FETCH_UID] = true;
}
/**
* Return the sequence number of the message.
*/
public function seq()
{
$this->_data[Horde_Imap_Client::FETCH_SEQ] = true;
}
/**
* Return the mod-sequence value for the message.
*
* The server must support the CONDSTORE IMAP extension, and the mailbox
* must support mod-sequences.
*/
public function modseq()
{
$this->_data[Horde_Imap_Client::FETCH_MODSEQ] = true;
}
/**
* Does the query contain the given criteria?
*
* @param integer $criteria The criteria to remove.
*
* @return boolean True if the query contains the given criteria.
*/
public function contains($criteria)
{
return isset($this->_data[$criteria]);
}
/**
* Remove an entry under a given criteria.
*
* @param integer $criteria Criteria ID.
* @param string $key The key to remove.
*/
public function remove($criteria, $key)
{
if (isset($this->_data[$criteria]) &&
is_array($this->_data[$criteria])) {
unset($this->_data[$criteria][$key]);
if (empty($this->_data[$criteria])) {
unset($this->_data[$criteria]);
}
}
}
/**
* Returns a hash of the current query object.
*
* @return string Hash.
*/
public function hash()
{
return hash('md5', serialize($this));
}
/* ArrayAccess methods. */
/**
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->_data[$offset]);
}
/**
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return isset($this->_data[$offset])
? $this->_data[$offset]
: null;
}
/**
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->_data[$offset] = $value;
}
/**
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->_data[$offset]);
}
/* Countable methods. */
/**
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->_data);
}
/* Iterator methods. */
/**
*/
#[ReturnTypeWillChange]
public function current()
{
$opts = current($this->_data);
return (!empty($opts) && ($this->key() == Horde_Imap_Client::FETCH_BODYPARTSIZE))
? array_keys($opts)
: $opts;
}
/**
*/
#[ReturnTypeWillChange]
public function key()
{
return key($this->_data);
}
/**
*/
#[ReturnTypeWillChange]
public function next()
{
next($this->_data);
}
/**
*/
#[ReturnTypeWillChange]
public function rewind()
{
reset($this->_data);
}
/**
*/
#[ReturnTypeWillChange]
public function valid()
{
return !is_null($this->key());
}
}
@@ -1,188 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Fetch results object for use with Horde_Imap_Client_Base#fetch().
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*
* @property-read integer $key_type The key type (sequence or UID).
*/
class Horde_Imap_Client_Fetch_Results
implements ArrayAccess, Countable, IteratorAggregate
{
/**
* Key type constants.
*/
const SEQUENCE = 1;
const UID = 2;
/**
* Internal data array.
*
* @var array
*/
protected $_data = array();
/**
* Key type.
*
* @var integer
*/
protected $_keyType;
/**
* Class to use when creating a new fetch object.
*
* @var string
*/
protected $_obClass;
/**
* Constructor.
*
* @param string $ob_class Class to use when creating a new fetch
* object.
* @param integer $key_type Key type.
*/
public function __construct($ob_class = 'Horde_Imap_Client_Data_Fetch',
$key_type = self::UID)
{
$this->_obClass = $ob_class;
$this->_keyType = $key_type;
}
/**
*/
public function __get($name)
{
switch ($name) {
case 'key_type':
return $this->_keyType;
}
}
/**
* Return a fetch object, creating and storing an empty object in the
* results set if it doesn't currently exist.
*
* @param string $key The key to retrieve.
*
* @return Horde_Imap_Client_Data_Fetch The fetch object.
*/
public function get($key)
{
if (!isset($this->_data[$key])) {
$this->_data[$key] = new $this->_obClass();
}
return $this->_data[$key];
}
/**
* Return the list of IDs.
*
* @return array ID list.
*/
public function ids()
{
ksort($this->_data);
return array_keys($this->_data);
}
/**
* Return the first fetch object in the results, if there is only one
* object.
*
* @return null|Horde_Imap_Client_Data_Fetch The fetch object if there is
* only one object, or null.
*/
public function first()
{
return (count($this->_data) === 1)
? reset($this->_data)
: null;
}
/**
* Clears all fetch results.
*
* @since 2.6.0
*/
public function clear()
{
$this->_data = array();
}
/* ArrayAccess methods. */
/**
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->_data[$offset]);
}
/**
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return isset($this->_data[$offset])
? $this->_data[$offset]
: null;
}
/**
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->_data[$offset] = $value;
}
/**
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->_data[$offset]);
}
/* Countable methods. */
/**
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->_data);
}
/* IteratorAggregate methods. */
/**
*/
#[ReturnTypeWillChange]
public function getIterator()
{
ksort($this->_data);
return new ArrayIterator($this->_data);
}
}
@@ -1,511 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* An object that provides a way to identify a list of IMAP indices.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*
* @property-read boolean $all Does this represent an ALL message set?
* @property-read array $ids The list of IDs.
* @property-read boolean $largest Does this represent the largest ID in use?
* @property-read string $max The largest ID (@since 2.20.0).
* @property-read string $min The smallest ID (@since 2.20.0).
* @property-read string $range_string Generates a range string consisting of
* all messages between begin and end of
* ID list.
* @property-read boolean $search_res Does this represent a search result?
* @property-read boolean $sequence Are these sequence IDs? If false, these
* are UIDs.
* @property-read boolean $special True if this is a "special" ID
* representation.
* @property-read string $tostring Return the non-sorted string
* representation.
* @property-read string $tostring_sort Return the sorted string
* representation.
*/
class Horde_Imap_Client_Ids implements Countable, Iterator, Serializable
{
/**
* "Special" representation constants.
*/
const ALL = "\01";
const SEARCH_RES = "\02";
const LARGEST = "\03";
/**
* Allow duplicate IDs?
*
* @var boolean
*/
public $duplicates = false;
/**
* List of IDs.
*
* @var mixed
*/
protected $_ids = array();
/**
* Are IDs message sequence numbers?
*
* @var boolean
*/
protected $_sequence = false;
/**
* Are IDs sorted?
*
* @var boolean
*/
protected $_sorted = false;
/**
* Constructor.
*
* @param mixed $ids See self::add().
* @param boolean $sequence Are $ids message sequence numbers?
*/
public function __construct($ids = null, $sequence = false)
{
$this->add($ids);
$this->_sequence = $sequence;
}
/**
*/
public function __get($name)
{
switch ($name) {
case 'all':
return ($this->_ids === self::ALL);
case 'ids':
return is_array($this->_ids)
? $this->_ids
: array();
case 'largest':
return ($this->_ids === self::LARGEST);
case 'max':
$this->sort();
return end($this->_ids);
case 'min':
$this->sort();
return reset($this->_ids);
case 'range_string':
if (!count($this)) {
return '';
}
$min = $this->min;
$max = $this->max;
return ($min == $max)
? $min
: $min . ':' . $max;
case 'search_res':
return ($this->_ids === self::SEARCH_RES);
case 'sequence':
return (bool)$this->_sequence;
case 'special':
return is_string($this->_ids);
case 'tostring':
case 'tostring_sort':
if ($this->all) {
return '1:*';
} elseif ($this->largest) {
return '*';
} elseif ($this->search_res) {
return '$';
}
return strval($this->_toSequenceString($name == 'tostring_sort'));
}
}
/**
*/
public function __toString()
{
return $this->tostring;
}
/**
* Add IDs to the current object.
*
* @param mixed $ids Either self::ALL, self::SEARCH_RES, self::LARGEST,
* Horde_Imap_Client_Ids object, array, or sequence
* string.
*/
public function add($ids)
{
if (!is_null($ids)) {
if (is_string($ids) &&
in_array($ids, array(self::ALL, self::SEARCH_RES, self::LARGEST))) {
$this->_ids = $ids;
} elseif ($add = $this->_resolveIds($ids)) {
if (is_array($this->_ids) && !empty($this->_ids)) {
foreach ($add as $val) {
$this->_ids[] = $val;
}
} else {
$this->_ids = $add;
}
if (!$this->duplicates) {
$this->_ids = (count($this->_ids) > 25000)
? array_unique($this->_ids)
: array_keys(array_flip($this->_ids));
}
}
$this->_sorted = is_array($this->_ids) && (count($this->_ids) === 1);
}
}
/**
* Removed IDs from the current object.
*
* @since 2.17.0
*
* @param mixed $ids Either Horde_Imap_Client_Ids object, array, or
* sequence string.
*/
public function remove($ids)
{
if (!$this->isEmpty() &&
($remove = $this->_resolveIds($ids))) {
$this->_ids = array_diff($this->_ids, array_unique($remove));
}
}
/**
* Is this object empty (i.e. does not contain IDs)?
*
* @return boolean True if object is empty.
*/
public function isEmpty()
{
return (is_array($this->_ids) && !count($this->_ids));
}
/**
* Reverses the order of the IDs.
*/
public function reverse()
{
if (is_array($this->_ids)) {
$this->_ids = array_reverse($this->_ids);
}
}
/**
* Sorts the IDs.
*/
public function sort()
{
if (!$this->_sorted && is_array($this->_ids)) {
$this->_sort($this->_ids);
$this->_sorted = true;
}
}
/**
* Sorts the IDs numerically.
*
* @param array $ids The array list.
*/
protected function _sort(&$ids)
{
sort($ids, SORT_NUMERIC);
}
/**
* Split the sequence string at an approximate length.
*
* @since 2.7.0
*
* @param integer $length Length to split.
*
* @return array A list containing individual sequence strings.
*/
public function split($length)
{
$id = new Horde_Stream_Temp();
$id->add($this->tostring_sort, true);
$out = array();
do {
$out[] = $id->substring(0, $length) . $id->getToChar(',');
} while (!$id->eof());
return $out;
}
/**
* Resolve the $ids input to add() and remove().
*
* @param mixed $ids Either Horde_Imap_Client_Ids object, array, or
* sequence string.
*
* @return array An array of IDs.
*/
protected function _resolveIds($ids)
{
if ($ids instanceof Horde_Imap_Client_Ids) {
return $ids->ids;
} elseif (is_array($ids)) {
return $ids;
} elseif (is_string($ids) || is_integer($ids)) {
return is_numeric($ids)
? array($ids)
: $this->_fromSequenceString($ids);
}
return array();
}
/**
* Create an IMAP message sequence string from a list of indices.
*
* Index Format: range_start:range_end,uid,uid2,...
*
* @param boolean $sort Numerically sort the IDs before creating the
* range?
*
* @return string The IMAP message sequence string.
*/
protected function _toSequenceString($sort = true)
{
if (empty($this->_ids)) {
return '';
}
$in = $this->_ids;
if ($sort && !$this->_sorted) {
$this->_sort($in);
}
$first = $last = array_shift($in);
$i = count($in) - 1;
$out = array();
foreach ($in as $key => $val) {
if (($last + 1) == $val) {
$last = $val;
}
if (($i == $key) || ($last != $val)) {
if ($last == $first) {
$out[] = $first;
if ($i == $key) {
$out[] = $val;
}
} else {
$out[] = $first . ':' . $last;
if (($i == $key) && ($last != $val)) {
$out[] = $val;
}
}
$first = $last = $val;
}
}
return empty($out)
? $first
: implode(',', $out);
}
/**
* Parse an IMAP message sequence string into a list of indices.
*
* @see _toSequenceString()
*
* @param string $str The IMAP message sequence string.
*
* @return array An array of indices.
*/
protected function _fromSequenceString($str)
{
$ids = array();
$str = trim($str);
if (!strlen($str)) {
return $ids;
}
$idarray = explode(',', $str);
foreach ($idarray as $val) {
$range = explode(':', $val);
if (isset($range[1])) {
for ($i = min($range), $j = max($range); $i <= $j; ++$i) {
$ids[] = $i;
}
} else {
$ids[] = $val;
}
}
return $ids;
}
/* Countable methods. */
/**
*/
#[ReturnTypeWillChange]
public function count()
{
return is_array($this->_ids)
? count($this->_ids)
: 0;
}
/* Iterator methods. */
/**
*/
#[ReturnTypeWillChange]
public function current()
{
return is_array($this->_ids)
? current($this->_ids)
: null;
}
/**
*/
#[ReturnTypeWillChange]
public function key()
{
return is_array($this->_ids)
? key($this->_ids)
: null;
}
/**
*/
#[ReturnTypeWillChange]
public function next()
{
if (is_array($this->_ids)) {
next($this->_ids);
}
}
/**
*/
#[ReturnTypeWillChange]
public function rewind()
{
if (is_array($this->_ids)) {
reset($this->_ids);
}
}
/**
*/
#[ReturnTypeWillChange]
public function valid()
{
return !is_null($this->key());
}
public function serialize()
{
return serialize($this->__serialize());
}
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version change.');
}
$this->__unserialize($data);
}
/**
*/
public function __serialize()
{
$save = array();
if ($this->duplicates) {
$save['d'] = 1;
}
if ($this->_sequence) {
$save['s'] = 1;
}
if ($this->_sorted) {
$save['is'] = 1;
}
switch ($this->_ids) {
case self::ALL:
$save['a'] = true;
break;
case self::LARGEST:
$save['l'] = true;
break;
case self::SEARCH_RES:
$save['sr'] = true;
break;
default:
$save['i'] = strval($this);
break;
}
return $save;
}
/**
*/
public function __unserialize($data)
{
$this->duplicates = !empty($data['d']);
$this->_sequence = !empty($data['s']);
$this->_sorted = !empty($data['is']);
if (isset($data['a'])) {
$this->_ids = self::ALL;
} elseif (isset($data['l'])) {
$this->_ids = self::LARGEST;
} elseif (isset($data['sr'])) {
$this->_ids = self::SEARCH_RES;
} elseif (isset($data['i'])) {
$this->add($data['i']);
}
}
}
@@ -1,256 +0,0 @@
<?php
/**
* Copyright 2012-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* An object implementing lookups between UIDs and message sequence numbers.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2012-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
* @since 2.1.0
*
* @property-read array $map The raw ID mapping data.
* @property-read Horde_Imap_Client_Ids $seq The sorted sequence values.
* @property-read Horde_Imap_Client_Ids $uids The sorted UIDs.
*/
class Horde_Imap_Client_Ids_Map implements Countable, IteratorAggregate, Serializable
{
/**
* Sequence -> UID mapping.
*
* @var array
*/
protected $_ids = array();
/**
* Is the array sorted?
*
* @var boolean
*/
protected $_sorted = true;
/**
* Constructor.
*
* @param array $ids Array of sequence -> UID mapping.
*/
public function __construct(array $ids = array())
{
$this->update($ids);
}
/**
*/
public function __get($name)
{
switch ($name) {
case 'map':
return $this->_ids;
case 'seq':
$this->sort();
return new Horde_Imap_Client_Ids(array_keys($this->_ids), true);
case 'uids':
$this->sort();
return new Horde_Imap_Client_Ids($this->_ids);
}
}
/**
* Updates the mapping.
*
* @param array $ids Array of sequence -> UID mapping.
*
* @return boolean True if the mapping changed.
*/
public function update($ids)
{
if (empty($ids)) {
return false;
} elseif (empty($this->_ids)) {
$this->_ids = $ids;
$change = true;
} else {
$change = false;
foreach ($ids as $k => $v) {
if (!isset($this->_ids[$k]) || ($this->_ids[$k] != $v)) {
$this->_ids[$k] = $v;
$change = true;
}
}
}
if ($change) {
$this->_sorted = false;
}
return $change;
}
/**
* Create a Sequence <-> UID lookup table.
*
* @param Horde_Imap_Client_Ids $ids IDs to lookup.
*
* @return array Keys are sequence numbers, values are UIDs.
*/
public function lookup(Horde_Imap_Client_Ids $ids)
{
if ($ids->all) {
return $this->_ids;
} elseif ($ids->sequence) {
return array_intersect_key($this->_ids, array_flip($ids->ids));
}
return array_intersect($this->_ids, $ids->ids);
}
/**
* Removes messages from the ID mapping.
*
* @param Horde_Imap_Client_Ids $ids IDs to remove.
*/
public function remove(Horde_Imap_Client_Ids $ids)
{
/* For sequence numbers, we need to reindex anytime we have an index
* that appears equal to or after a previously seen index. If an IMAP
* server is smart, it will expunge in reverse order instead. */
if ($ids->sequence) {
$remove = $ids->ids;
} else {
$ids->sort();
$remove = array_reverse(array_keys($this->lookup($ids)));
}
if (empty($remove)) {
return;
}
$this->sort();
if (count($remove) == count($this->_ids) &&
!array_diff($remove, array_keys($this->_ids))) {
$this->_ids = array();
return;
}
/* Find the minimum sequence number to remove. We know entries before
* this are untouched so no need to process them multiple times. */
$first = min($remove);
$edit = $newids = array();
foreach (array_keys($this->_ids) as $i => $seq) {
if ($seq >= $first) {
$i += (($seq == $first) ? 0 : 1);
$newids = array_slice($this->_ids, 0, $i, true);
$edit = array_slice($this->_ids, $i + (($seq == $first) ? 0 : 1), null, true);
break;
}
}
if (!empty($edit)) {
foreach ($remove as $val) {
$found = false;
$tmp = array();
foreach (array_keys($edit) as $i => $seq) {
if ($found) {
$tmp[$seq - 1] = $edit[$seq];
} elseif ($seq >= $val) {
$tmp = array_slice($edit, 0, ($seq == $val) ? $i : $i + 1, true);
$found = true;
}
}
$edit = $tmp;
}
}
$this->_ids = $newids + $edit;
}
/**
* Sort the map.
*/
public function sort()
{
if (!$this->_sorted) {
ksort($this->_ids, SORT_NUMERIC);
$this->_sorted = true;
}
}
/* Countable methods. */
/**
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->_ids);
}
/* IteratorAggregate method. */
/**
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->_ids);
}
/* Serializable methods. */
public function serialize()
{
return serialize($this->__serialize());
}
public function unserialize($data)
{
$data = @unserialize($data);
if (!is_array($data)) {
throw new Exception('Cache version change.');
}
$this->__unserialize($data);
}
/**
*/
public function __serialize()
{
/* Sort before storing; provides more compressible representation. */
$this->sort();
return [
strval(new Horde_Imap_Client_Ids(array_keys($this->_ids))),
strval(new Horde_Imap_Client_Ids(array_values($this->_ids)))
];
}
/**
*/
public function __unserialize($data)
{
$keys = new Horde_Imap_Client_Ids($data[0]);
$vals = new Horde_Imap_Client_Ids($data[1]);
$this->_ids = array_combine($keys->ids, $vals->ids);
/* Guaranteed to be sorted if unserializing. */
$this->_sorted = true;
}
}
@@ -1,64 +0,0 @@
<?php
/**
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
/**
* Wrapper around Ids object that correctly handles POP3 UID strings.
*
* @author Michael Slusarz <[email protected]>
* @category Horde
* @copyright 2011-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Imap_Client
*/
class Horde_Imap_Client_Ids_Pop3 extends Horde_Imap_Client_Ids
{
/**
*/
protected function _sort(&$ids)
{
/* There is no guarantee of POP3 UIDL order - IDs need to be unique,
* but there is no requirement they need be incrementing. RFC
* 1939[7] */
}
/**
* Create a POP3 message sequence string.
*
* Index Format: UID1[SPACE]UID2...
*
* @param boolean $sort Not used in this class.
*
* @return string The POP3 message sequence string.
*/
protected function _toSequenceString($sort = true)
{
/* $sort is ignored - see _sort(). */
/* Use space as delimiter as it is the only printable ASCII character
* that is not allowed as part of the UID (RFC 1939 [7]). */
return implode(' ', count($this->_ids) > 25000 ? array_unique($this->_ids) : array_keys(array_flip($this->_ids)));
}
/**
* Parse a POP3 message sequence string into a list of indices.
*
* @param string $str The POP3 message sequence string.
*
* @return array An array of UIDs.
*/
protected function _fromSequenceString($str)
{
return explode(' ', trim($str));
}
}

Some files were not shown because too many files have changed in this diff Show More