From 59c1392b2cb9ab8856e32d0ce1ef864f53025b35 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 25 Mar 2021 16:26:26 +0100 Subject: [PATCH 01/90] Added feature #67 --- dev/Styles/MessageList.less | 12 ++++++++---- .../app/templates/Views/User/MailMessageList.html | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dev/Styles/MessageList.less b/dev/Styles/MessageList.less index f7021d2ff..2af0a7e51 100644 --- a/dev/Styles/MessageList.less +++ b/dev/Styles/MessageList.less @@ -254,9 +254,9 @@ html.rl-no-preview-pane { padding: 0 10px 0 5px; } - time { + time, .sizeParent { margin: 0 5px; - color: #999; + opacity: 0.6; font-size: 11px; white-space: nowrap; } @@ -472,7 +472,7 @@ html .messageList .line-loading { line-height: 16px; margin-left: 30px; order: 1; - width: calc(100% - 60px); + width: calc(100% - 120px); } .senderParent { @@ -483,9 +483,13 @@ html .messageList .line-loading { order: 1; } - .attachmentParent { + .sizeParent { order: 2; } + + .attachmentParent { + order: 3; + } } } diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index b00eab42d..62616d7fc 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -177,6 +177,8 @@ +
+
From fd5940f5fcae2e7018f0c0d5ffa4fef4652822ce Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 25 Mar 2021 16:51:54 +0100 Subject: [PATCH 02/90] Bugfix: missing space between number and 'Bytes' --- dev/Common/File.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/Common/File.js b/dev/Common/File.js index 70622c862..63a9a8d31 100644 --- a/dev/Common/File.js +++ b/dev/Common/File.js @@ -297,7 +297,7 @@ export const FileInfo = { friendlySize: bytes => { bytes = parseInt(bytes, 10) || 0; let i = Math.floor(Math.log(bytes) / Math.log(1024)); - return (bytes / Math.pow(1024, i)).toFixed(2>i ? 0 : 1) + sizes[i]; + return (bytes / Math.pow(1024, i)).toFixed(2>i ? 0 : 1) + ' ' + sizes[i]; } }; From 883bf6b026c1c7874518b6a52d0eb8b46005bb5b Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 26 Mar 2021 15:07:14 +0100 Subject: [PATCH 03/90] Start https://github.com/the-djmaze/snappymail/issues/67#issuecomment-806948346 --- dev/App/User.js | 14 +++-- dev/Common/EnumsUser.js | 16 +++++ dev/Model/FolderCollection.js | 1 + dev/Remote/User/Fetch.js | 60 ++++++++----------- dev/Stores/User/Folder.js | 10 +++- .../app/libraries/MailSo/Imap/ImapClient.php | 8 +++ .../MailSo/Mail/FolderCollection.php | 7 +++ .../app/libraries/MailSo/Mail/MailClient.php | 60 +++++++++++++++---- .../libraries/RainLoop/Actions/Messages.php | 21 ++++--- 9 files changed, 135 insertions(+), 62 deletions(-) diff --git a/dev/App/User.js b/dev/App/User.js index becf923ff..36f7f2836 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -187,16 +187,18 @@ class AppUser extends AbstractApp { } MessageUserStore.listLoading(false); }, - FolderUserStore.currentFolderFullNameRaw(), - iOffset, - SettingsUserStore.messagesPerPage(), - MessageUserStore.listSearch(), - MessageUserStore.listThreadUid() + { + Folder: FolderUserStore.currentFolderFullNameRaw(), + Offset: iOffset, + Limit: SettingsUserStore.messagesPerPage(), + Search: MessageUserStore.listSearch(), + UidNext: MessageUserStore.listThreadUid() + } ); } recacheInboxMessageList() { - Remote.messageList(()=>{}, getFolderInboxName(), 0, SettingsUserStore.messagesPerPage(), '', '', true); + Remote.messageList(null, {Folder: getFolderInboxName()}, true); } /** diff --git a/dev/Common/EnumsUser.js b/dev/Common/EnumsUser.js index 19d382c70..12514e334 100644 --- a/dev/Common/EnumsUser.js +++ b/dev/Common/EnumsUser.js @@ -14,6 +14,22 @@ export const FolderType = { User: 99 }; +/** + * @enum {string} + */ +export const FolderSortType = { + DateDesc: '', // default 'REVERSE DATE' + DateAsc: 'DATE', + FromDesc: 'REVERSE FROM', + FromAsc: 'FROM', + SizeDesc: 'REVERSE SIZE', + SizeAsc: 'SIZE', + SubjectDesc: 'REVERSE SUBJECT', + SubjectAsc: 'SUBJECT' +// ToDesc: 'REVERSE TO', +// ToAsc: 'TO', +}; + /** * @enum {string} */ diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index 017fa37a9..cc172d464 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -113,6 +113,7 @@ export class FolderCollectionModel extends AbstractCollectionModel AppUserStore.threadsAllowed(!!(Settings.app('useImapThread') && this.IsThreadsSupported)); FolderUserStore.folderListOptimized(!!this.Optimized); + FolderUserStore.sortSupported(!!this.IsSortSupported); let update = false; diff --git a/dev/Remote/User/Fetch.js b/dev/Remote/User/Fetch.js index 182f15a23..64bf5704e 100644 --- a/dev/Remote/User/Fetch.js +++ b/dev/Remote/User/Fetch.js @@ -297,54 +297,44 @@ class RemoteUserFetch extends AbstractFetchRemote { /** * @param {Function} fCallback - * @param {string} sFolderFullNameRaw - * @param {number=} iOffset = 0 - * @param {number=} iLimit = 20 - * @param {string=} sSearch = '' - * @param {string=} sThreadUid = '' + * @param {object} params * @param {boolean=} bSilent = false */ - messageList(fCallback, sFolderFullNameRaw, iOffset = 0, iLimit = 20, sSearch = '', sThreadUid = '', bSilent = false) { - sFolderFullNameRaw = pString(sFolderFullNameRaw); - - const folderHash = getFolderHash(sFolderFullNameRaw), - useThreads = AppUserStore.threadsAllowed() && SettingsUserStore.useThreads(), + messageList(fCallback, params, bSilent = false) { + const + sFolderFullNameRaw = pString(params.Folder), + folderHash = getFolderHash(sFolderFullNameRaw), + useThreads = AppUserStore.threadsAllowed() && SettingsUserStore.useThreads() ? 1 : 0, inboxUidNext = getFolderInboxName() === sFolderFullNameRaw ? getFolderUidNext(sFolderFullNameRaw) : ''; - let params = {}, sGetAdd = ''; + params.Folder = sFolderFullNameRaw; + params.ThreadUid = useThreads ? params.ThreadUid : ''; + params = Object.assign({ + Folder: '', + Offset: 0, + Limit: SettingsUserStore.messagesPerPage(), + Search: '', + UidNext: inboxUidNext, + UseThreads: useThreads, + ThreadUid: '', + Sort: FolderUserStore.sortMode() + }, params); - if (folderHash && (!sSearch || !sSearch.includes('is:'))) { + let sGetAdd = ''; + + if (folderHash && (!params.Search || !params.Search.includes('is:'))) { sGetAdd = 'MessageList/' + SUB_QUERY_PREFIX + '/' + - urlsafeArray([ - sFolderFullNameRaw, - iOffset, - iLimit, - sSearch, - SettingsGet('ProjectHash'), - folderHash, - inboxUidNext, - useThreads ? 1 : 0, - useThreads ? sThreadUid : '' - ]); - } else { - params = { - Folder: sFolderFullNameRaw, - Offset: iOffset, - Limit: iLimit, - Search: sSearch, - UidNext: inboxUidNext, - UseThreads: useThreads ? 1 : 0, - ThreadUid: useThreads ? sThreadUid : '' - }; + urlsafeArray([SettingsGet('ProjectHash'),folderHash].concat(Object.values(params))); + params = {}; } this.defaultRequest( fCallback, 'MessageList', params, - sSearch ? 300000 : 30000, + 30000, sGetAdd, bSilent ? [] : ['MessageList'] ); @@ -562,7 +552,7 @@ class RemoteUserFetch extends AbstractFetchRemote { * @param {Object} oData */ sendMessage(fCallback, oData) { - this.defaultRequest(fCallback, 'SendMessage', oData, 300000); + this.defaultRequest(fCallback, 'SendMessage', oData, 30000); } /** diff --git a/dev/Stores/User/Folder.js b/dev/Stores/User/Folder.js index 0cc19c389..f8ab9b6a5 100644 --- a/dev/Stores/User/Folder.js +++ b/dev/Stores/User/Folder.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { FolderType } from 'Common/EnumsUser'; +import { FolderType, FolderSortType } from 'Common/EnumsUser'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { addObservablesTo, addSubscribablesTo } from 'Common/Utils'; import { folderListOptionsBuilder } from 'Common/UtilsUser'; @@ -19,6 +19,12 @@ export const FolderUserStore = new class { */ displaySpecSetting: false, + /** + * If the IMAP server supports SORT + */ + sortSupported: false, +// sortMode: '', + sentFolder: '', draftFolder: '', spamFolder: '', @@ -36,6 +42,8 @@ export const FolderUserStore = new class { foldersInboxUnreadCount: 0 }); + this.sortMode = ko.observable('').extend({ limitedList: Object.values(FolderSortType) }); + this.namespace = ''; this.folderList = ko.observableArray(); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 9ea88e869..e6d243c6e 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -293,6 +293,13 @@ class ImapClient extends \MailSo\Net\NetClient } /** + * Test support for things like: + * IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY + * THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND + * URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED + * I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH + * LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY STATUS=SIZE LITERAL+ NOTIFY SPECIAL-USE + * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ @@ -596,6 +603,7 @@ class ImapClient extends \MailSo\Net\NetClient } /** + * See https://tools.ietf.org/html/rfc5256 * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php index 8db19093f..a6e0a1044 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/FolderCollection.php @@ -32,6 +32,11 @@ class FolderCollection extends \MailSo\Base\Collection */ public $IsThreadsSupported; + /** + * @var bool + */ + public $IsSortSupported; + /** * @var bool */ @@ -50,6 +55,7 @@ class FolderCollection extends \MailSo\Base\Collection $this->FoldersHash = ''; $this->SystemFolders = array(); $this->IsThreadsSupported = false; + $this->IsSortSupported = false; $this->Optimized = false; } @@ -239,6 +245,7 @@ class FolderCollection extends \MailSo\Base\Collection 'Namespace' => $this->GetNamespace(), 'FoldersHash' => $this->FoldersHash ?: '', 'IsThreadsSupported' => $this->IsThreadsSupported, + 'IsSortSupported' => $this->IsSortSupported, 'Optimized' => $this->Optimized, 'CountRec' => $this->CountRec(), 'SystemFolders' => isset($this->SystemFolders) && \is_array($this->SystemFolders) ? diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index e7bad6d30..12f25fa8e 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -1473,8 +1473,47 @@ class MailClient * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ - public function GetUids(?\MailSo\Cache\CacheClient $oCacher, string $sSearch, string $sFilter, string $sFolderName, string $sFolderHash, bool $bUseSortIfSupported = false) : array + public function GetUids(?\MailSo\Cache\CacheClient $oCacher, string $sSearch, + string $sFilter, string $sFolderName, string $sFolderHash, + bool $bUseSortIfSupported = false, string $sSort = '') : array { + /* TODO: Validate $sSort + ARRIVAL + Internal date and time of the message. This differs from the + ON criteria in SEARCH, which uses just the internal date. + + CC + [IMAP] addr-mailbox of the first "cc" address. + + DATE + Sent date and time, as described in section 2.2. + + FROM + [IMAP] addr-mailbox of the first "From" address. + + REVERSE + Followed by another sort criterion, has the effect of that + criterion but in reverse (descending) order. + Note: REVERSE only reverses a single criterion, and does not + affect the implicit "sequence number" sort criterion if all + other criteria are identical. Consequently, a sort of + REVERSE SUBJECT is not the same as a reverse ordering of a + SUBJECT sort. This can be avoided by use of additional + criteria, e.g., SUBJECT DATE vs. REVERSE SUBJECT REVERSE + DATE. In general, however, it's better (and faster, if the + client has a "reverse current ordering" command) to reverse + the results in the client instead of issuing a new SORT. + + SIZE + Size of the message in octets. + + SUBJECT + Base subject text. + + TO + [IMAP] addr-mailbox of the first "To" address. + */ + $aResultUids = false; $bUidsFromCacher = false; $bUseCacheAfterSearch = true; @@ -1482,18 +1521,13 @@ class MailClient $sSerializedHash = ''; $sSerializedLog = ''; - $bUseSortIfSupported = $bUseSortIfSupported ? !!$this->oImapClient->IsSupported('SORT') : false; - - if (0 < \strlen($sSearch)) - { - $bUseSortIfSupported = false; - } + $bUseSortIfSupported = $bUseSortIfSupported && !\strlen($sSearch) && $this->oImapClient->IsSupported('SORT'); $sSearchCriterias = $this->getImapSearchCriterias($sSearch, $sFilter, 0, $bUseCacheAfterSearch); if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited()) { $sSerializedHash = 'GetUids/'. - ($bUseSortIfSupported ? 'S': 'N').'/'. + ($bUseSortIfSupported ? 'S' . $sSort : 'N').'/'. $this->GenerateImapClientHash().'/'. $sFolderName.'/'.$sSearchCriterias; @@ -1522,7 +1556,7 @@ class MailClient if (!\is_array($aResultUids)) { $aResultUids = $bUseSortIfSupported ? - $this->oImapClient->MessageSimpleSort(array('REVERSE DATE'), $sSearchCriterias, true) : + $this->oImapClient->MessageSimpleSort(array($sSort ?: 'REVERSE DATE'), $sSearchCriterias, true) : $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8') ; @@ -1551,7 +1585,7 @@ class MailClient public function MessageList(string $sFolderName, int $iOffset = 0, int $iLimit = 10, string $sSearch = '', string $sPrevUidNext = '', ?\MailSo\Cache\CacheClient $oCacher = null, bool $bUseSortIfSupported = false, bool $bUseThreadSortIfSupported = false, - string $sThreadUid = '', string $sFilter = '') : MessageCollection + string $sThreadUid = '', string $sFilter = '', string $sSort = '') : MessageCollection { $sFilter = \trim($sFilter); $sSearch = \trim($sSearch); @@ -1584,7 +1618,7 @@ class MailClient $sUidNext = '0'; $sHighestModSeq = ''; - $bUseSortIfSupported = $bUseSortIfSupported ? $this->oImapClient->IsSupported('SORT') : false; + $bUseSortIfSupported = $bUseSortIfSupported && $this->oImapClient->IsSupported('SORT'); $bUseThreadSortIfSupported = $bUseThreadSortIfSupported ? ($this->oImapClient->IsSupported('THREAD=REFS') || $this->oImapClient->IsSupported('THREAD=REFERENCES') || $this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT')) : false; @@ -1630,7 +1664,7 @@ class MailClient if (0 < $iMessageRealCount && !$bMessageListOptimization) { $mAllSortedUids = $this->GetUids($oCacher, '', $sFilter, - $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported); + $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported, $sSort); $mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap( $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $mAllSortedUids, $oCacher) : null; @@ -2010,6 +2044,8 @@ class MailClient } $oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported(); + + $oFolderCollection->IsSortSupported = $this->oImapClient->IsSupported('SORT'); } return $oFolderCollection; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index ec894f8e2..8b8f0352e 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -24,16 +24,17 @@ trait Messages $sUidNext = ''; $bUseThreads = false; $sThreadUid = ''; + $sSort = ''; $sRawKey = $this->GetActionParam('RawKey', ''); - $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 9); + $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 10); if ($aValues && 7 < \count($aValues)) { - $sFolder =(string) $aValues[0]; - $iOffset = (int) $aValues[1]; - $iLimit = (int) $aValues[2]; - $sSearch = (string) $aValues[3]; + $sFolder = (string) $aValues[2]; + $iOffset = (int) $aValues[3]; + $iLimit = (int) $aValues[4]; + $sSearch = (string) $aValues[5]; $sUidNext = (string) $aValues[6]; $bUseThreads = (bool) $aValues[7]; @@ -42,6 +43,8 @@ trait Messages $sThreadUid = isset($aValues[8]) ? (string) $aValues[8] : ''; } + $sSort = isset($aValues[9]) ? (string) $aValues[9] : ''; + $this->verifyCacheByKey($sRawKey); } else @@ -50,8 +53,9 @@ trait Messages $iOffset = (int) $this->GetActionParam('Offset', 0); $iLimit = (int) $this->GetActionParam('Limit', 10); $sSearch = $this->GetActionParam('Search', ''); + $sSort = $this->GetActionParam('Sort', ''); $sUidNext = $this->GetActionParam('UidNext', ''); - $bUseThreads = '1' === (string) $this->GetActionParam('UseThreads', '0'); + $bUseThreads = !empty($this->GetActionParam('UseThreads', '0')); if ($bUseThreads) { @@ -76,10 +80,11 @@ trait Messages $oMessageList = $this->MailClient()->MessageList( $sFolder, $iOffset, $iLimit, $sSearch, $sUidNext, $this->cacherForUids(), - !!$this->Config()->Get('labs', 'use_imap_sort', false), + !!$this->Config()->Get('labs', 'use_imap_sort', true), $bUseThreads, $sThreadUid, - '' + '', + $sSort ); } catch (\Throwable $oException) From 864cca6ffd4bda1a2905114e1adf00f028b39d41 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 26 Mar 2021 15:07:46 +0100 Subject: [PATCH 04/90] Dropped PHP73 branch --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 831e34b7a..30bb8d30a 100644 --- a/README.md +++ b/README.md @@ -190,8 +190,3 @@ Still TODO: CKEditor including the 7 asset requests (css,language,plugins,icons) is 633.46 KB / 180.47 KB (gzip). To use the old CKEditor, you must install the plugin. - -### PHP73 branch - -There's a RainLoop 1.14.0 branch with only the PHP 7.3 changes at -https://github.com/the-djmaze/snappymail/tree/php73 From 52dadc46f33602672b614602c46ec8579ef8311a Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 26 Mar 2021 15:58:10 +0100 Subject: [PATCH 05/90] Improve MailSo Imap ResponseException message --- .../MailSo/Imap/Exceptions/InvalidResponseException.php | 2 +- .../MailSo/Imap/Exceptions/LoginBadCredentialsException.php | 2 +- .../MailSo/Imap/Exceptions/LoginBadMethodException.php | 2 +- .../app/libraries/MailSo/Imap/Exceptions/LoginException.php | 2 +- .../MailSo/Imap/Exceptions/NegativeResponseException.php | 2 +- .../libraries/MailSo/Imap/Exceptions/ResponseException.php | 6 ++++-- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/InvalidResponseException.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/InvalidResponseException.php index fa9c82247..1a8b64086 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/InvalidResponseException.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/InvalidResponseException.php @@ -16,4 +16,4 @@ namespace MailSo\Imap\Exceptions; * @package Imap * @subpackage Exceptions */ -class InvalidResponseException extends \MailSo\Imap\Exceptions\ResponseException {} +class InvalidResponseException extends ResponseException {} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadCredentialsException.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadCredentialsException.php index eb440089d..a8680c7ec 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadCredentialsException.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadCredentialsException.php @@ -16,4 +16,4 @@ namespace MailSo\Imap\Exceptions; * @package Imap * @subpackage Exceptions */ -class LoginBadCredentialsException extends \MailSo\Imap\Exceptions\LoginException {} \ No newline at end of file +class LoginBadCredentialsException extends LoginException {} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadMethodException.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadMethodException.php index 358f06dc5..25b89768f 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadMethodException.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginBadMethodException.php @@ -16,4 +16,4 @@ namespace MailSo\Imap\Exceptions; * @package Imap * @subpackage Exceptions */ -class LoginBadMethodException extends \MailSo\Imap\Exceptions\LoginException {} \ No newline at end of file +class LoginBadMethodException extends LoginException {} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginException.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginException.php index 4e5172a29..3c138d51a 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginException.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/LoginException.php @@ -16,4 +16,4 @@ namespace MailSo\Imap\Exceptions; * @package Imap * @subpackage Exceptions */ -class LoginException extends \MailSo\Imap\Exceptions\NegativeResponseException {} +class LoginException extends NegativeResponseException {} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/NegativeResponseException.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/NegativeResponseException.php index 43a277ece..14d8791b7 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/NegativeResponseException.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/NegativeResponseException.php @@ -16,7 +16,7 @@ namespace MailSo\Imap\Exceptions; * @package Imap * @subpackage Exceptions */ -class NegativeResponseException extends \MailSo\Imap\Exceptions\ResponseException +class NegativeResponseException extends ResponseException { public function getAlertFromStatus() : string diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/ResponseException.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/ResponseException.php index a6bb233f7..1eac5eaee 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/ResponseException.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Exceptions/ResponseException.php @@ -25,9 +25,11 @@ class ResponseException extends \MailSo\Imap\Exceptions\Exception public function __construct(?\MailSo\Imap\ResponseCollection $oResponses = null, string $sMessage = '', int $iCode = 0, ?\Throwable $oPrevious = null) { - parent::__construct($sMessage, $iCode, $oPrevious); - $this->oResponses = $oResponses; + if (!$sMessage && $response = $this->GetLastResponse()) { + $sMessage = $response->OptionalResponse[0] . ' ' . $response->HumanReadable; + } + parent::__construct($sMessage, $iCode, $oPrevious); } public function GetResponses() : ?\MailSo\Imap\ResponseCollection From 801e3f8e2baf073cd5c6b093a450323983c3dfd2 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 26 Mar 2021 16:47:28 +0100 Subject: [PATCH 06/90] Resolve Issue #70 --- .../MailSo/Imap/ResponseCollection.php | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php index 43e46c489..372d0bea5 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php @@ -100,16 +100,27 @@ class ResponseCollection extends \MailSo\Base\Collection foreach ($this as $oResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oResponse->ResponseType && - $sStatus === $oResponse->StatusOrIndex && 5 === count($oResponse->ResponseList)) + $sStatus === $oResponse->StatusOrIndex && 5 <= count($oResponse->ResponseList)) { try { - $sFullNameRaw = $oResponse->ResponseList[4]; - if (\is_array($sFullNameRaw)) { - $sFullNameRaw = "[{$sFullNameRaw[0]}]"; + /** + * A bug in the parser converts folder names that start with '[' into arrays, + * and subfolders are in $oResponse->ResponseList[5+] + * https://github.com/the-djmaze/snappymail/issues/1 + * https://github.com/the-djmaze/snappymail/issues/70 + */ + $sFullNameRaw = \array_slice($oResponse->ResponseList, 4); + foreach ($sFullNameRaw as &$name) { + if (\is_array($name)) { + $name = "[{$name[0]}]"; + } else if (!\is_string($name)) { + $name = ''; + } } - if (!\is_string($sFullNameRaw)) { - \error_log('ResponseCollection::getFoldersResult: invalid string ' . \var_export($sFullNameRaw, true)); + $sFullNameRaw = \implode('', $sFullNameRaw); + if (!\strlen($sFullNameRaw)) { + \error_log('ResponseCollection::getFoldersResult: invalid string ' . \var_export($oResponse->ResponseList[4], true)); continue; } $oFolder = new Folder($sFullNameRaw, From 795275334c36721443d4fde64ecc24cc051c6a27 Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 26 Mar 2021 17:04:53 +0100 Subject: [PATCH 07/90] Issue #70 the error_log() is not needed anymore --- .../v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php index 372d0bea5..a0aa4db52 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ResponseCollection.php @@ -119,10 +119,7 @@ class ResponseCollection extends \MailSo\Base\Collection } } $sFullNameRaw = \implode('', $sFullNameRaw); - if (!\strlen($sFullNameRaw)) { - \error_log('ResponseCollection::getFoldersResult: invalid string ' . \var_export($oResponse->ResponseList[4], true)); - continue; - } + $oFolder = new Folder($sFullNameRaw, $oResponse->ResponseList[3], $oResponse->ResponseList[2]); From 7eeed62b09dffab0dc986f0d22e8ac28be833a3e Mon Sep 17 00:00:00 2001 From: djmaze Date: Sat, 27 Mar 2021 00:57:04 +0100 Subject: [PATCH 08/90] Get sorting working for #67 --- dev/Styles/MessageList.less | 5 ++ dev/View/User/MailBox/MessageList.js | 20 ++++++- .../app/localization/webmail/_source.en.yml | 8 +++ .../templates/Views/User/MailMessageList.html | 53 +++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/dev/Styles/MessageList.less b/dev/Styles/MessageList.less index 2af0a7e51..8a73446de 100644 --- a/dev/Styles/MessageList.less +++ b/dev/Styles/MessageList.less @@ -6,6 +6,11 @@ html.rl-no-preview-pane { } } +#sort-list-dropdown-id { + padding-left: 6px; + padding-right: 6px; +} + .messageList { .toolbar { diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index b1b48ba62..3f6048952 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -74,6 +74,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.messageListDisableAutoSelect = MessageUserStore.listDisableAutoSelect; this.folderList = FolderUserStore.folderList; + this.sortSupported = FolderUserStore.sortSupported; this.composeInEdit = AppUserStore.composeInEdit; this.leftPanelDisabled = leftPanelDisabled; @@ -106,6 +107,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.addObservables({ moveDropdownTrigger: false, moreDropdownTrigger: false, + sortDropdownTrigger: false, dragOverArea: null, dragOverBodyArea: null, @@ -174,7 +176,18 @@ export class MessageListMailBoxUserView extends AbstractViewRight { mobileCheckedStateHide: () => ThemeStore.isMobile() ? !MessageUserStore.listChecked().length : true, - messageListFocused: () => Scope.MessageList === AppUserStore.focusedState() + messageListFocused: () => Scope.MessageList === AppUserStore.focusedState(), + + sortText: () => { + let mode = FolderUserStore.sortMode().split(/\s+/); + if ('' === mode[0]) { + return '📅⬇'; + } + return (mode.includes('SIZE') ? '✉' + : (mode.includes('FROM') ? '@' + : (mode.includes('SUBJECT') ? '𝐒' : '📅'))) + + (mode.includes('REVERSE') ? '⬇' : '⬆'); + } }); this.hasCheckedOrSelectedLines = MessageUserStore.hasCheckedOrSelected, @@ -246,6 +259,11 @@ export class MessageListMailBoxUserView extends AbstractViewRight { }); } + changeSort(self, event) { + FolderUserStore.sortMode(event.target.closest('li').dataset.sort); + this.reloadCommand(); + } + clearCommand() { if (Settings.capa(Capa.DangerousActions)) { showScreenPopup(FolderClearPopupView, [FolderUserStore.currentFolder()]); diff --git a/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml b/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml index 11a00960a..db0d833ff 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml @@ -101,6 +101,14 @@ en: YESTERDAY_AT: "yesterday at %TIME%" NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!" QUOTA_SIZE: "Using %SIZE% (%PROC%%) of your %LIMIT%" + SORT_DATE_DESC: "Date descending" + SORT_DATE_ASC: "Date ascending" + SORT_SIZE_DESC: "Size descending" + SORT_SIZE_ASC: "Size ascending" + SORT_SUBJECT_DESC: "Subject descending" + SORT_SUBJECT_ASC: "Subject ascending" + SORT_FROM_DESC: "From descending" + SORT_FROM_ASC: "From ascending" MESSAGE: BUTTON_EDIT: "Edit" BUTTON_UNSUBSCRIBE: "Unsubscribe from this list" diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index 62616d7fc..9f89d752d 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -101,6 +101,59 @@
+
From 64cf19746df6ac7ce499b8e695abfdbf722be888 Mon Sep 17 00:00:00 2001 From: djmaze Date: Sat, 27 Mar 2021 16:02:48 +0100 Subject: [PATCH 09/90] Finish the sort feature --- dev/Common/EnumsUser.js | 2 +- dev/Stores/User/Folder.js | 4 ++-- dev/View/User/MailBox/MessageList.js | 8 +++++--- .../app/localization/webmail/_source.en.yml | 13 +++++++------ .../0.0.0/app/localization/webmail/de_DE.yml | 9 +++++++++ .../0.0.0/app/localization/webmail/en_GB.yml | 9 +++++++++ .../0.0.0/app/localization/webmail/en_US.yml | 9 +++++++++ .../0.0.0/app/localization/webmail/es_ES.yml | 9 +++++++++ .../0.0.0/app/localization/webmail/fr_FR.yml | 9 +++++++++ .../0.0.0/app/localization/webmail/nl_NL.yml | 9 +++++++++ .../0.0.0/app/localization/webmail/zh_CN.yml | 9 +++++++++ .../templates/Views/User/MailMessageList.html | 8 ++++---- vendors/fontastic/fonts/snappymail.woff | Bin 9712 -> 9920 bytes vendors/fontastic/fonts/snappymail.woff2 | Bin 7808 -> 7916 bytes 14 files changed, 82 insertions(+), 16 deletions(-) diff --git a/dev/Common/EnumsUser.js b/dev/Common/EnumsUser.js index 12514e334..9ede882ee 100644 --- a/dev/Common/EnumsUser.js +++ b/dev/Common/EnumsUser.js @@ -17,7 +17,7 @@ export const FolderType = { /** * @enum {string} */ -export const FolderSortType = { +export const FolderSortMode = { DateDesc: '', // default 'REVERSE DATE' DateAsc: 'DATE', FromDesc: 'REVERSE FROM', diff --git a/dev/Stores/User/Folder.js b/dev/Stores/User/Folder.js index f8ab9b6a5..34ed105bf 100644 --- a/dev/Stores/User/Folder.js +++ b/dev/Stores/User/Folder.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { FolderType, FolderSortType } from 'Common/EnumsUser'; +import { FolderType, FolderSortMode } from 'Common/EnumsUser'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { addObservablesTo, addSubscribablesTo } from 'Common/Utils'; import { folderListOptionsBuilder } from 'Common/UtilsUser'; @@ -42,7 +42,7 @@ export const FolderUserStore = new class { foldersInboxUnreadCount: 0 }); - this.sortMode = ko.observable('').extend({ limitedList: Object.values(FolderSortType) }); + this.sortMode = ko.observable('').extend({ limitedList: Object.values(FolderSortMode) }); this.namespace = ''; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 3f6048952..3701cbe93 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -179,13 +179,15 @@ export class MessageListMailBoxUserView extends AbstractViewRight { messageListFocused: () => Scope.MessageList === AppUserStore.focusedState(), sortText: () => { - let mode = FolderUserStore.sortMode().split(/\s+/); - if ('' === mode[0]) { + let mode = FolderUserStore.sortMode(); + if ('' === mode) { return '📅⬇'; } + mode = mode.split(/\s+/); return (mode.includes('SIZE') ? '✉' : (mode.includes('FROM') ? '@' - : (mode.includes('SUBJECT') ? '𝐒' : '📅'))) + : (mode.includes('SUBJECT') ? '𝐒' + : '📅'))) + (mode.includes('REVERSE') ? '⬇' : '⬆'); } }); diff --git a/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml b/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml index db0d833ff..e5e8a3bde 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml @@ -101,14 +101,15 @@ en: YESTERDAY_AT: "yesterday at %TIME%" NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!" QUOTA_SIZE: "Using %SIZE% (%PROC%%) of your %LIMIT%" - SORT_DATE_DESC: "Date descending" + SORT: "Sort" SORT_DATE_ASC: "Date ascending" - SORT_SIZE_DESC: "Size descending" + SORT_DATE_DESC: "Date descending" SORT_SIZE_ASC: "Size ascending" - SORT_SUBJECT_DESC: "Subject descending" - SORT_SUBJECT_ASC: "Subject ascending" - SORT_FROM_DESC: "From descending" - SORT_FROM_ASC: "From ascending" + SORT_SIZE_DESC: "Size descending" + SORT_SUBJECT_ASC: "Subject A-Z" + SORT_SUBJECT_DESC: "Subject Z-A" + SORT_FROM_ASC: "From A-Z" + SORT_FROM_DESC: "From Z-A" MESSAGE: BUTTON_EDIT: "Edit" BUTTON_UNSUBSCRIBE: "Unsubscribe from this list" diff --git a/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml b/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml index 93e0ff611..a042e84b7 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml @@ -101,6 +101,15 @@ de_DE: YESTERDAY_AT: "Gestern um %TIME%" NEW_MESSAGE_NOTIFICATION: "Sie haben %COUNT% neue Nachrichten!" QUOTA_SIZE: "Sie nutzen %SIZE% (%PROC%%) von %LIMIT%." + SORT: "Sortieren" + SORT_DATE_ASC: "Datum aufsteigend" + SORT_DATE_DESC: "Datum absteigend" + SORT_SIZE_ASC: "Größe aufsteigend" + SORT_SIZE_DESC: "Größe absteigend" + SORT_SUBJECT_ASC: "Betreff A-Z" + SORT_SUBJECT_DESC: "Betreff Z-A" + SORT_FROM_ASC: "Von A-Z" + SORT_FROM_DESC: "Von Z-A" MESSAGE: BUTTON_EDIT: "Bearbeiten" BUTTON_UNSUBSCRIBE: "Diese Liste abbestellen" diff --git a/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml b/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml index 1e7f1fc30..840950b7c 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml @@ -101,6 +101,15 @@ en_GB: YESTERDAY_AT: "yesterday at %TIME%" NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!" QUOTA_SIZE: "Using %SIZE% (%PROC%%) of your %LIMIT%" + SORT: "Sort" + SORT_DATE_ASC: "Date ascending" + SORT_DATE_DESC: "Date descending" + SORT_SIZE_ASC: "Size ascending" + SORT_SIZE_DESC: "Size descending" + SORT_SUBJECT_ASC: "Subject A-Z" + SORT_SUBJECT_DESC: "Subject Z-A" + SORT_FROM_ASC: "From A-Z" + SORT_FROM_DESC: "From Z-A" MESSAGE: BUTTON_EDIT: "Edit" BUTTON_UNSUBSCRIBE: "Unsubscribe from this list" diff --git a/snappymail/v/0.0.0/app/localization/webmail/en_US.yml b/snappymail/v/0.0.0/app/localization/webmail/en_US.yml index 6b58bec90..b1bedc535 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/en_US.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/en_US.yml @@ -101,6 +101,15 @@ en_US: YESTERDAY_AT: "yesterday at %TIME%" NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!" QUOTA_SIZE: "Using %SIZE% (%PROC%%) of your %LIMIT%" + SORT: "Sort" + SORT_DATE_ASC: "Date ascending" + SORT_DATE_DESC: "Date descending" + SORT_SIZE_ASC: "Size ascending" + SORT_SIZE_DESC: "Size descending" + SORT_SUBJECT_ASC: "Subject A-Z" + SORT_SUBJECT_DESC: "Subject Z-A" + SORT_FROM_ASC: "From A-Z" + SORT_FROM_DESC: "From Z-A" MESSAGE: BUTTON_EDIT: "Edit" BUTTON_UNSUBSCRIBE: "Unsubscribe from this list" diff --git a/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml b/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml index 17b2bb532..8b0d43ede 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml @@ -101,6 +101,15 @@ es_ES: YESTERDAY_AT: "ayer a las %TIME%" NEW_MESSAGE_NOTIFICATION: "Tiene %COUNT% nuevos mensajes." QUOTA_SIZE: "Usando %SIZE% (%PROC%%) de sus %LIMIT%" + SORT: "Ordenar" + SORT_DATE_ASC: "Fecha ascendente" + SORT_DATE_DESC: "Fecha descendente" + SORT_SIZE_ASC: "Tamaño ascendente" + SORT_SIZE_DESC: "Tamaño descendente" + SORT_SUBJECT_ASC: "Asunto A-Z" + SORT_SUBJECT_DESC: "Asunto Z-A" + SORT_FROM_ASC: "Desde A-Z" + SORT_FROM_DESC: "Desde Z-A" MESSAGE: BUTTON_EDIT: "Editar" BUTTON_UNSUBSCRIBE: "Eliminar suscripción de esta lista" diff --git a/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml b/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml index a67a06f02..f67640847 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml @@ -101,6 +101,15 @@ fr_FR: YESTERDAY_AT: "hier à %TIME%" NEW_MESSAGE_NOTIFICATION: "Vous avez %COUNT% nouveaux messages !" QUOTA_SIZE: "Vous utilisez %SIZE% (%PROC%%) sur %LIMIT%" + SORT: "Trier" + SORT_DATE_ASC: "Date ascendante" + SORT_DATE_DESC: "Date décroissante" + SORT_SIZE_ASC: "Taille ascendante" + SORT_SIZE_DESC: "Taille décroissante" + SORT_SUBJECT_ASC: "Sujet A-Z" + SORT_SUBJECT_DESC: "Sujet Z-A" + SORT_FROM_ASC: "De A-Z" + SORT_FROM_DESC: "De Z-A" MESSAGE: BUTTON_EDIT: "Modifier" BUTTON_UNSUBSCRIBE: "Se désinscrire de cette liste" diff --git a/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml b/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml index 7bc303307..8a37c4761 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml @@ -101,6 +101,15 @@ nl_NL: YESTERDAY_AT: "gisteren om %TIME%" NEW_MESSAGE_NOTIFICATION: "U heeft %COUNT% nieuwe berichten!" QUOTA_SIZE: "U gebruikt %SIZE% (%PROC%%) van %LIMIT%" + SORT: "Sorteren" + SORT_DATE_ASC: "Datum oplopend" + SORT_DATE_DESC: "Datum aflopend" + SORT_SIZE_ASC: "Grootte oplopend" + SORT_SIZE_DESC: "Grootte aflopend" + SORT_SUBJECT_ASC: "Onderwerp A-Z" + SORT_SUBJECT_DESC: "Onderwerp Z-A" + SORT_FROM_ASC: "Van A-Z" + SORT_FROM_DESC: "Van Z-A" MESSAGE: BUTTON_EDIT: "Aanpassen" BUTTON_UNSUBSCRIBE: "Abonnement op deze e-mail lijst opzeggen" diff --git a/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml b/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml index 37337a3a4..2ff83d1d1 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml @@ -101,6 +101,15 @@ zh_CN: YESTERDAY_AT: "昨天 %TIME%" NEW_MESSAGE_NOTIFICATION: "您有 %COUNT% 封新邮件!" QUOTA_SIZE: "使用了 %LIMIT% 存储空间中的%SIZE% (%PROC%%)" + SORT: "排序" + SORT_DATE_ASC: "日期升序" + SORT_DATE_DESC: "日期降序" + SORT_SIZE_ASC: "大小递增" + SORT_SIZE_DESC: "大小递减" + SORT_SUBJECT_ASC: "主题 A-Z" + SORT_SUBJECT_DESC: "主题 Z-A" + SORT_FROM_ASC: "发送自 A-Z" + SORT_FROM_DESC: "发送自 Z-A" MESSAGE: BUTTON_EDIT: "编辑" BUTTON_UNSUBSCRIBE: "从此列表中取消订阅" diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index 9f89d752d..3a039252f 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -101,8 +101,8 @@
- +
From 135831b11684b043ae68e911b4420162315807a6 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 14:21:47 +0200 Subject: [PATCH 12/90] Drop root folders with \noselect flag Like 'dovecot' which has 'dovecot.sieve' which has no emails --- .../v/0.0.0/app/libraries/RainLoop/Actions/Folders.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php index f1af24421..2bfc28825 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -36,6 +36,12 @@ trait Folders if ($oFolderCollection instanceof \MailSo\Mail\FolderCollection) { + foreach ($oFolderCollection as $i => $oFolder) { + if (!$oFolder->IsSelectable()) { + unset($oFolderCollection[$i]); + } + } + $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); $aSystemFolders = array(); From 5d09a141938faca297bf86c43496237ec22fb61f Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 15:51:46 +0200 Subject: [PATCH 13/90] Bugfix: Previous commit didn't fix numeric indices --- .../v/0.0.0/app/libraries/RainLoop/Actions/Folders.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php index 2bfc28825..182910d8f 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -36,11 +36,13 @@ trait Folders if ($oFolderCollection instanceof \MailSo\Mail\FolderCollection) { - foreach ($oFolderCollection as $i => $oFolder) { + $aFolders = $oFolderCollection->getArrayCopy(); + foreach ($aFolders as $i => $oFolder) { if (!$oFolder->IsSelectable()) { - unset($oFolderCollection[$i]); + unset($aFolders[$i]); } } + $oFolderCollection->exchangeArray(\array_values($aFolders)); $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); From 544d2272c807e882f9b632d92492d2f14a696080 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 15:52:37 +0200 Subject: [PATCH 14/90] Resolve #68 --- snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php index 182910d8f..551a26659 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -518,7 +518,7 @@ trait Folders if (isset($aMap[$sName]) || isset($aMap[$sFullName])) { $iFolderType = isset($aMap[$sName]) ? $aMap[$sName] : $aMap[$sFullName]; - if (!isset($aResult[$iFolderType]) && \in_array($iFolderType, array( + if ((!isset($aResult[$iFolderType]) || $sName === $sFullName || "INBOX{$oFolder->Delimiter()}{$sName}" === $sFullName) && \in_array($iFolderType, array( FolderType::INBOX, FolderType::SENT, FolderType::DRAFTS, From 34efa2205cf5af1cb22fe9e27bf0d1221d948b28 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 15:58:07 +0200 Subject: [PATCH 15/90] Properly backslash escape IMAP folder flags --- .../MailSo/Imap/Enumerations/MessageFlag.php | 12 ++-- .../app/libraries/MailSo/Imap/Folder.php | 4 +- .../app/libraries/MailSo/Mail/Folder.php | 64 +++++++++---------- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MessageFlag.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MessageFlag.php index a539175aa..be19077ec 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MessageFlag.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Enumerations/MessageFlag.php @@ -18,10 +18,10 @@ namespace MailSo\Imap\Enumerations; */ abstract class MessageFlag { - const RECENT = '\Recent'; - const SEEN = '\Seen'; - const DELETED = '\Deleted'; - const FLAGGED = '\Flagged'; - const ANSWERED = '\Answered'; - const DRAFT = '\Draft'; + const RECENT = '\\Recent'; + const SEEN = '\\Seen'; + const DELETED = '\\Deleted'; + const FLAGGED = '\\Flagged'; + const ANSWERED = '\\Answered'; + const DRAFT = '\\Draft'; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Folder.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Folder.php index 34f818594..0cd33b8d5 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Folder.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Folder.php @@ -116,12 +116,12 @@ class Folder public function IsSelectable() : bool { - return !\in_array('\noselect', $this->aFlagsLowerCase); + return !\in_array('\\noselect', $this->aFlagsLowerCase); } public function IsInbox() : bool { - return 'INBOX' === \strtoupper($this->sFullNameRaw) || \in_array('\inbox', $this->aFlagsLowerCase); + return 'INBOX' === \strtoupper($this->sFullNameRaw) || \in_array('\\inbox', $this->aFlagsLowerCase); } /** diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php index 4c8864259..a86752760 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Folder.php @@ -76,7 +76,7 @@ class Folder implements \JsonSerializable public static function NewNonExistenInstance(string $sFullNameRaw, string $sDelimiter) : self { return new self( - new \MailSo\Imap\Folder($sFullNameRaw, $sDelimiter, array('\NoSelect')), true, false); + new \MailSo\Imap\Folder($sFullNameRaw, $sDelimiter, array('\\Noselect')), true, false); } public function Name() : string @@ -188,43 +188,41 @@ class Folder implements \JsonSerializable public function GetFolderListType() : int { $aFlags = $this->oImapFolder->FlagsLowerCase(); - $iListType = \MailSo\Imap\Enumerations\FolderType::USER; switch (true) { - case \in_array('\inbox', $aFlags) || 'INBOX' === \strtoupper($this->FullNameRaw()): - $iListType = \MailSo\Imap\Enumerations\FolderType::INBOX; - break; - case \in_array('\sent', $aFlags): - case \in_array('\sentmail', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::SENT; - break; - case \in_array('\drafts', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::DRAFTS; - break; - case \in_array('\junk', $aFlags): - case \in_array('\spam', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::JUNK; - break; - case \in_array('\trash', $aFlags): - case \in_array('\bin', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::TRASH; - break; - case \in_array('\important', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::IMPORTANT; - break; - case \in_array('\flagged', $aFlags): - case \in_array('\starred', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::FLAGGED; - break; - case \in_array('\all', $aFlags): - case \in_array('\allmail', $aFlags): - case \in_array('\archive', $aFlags): - $iListType = \MailSo\Imap\Enumerations\FolderType::ALL; - break; + case \in_array('\\inbox', $aFlags) || 'INBOX' === \strtoupper($this->FullNameRaw()): + return \MailSo\Imap\Enumerations\FolderType::INBOX; + + case \in_array('\\sent', $aFlags): + case \in_array('\\sentmail', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::SENT; + + case \in_array('\\drafts', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::DRAFTS; + + case \in_array('\\junk', $aFlags): + case \in_array('\\spam', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::JUNK; + + case \in_array('\\trash', $aFlags): + case \in_array('\\bin', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::TRASH; + + case \in_array('\\important', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::IMPORTANT; + + case \in_array('\\flagged', $aFlags): + case \in_array('\\starred', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::FLAGGED; + + case \in_array('\\all', $aFlags): + case \in_array('\\allmail', $aFlags): + case \in_array('\\archive', $aFlags): + return \MailSo\Imap\Enumerations\FolderType::ALL; } - return $iListType; + return \MailSo\Imap\Enumerations\FolderType::USER; } public function jsonSerialize() From d0dc257619ef614c0a417cc2285f515a653dc999 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 20:49:43 +0200 Subject: [PATCH 16/90] Support TLSv 1.3 where possible --- snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php index 6187e1732..ec34e5232 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php @@ -283,6 +283,8 @@ abstract class NetClient { switch (true) { + case defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && OPENSSL_VERSION_NUMBER >= 0x10101000 && + \stream_socket_enable_crypto($this->rConnect, true, STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT): case defined('STREAM_CRYPTO_METHOD_ANY_CLIENT') && \stream_socket_enable_crypto($this->rConnect, true, STREAM_CRYPTO_METHOD_ANY_CLIENT): case defined('STREAM_CRYPTO_METHOD_TLS_CLIENT') && From 68d103b3c19938fce76fc3c58ae12ec2d76a4697 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 20:51:14 +0200 Subject: [PATCH 17/90] Added support for SCRAM-SHA-1 & SCRAM-SHA-256 https://github.com/RainLoop/rainloop-webmail/issues/1914 --- .../app/libraries/MailSo/Smtp/SmtpClient.php | 200 +++++++----------- .../v/0.0.0/app/libraries/snappymail/sasl.php | 52 +++++ .../app/libraries/snappymail/sasl/cram.php | 27 +++ .../app/libraries/snappymail/sasl/login.php | 32 +++ .../app/libraries/snappymail/sasl/plain.php | 18 ++ .../app/libraries/snappymail/sasl/scram.php | 119 +++++++++++ 6 files changed, 330 insertions(+), 118 deletions(-) create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/sasl.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/sasl/cram.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/sasl/plain.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php index 96974594b..b9acd0787 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php @@ -62,11 +62,6 @@ class SmtpClient extends \MailSo\Net\NetClient */ private $aResults; - /** - * @var bool - */ - public $__USE_SINGLE_LINE_AUTH_PLAIN_COMMAND = false; - function __construct() { parent::__construct(); @@ -150,126 +145,94 @@ class SmtpClient extends \MailSo\Net\NetClient { $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sLogin)); - if ($bUseAuthCramMd5IfSupported && $this->IsAuthSupported('CRAM-MD5')) - { - try - { - $this->sendRequestWithCheck('AUTH', 334, 'CRAM-MD5'); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadMethodException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); - } - - $sTicket = ''; - - $sContinuationResponse = !empty($this->aResults[0]) ? \trim($this->aResults[0]) : ''; - if ($sContinuationResponse && '334 ' === \substr($sContinuationResponse, 0, 4) && 0 < \strlen(\substr($sContinuationResponse, 4))) - { - $sTicket = \base64_decode(\substr($sContinuationResponse, 4)); - $this->writeLogWithCrlf('ticket: '.$sTicket); - } - - if (empty($sTicket)) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\NegativeResponseException, - \MailSo\Log\Enumerations\Type::NOTICE, true - ); - } - - try - { - $this->sendRequestWithCheck(\base64_encode($sLogin.' '.\hash_hmac('md5', $sTicket, $sPassword)), 235, '', true); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); +// $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']); + $type = ''; + $types = [ + 'SCRAM-SHA-256' => 1, // !$encrypted + 'SCRAM-SHA-1' => 1, // !$encrypted + 'CRAM-MD5' => $bUseAuthCramMd5IfSupported, + 'PLAIN' => $bUseAuthPlainIfSupported, + 'LOGIN' => 1 // $encrypted + ]; + foreach ($types as $sasl_type => $active) { + if ($active && $this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) { + $type = $sasl_type; + break; } } - else if ($bUseAuthPlainIfSupported && $this->IsAuthSupported('PLAIN')) - { - if ($this->__USE_SINGLE_LINE_AUTH_PLAIN_COMMAND) - { - try - { - $this->sendRequestWithCheck('AUTH', 235, 'PLAIN '.\base64_encode("\0".$sLogin."\0".$sPassword), true); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); - } - } - else - { - try - { - $this->sendRequestWithCheck('AUTH', 334, 'PLAIN'); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadMethodException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); - } - try - { - $this->sendRequestWithCheck(\base64_encode("\0".$sLogin."\0".$sPassword), 235, '', true); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); - } - } - } - else if ($this->IsAuthSupported('LOGIN')) - { - try - { - $this->sendRequestWithCheck('AUTH', 334, 'LOGIN'); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadMethodException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); - } - - try - { - $this->sendRequestWithCheck(\base64_encode($sLogin), 334, ''); - $this->sendRequestWithCheck(\base64_encode($sPassword), 235, '', true); - } - catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) - { - $this->writeLogException( - new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( - $oException->GetResponses(), $oException->getMessage(), 0, $oException), - \MailSo\Log\Enumerations\Type::NOTICE, true); - } - } - else - { + if (!$type) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadMethodException, \MailSo\Log\Enumerations\Type::NOTICE, true); } + $SASL = \SnappyMail\SASL::factory($type); + $SASL->base64 = true; + + // Start authentication + try + { + $sResult = $this->sendRequestWithCheck('AUTH', 334, $type); + } + catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) + { + $this->writeLogException( + new \MailSo\Smtp\Exceptions\LoginBadMethodException( + $oException->GetResponses(), $oException->getMessage(), 0, $oException), + \MailSo\Log\Enumerations\Type::NOTICE, true); + } + + try + { + switch ($type) + { + // RFC 4616 + case 'PLAIN': + $this->sendRequestWithCheck($SASL->authenticate($username, $passphrase), 235, '', true); + break; + + case 'LOGIN': + $sResult = $this->sendRequestWithCheck($SASL->authenticate($username, $passphrase, $sResult), 334, ''); + $this->sendRequestWithCheck($SASL->challenge($sResult), 235, '', true); + break; + + // RFC 2195 + case 'CRAM-MD5': + if (empty($sResult)) { + $this->writeLogException( + new \MailSo\Smtp\Exceptions\NegativeResponseException, + \MailSo\Log\Enumerations\Type::NOTICE, true + ); + } + $this->sendRequestWithCheck($SASL->authenticate($sLogin, $sPassword, $sResult), 235, '', true); + break; + + // RFC 5802 + case 'SCRAM-SHA-1': + case 'SCRAM-SHA-256': + $sResult = $this->sendRequestWithCheck($SASL->authenticate($username, $passphrase, $sResult), 234, ''); + $sResult = $this->sendRequestWithCheck($SASL->challenge($sResult), 235, '', true); + $SASL->verify($sResult); + break; + +/* + // https://developers.google.com/gmail/imap/xoauth2-protocol + case 'XOAUTH2': + throw new \Exception('Please use app passphrases: https://support.google.com/mail/answer/185833'); + break; +*/ + + } + } + catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) + { + $this->writeLogException( + new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( + $oException->GetResponses(), $oException->getMessage(), 0, $oException), + \MailSo\Log\Enumerations\Type::NOTICE, true); + } + return $this; } @@ -542,10 +505,11 @@ class SmtpClient extends \MailSo\Net\NetClient * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ - private function sendRequestWithCheck(string $sCommand, $mExpectCode, string $sAddToCommand = '', bool $bSecureLog = false, string $sErrorPrefix = '') : void + private function sendRequestWithCheck(string $sCommand, $mExpectCode, string $sAddToCommand = '', bool $bSecureLog = false, string $sErrorPrefix = '') : string { $this->sendRequest($sCommand, $sAddToCommand, $bSecureLog); $this->validateResponse($mExpectCode, $sErrorPrefix); + return empty($this->aResults[0]) ? '' : \substr($this->aResults[0], 4); } private function ehloOrHelo(string $sHost) : void diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php new file mode 100644 index 000000000..ef7565eab --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php @@ -0,0 +1,52 @@ +base64 ? \base64_decode($data) : $data; + } + + final protected function encode(string $data) : string + { + return $this->base64 ? \base64_encode($data) : $data; + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/cram.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/cram.php new file mode 100644 index 000000000..4ed7f6b7e --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/cram.php @@ -0,0 +1,27 @@ +algo = $algo; + } + + public function authenticate(string $authcid, string $passphrase, ?string $challenge = null) : string + { + return $this->encode($authcid . ' ' . \hash_hmac($this->algo, $this->decode($challenge), $passphrase)); + } + + public static function isSupported(string $param) : bool + { + return \in_array(\strtolower($param), \hash_algos()); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php new file mode 100644 index 000000000..743a62f6f --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php @@ -0,0 +1,32 @@ +decode($challenge)) { + throw new \Exception("Invalid response: {$challenge}"); + } + $this->passphrase = $passphrase; + return $this->encode($username); + } + + public function challenge(string $challenge) : string + { + if ($challenge && 'Password:' !== $this->decode($challenge)) { + throw new \Exception("invalid response: {$challenge}"); + } + return $this->encode($this->passphrase); + } + + public static function isSupported(string $param) : bool + { + return true; + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/plain.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/plain.php new file mode 100644 index 000000000..dca6aa103 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/plain.php @@ -0,0 +1,18 @@ +encode("{$authzid}\x00{$username}\x00{$passphrase}"); + } + + public static function isSupported(string $param) : bool + { + return true; + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php new file mode 100644 index 000000000..f1b630917 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php @@ -0,0 +1,119 @@ +algo = $algo; + } + + public function authenticate(string $authcid, string $passphrase, ?string $authzid = null) : string + { + // SASLprep + $authcid = \str_replace(array('=',','), array('=3D','=2C'), $authcid); + + $this->nonce = \bin2hex(\random_bytes(16)); + $this->passphrase = $passphrase; + $this->gs2_header = 'n,' . (empty($authzid) ? '' : 'a=' . $authzid) . ','; + $this->auth_message = "n={$authcid},r={$this->nonce}"; + return $this->encode($this->gs2_header . $this->auth_message); + } + + public function challenge(string $challenge) : string + { + $challenge = $this->decode($challenge); + $values = static::parseMessage($challenge); + + if (empty($values['r'])) { + throw new \Exception('Server nonce not found'); + } + if (empty($values['s'])) { + throw new \Exception('Server salt not found'); + } + if (empty($values['i'])) { + throw new \Exception('Server iterator not found'); + } + + if (\substr($values['r'], 0, \strlen($this->nonce)) !== $this->nonce) { + throw new \Exception('Server invalid nonce'); + } + + $salt = \base64_decode($values['s']); + if (!$salt) { + throw new \Exception('Server invalid salt'); + } + + $pass = \hash_pbkdf2($this->algo, $this->passphrase, $salt, \intval($values['i']), 0, true); + $this->passphrase = null; + + $ckey = \hash_hmac($this->algo, 'Client Key', $pass, true); + $skey = \hash($this->algo, $ckey, true); + + $cfmb = 'c='.\base64_encode($this->gs2_header).',r='.$values['r']; + $amsg = "{$this->auth_message},{$challenge},{$cfmb}"; + + $csig = \hash_hmac($this->algo, $amsg, $skey, true); + $proof = \base64_encode($ckey ^ $csig); + + $skey = \hash_hmac($this->algo, 'Server Key', $pass, true); + $this->server_key = \hash_hmac($this->algo, $amsg, $skey, true); + + return $this->encode("{$cfmb},p={$proof}"); + } + + public function verify(string $data) : bool + { + $v = static::parseMessage($this->decode($data)); + if (empty($v['v'])) { + throw new \Exception('Server signature not found'); + } + if (\base64_encode($this->server_key) !== $v['v']) { + throw new \Exception('Server signature invalid'); + } + return true; + } + + protected static function parseMessage(string $msg) : array + { + if ($msg && \preg_match_all('#(\w+)\=(?:"([^"]+)"|([^,]+))#', $msg, $m)) { + return \array_combine( + $m[1], + \array_replace( + \array_filter($m[2]), + \array_filter($m[3]) + ) + ); + } + return array(); + } + + public static function isSupported(string $param) : bool + { + $param = \str_replace('sha-', 'sha', \strtolower($param)); + return \in_array($param, \hash_algos()); + } + +} From 389e473e221c01c4e8248a5c6c8b39272da53af3 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 21:43:51 +0200 Subject: [PATCH 18/90] Improved support for SCRAM-SHA-1 & SCRAM-SHA-256 --- .../app/libraries/MailSo/Imap/ImapClient.php | 31 +++++++++++++++---- .../app/libraries/MailSo/Smtp/SmtpClient.php | 8 ++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index e6d243c6e..214efabd0 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -162,21 +162,40 @@ class ImapClient extends \MailSo\Net\NetClient $this->sLogginedUser = $sLogin; +// $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']); + $type = ''; + $types = [ +// 'SCRAM-SHA-256' => 1, // !$encrypted +// 'SCRAM-SHA-1' => 1, // !$encrypted + 'CRAM-MD5' => $bUseAuthCramMd5IfSupported, + 'PLAIN' => $bUseAuthPlainIfSupported, + 'LOGIN' => 1 // $encrypted + ]; + foreach ($types as $sasl_type => $active) { + if ($active && $this->IsSupported("AUTH={$sasl_type}") && \SnappyMail\SASL::isSupported($sasl_type)) { + $type = $sasl_type; + break; + } + } + + $SASL = \SnappyMail\SASL::factory($type); + $SASL->base64 = true; + try { - if ($bUseAuthCramMd5IfSupported && $this->IsSupported('AUTH=CRAM-MD5')) + if ('CRAM-MD5' === $type) { - $oContinuationResponse = $this->SendRequestGetResponse('AUTHENTICATE', array('CRAM-MD5'))->getLast(); + $oContinuationResponse = $this->SendRequestGetResponse('AUTHENTICATE', array($type))->getLast(); if ($oContinuationResponse && Enumerations\ResponseType::CONTINUATION === $oContinuationResponse->ResponseType) { $sTicket = $oContinuationResponse->ResponseList[1] ?? null; if ($sTicket) { + $sToken = $SASL->authenticate($sLogin, $sPassword, $sTicket) + $sTicket = \base64_decode($sTicket); $this->oLogger->Write('ticket: '.$sTicket); - $sToken = \base64_encode($sLogin.' '.\hash_hmac('md5', $sTicket, $sPassword)); - if ($this->oLogger) { $this->oLogger->AddSecret($sToken); @@ -199,9 +218,9 @@ class ImapClient extends \MailSo\Net\NetClient \MailSo\Log\Enumerations\Type::NOTICE, true); } } - else if ($bUseAuthPlainIfSupported && $this->IsSupported('AUTH=PLAIN')) + else if ('PLAIN' === $type) { - $sToken = \base64_encode("\0".$sLogin."\0".$sPassword); + $sToken = $SASL->authenticate($sLogin, $sPassword); if ($this->oLogger) { $this->oLogger->AddSecret($sToken); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php index b9acd0787..78364724b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php @@ -189,11 +189,11 @@ class SmtpClient extends \MailSo\Net\NetClient { // RFC 4616 case 'PLAIN': - $this->sendRequestWithCheck($SASL->authenticate($username, $passphrase), 235, '', true); + $this->sendRequestWithCheck($SASL->authenticate($sLogin, $sPassword), 235, '', true); break; case 'LOGIN': - $sResult = $this->sendRequestWithCheck($SASL->authenticate($username, $passphrase, $sResult), 334, ''); + $sResult = $this->sendRequestWithCheck($SASL->authenticate($sLogin, $sPassword, $sResult), 334, ''); $this->sendRequestWithCheck($SASL->challenge($sResult), 235, '', true); break; @@ -211,7 +211,7 @@ class SmtpClient extends \MailSo\Net\NetClient // RFC 5802 case 'SCRAM-SHA-1': case 'SCRAM-SHA-256': - $sResult = $this->sendRequestWithCheck($SASL->authenticate($username, $passphrase, $sResult), 234, ''); + $sResult = $this->sendRequestWithCheck($SASL->authenticate($sLogin, $sPassword, $sResult), 234, ''); $sResult = $this->sendRequestWithCheck($SASL->challenge($sResult), 235, '', true); $SASL->verify($sResult); break; @@ -509,7 +509,7 @@ class SmtpClient extends \MailSo\Net\NetClient { $this->sendRequest($sCommand, $sAddToCommand, $bSecureLog); $this->validateResponse($mExpectCode, $sErrorPrefix); - return empty($this->aResults[0]) ? '' : \substr($this->aResults[0], 4); + return empty($this->aResults[0]) ? '' : \trim(\substr($this->aResults[0], 4)); } private function ehloOrHelo(string $sHost) : void From 52abb3cf48a091a49f5cdf62a8a176b790bc7cc6 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 21:54:55 +0200 Subject: [PATCH 19/90] Bugfix: missed SASL commit --- snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 214efabd0..fa1a64084 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -191,7 +191,7 @@ class ImapClient extends \MailSo\Net\NetClient $sTicket = $oContinuationResponse->ResponseList[1] ?? null; if ($sTicket) { - $sToken = $SASL->authenticate($sLogin, $sPassword, $sTicket) + $sToken = $SASL->authenticate($sLogin, $sPassword, $sTicket); $sTicket = \base64_decode($sTicket); $this->oLogger->Write('ticket: '.$sTicket); From be855b36f1cb63f8c5966585a4c586793acf3251 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 21:58:06 +0200 Subject: [PATCH 20/90] Bugfix: SASL::factory() should not return void --- snappymail/v/0.0.0/app/libraries/snappymail/sasl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php index ef7565eab..bc5b6020f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php @@ -19,7 +19,7 @@ abstract class SASL return false; } - final public static function factory(string $type) : void + final public static function factory(string $type) { if (\preg_match('/^([A-Z2]+)(?:-(.+))?$/Di', $type, $m)) { $class = __CLASS__ . "\\{$m[1]}"; From b594f6f5fef6a59eaa42c093f55cd7f5339e2d19 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 22:05:08 +0200 Subject: [PATCH 21/90] Improved #69 --- dev/Model/FolderCollection.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index 70bd6da30..0b4112f9d 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -10,6 +10,7 @@ import * as Local from 'Storage/Client'; import { AppUserStore } from 'Stores/User/App'; import { FolderUserStore } from 'Stores/User/Folder'; +import { SettingsUserStore } from 'Stores/User/Settings'; import ko from 'ko'; @@ -300,7 +301,10 @@ export class FolderModel extends AbstractModel { canBeDeleted: () => !folder.isSystemFolder() && !folder.subFolders.length, - canBeSubscribed: () => !folder.isSystemFolder() && folder.selectable && Settings.app('useImapSubscribe'), + canBeSubscribed: () => !folder.isSystemFolder() + && SettingsUserStore.hideUnsubscribed() + && folder.selectable + && Settings.app('useImapSubscribe'), canBeSelected: () => !folder.isSystemFolder() && folder.selectable, From 1129db3f042acd7fd787710c4e3ac2d40d9c6cd7 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 22:05:35 +0200 Subject: [PATCH 22/90] Improved #67 --- dev/View/User/MailBox/MessageList.js | 18 +++++++++--------- .../templates/Views/User/MailMessageList.html | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 3701cbe93..e5238ae75 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -179,16 +179,16 @@ export class MessageListMailBoxUserView extends AbstractViewRight { messageListFocused: () => Scope.MessageList === AppUserStore.focusedState(), sortText: () => { - let mode = FolderUserStore.sortMode(); - if ('' === mode) { - return '📅⬇'; - } + let mode = FolderUserStore.sortMode(), + desc = '' === mode || mode.includes('REVERSE'); mode = mode.split(/\s+/); - return (mode.includes('SIZE') ? '✉' - : (mode.includes('FROM') ? '@' - : (mode.includes('SUBJECT') ? '𝐒' - : '📅'))) - + (mode.includes('REVERSE') ? '⬇' : '⬆'); + if (mode.includes('FROM')) { + return '@' + (desc ? '⬆' : '⬇'); + } + if (mode.includes('SUBJECT')) { + return '𝐒' + (desc ? '⬆' : '⬇'); + } + return (mode.includes('SIZE') ? '✉' : '📅') + (desc ? '⬇' : '⬆'); } }); diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index 3a039252f..e5cfa630d 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -130,25 +130,25 @@ From ba982edce7f1d3726f434988c12f53ac24042824 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 29 Mar 2021 22:31:51 +0200 Subject: [PATCH 23/90] v2.4.3 --- README.md | 16 ++-- dev/Settings/Admin/General.js | 6 +- package.json | 2 +- .../app/libraries/MailSo/Smtp/SmtpClient.php | 1 + yarn.lock | 77 +++++++++---------- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 30bb8d30a..7cd7bcb40 100644 --- a/README.md +++ b/README.md @@ -116,23 +116,23 @@ RainLoop 1.15 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.158.025 | 100.608 | -|app.js |4.215.733 | 495.934 | +|admin.js |2.158.025 | 100.721 | +|app.js |4.215.733 | 497.330 | |boot.js | 672.433 | 4.726 | |libs.js | 647.679 | 227.974 | |polyfills.js | 325.908 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.019.778 | 829.355 | +|TOTAL |8.019.778 | 830.864 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | |admin.min.js | 255.514 | 50.607 | 73.899 | 14.944 | 60.674 | 13.364 | -|app.min.js | 516.000 | 240.553 |140.430 | 70.291 |110.657 | 59.309 | +|app.min.js | 516.000 | 241.496 |140.430 | 70.637 |110.657 | 59.605 | |boot.min.js | 66.456 | 2.442 | 22.553 | 1.371 | 20.043 | 1.178 | |libs.min.js | 574.626 | 115.877 |177.280 | 42.809 |151.855 | 38.099 | |polyfills.min.js | 32.608 | 0 | 11.315 | 0 | 10.072 | 0 | -|TOTAL |1.445.204 | 409.479 |425.477 |129.415 |353.301 |111.950 | -|TOTAL (no admin) |1.189.690 | 358.872 |351.061 |114.471 |292.627 | 98.586 | +|TOTAL |1.445.204 | 410.422 |425.477 |129.761 |353.301 |112.246 | +|TOTAL (no admin) |1.189.690 | 359.815 |351.061 |114.817 |292.627 | 98.882 | For a user its around 66% smaller and faster than traditional RainLoop. @@ -166,8 +166,8 @@ For a user its around 66% smaller and faster than traditional RainLoop. |css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli | |------------ |-------: |-------: |------: |------: |--------: | -|app.css | 340.334 | 107.279 | 46,959 | 18.816 | 16.160 | -|app.min.css | 274.791 | 88.402 | 39.618 | 16.831 | 14.827 | +|app.css | 340.334 | 107.471 | 46,959 | 18.828 | 16.184 | +|app.min.css | 274.791 | 88.601 | 39.618 | 16.867 | 14.861 | |boot.css | | 2.066 | | 913 | 742 | |boot.min.css | | 1.696 | | 818 | 664 | |admin.css | | 46.393 | | 9.237 | 8.073 | diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index 22066fbce..7a0f459d6 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -53,7 +53,11 @@ export class GeneralAdminSettings { this.weakPassword = rl.app.weakPassword; -// fetch('./data/VERSION?' + Math.random()).then(response => this.dataFolderAccess(response.ok)); + /** https://github.com/RainLoop/rainloop-webmail/issues/1924 + if (this.weakPassword) { + fetch('./data/VERSION?' + Math.random()).then(response => this.dataFolderAccess(response.ok)); + } + */ this.mainAttachmentLimit = ko .observable(pInt(SettingsGet('AttachmentLimit')) / (1024 * 1024)) diff --git a/package.json b/package.json index 3e89b199b..e47012071 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "title": "SnappyMail", "description": "Simple, modern & fast web-based email client", "private": true, - "version": "2.4.2", + "version": "2.4.3", "homepage": "https://snappymail.eu", "author": { "name": "DJ Maze", diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php index 78364724b..bd0eb34e7 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php @@ -162,6 +162,7 @@ class SmtpClient extends \MailSo\Net\NetClient } if (!$type) { + \trigger_error("SMTP {$this->sConnectedHost} no supported AUTH options. Disable login" . ($this->IsSupported('STARTTLS') ? ' or try with STARTTLS' : '')); $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadMethodException, \MailSo\Log\Enumerations\Type::NOTICE, true); diff --git a/yarn.lock b/yarn.lock index b64255cea..e3eaf0120 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,7 +9,7 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.8", "@babel/compat-data@^7.8.6": +"@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.8.6": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== @@ -35,7 +35,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.13.0", "@babel/generator@^7.8.7": +"@babel/generator@^7.13.9", "@babel/generator@^7.8.7": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== @@ -60,11 +60,11 @@ "@babel/types" "^7.12.13" "@babel/helper-compilation-targets@^7.13.8", "@babel/helper-compilation-targets@^7.8.7": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" - integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5" + integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== dependencies: - "@babel/compat-data" "^7.13.8" + "@babel/compat-data" "^7.13.12" "@babel/helper-validator-option" "^7.12.17" browserslist "^4.14.5" semver "^6.3.0" @@ -134,9 +134,9 @@ "@babel/types" "^7.13.12" "@babel/helper-module-transforms@^7.13.0": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.12.tgz#600e58350490828d82282631a1422268e982ba96" - integrity sha512-7zVQqMO3V+K4JOOj40kxiCrMf6xlQAkewBB0eu2b03OO/Q21ZutOzjpfD79A5gtE/2OWi1nv625MrDlGlkbknQ== + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef" + integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g== dependencies: "@babel/helper-module-imports" "^7.13.12" "@babel/helper-replace-supers" "^7.13.12" @@ -144,8 +144,8 @@ "@babel/helper-split-export-declaration" "^7.12.13" "@babel/helper-validator-identifier" "^7.12.11" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.12" + "@babel/traverse" "^7.13.13" + "@babel/types" "^7.13.14" "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" @@ -237,10 +237,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.7.0", "@babel/parser@^7.8.7": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.12.tgz#ba320059420774394d3b0c0233ba40e4250b81d1" - integrity sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw== +"@babel/parser@^7.12.13", "@babel/parser@^7.13.13", "@babel/parser@^7.7.0", "@babel/parser@^7.8.7": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" + integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.13.8" @@ -719,25 +719,24 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.6": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" - integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== +"@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.6": + version "7.13.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" + integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.0" + "@babel/generator" "^7.13.9" "@babel/helper-function-name" "^7.12.13" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/parser" "^7.13.13" + "@babel/types" "^7.13.13" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.19" -"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.7.0", "@babel/types@^7.8.7": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.12.tgz#edbf99208ef48852acdff1c8a681a1e4ade580cd" - integrity sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA== +"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.7.0", "@babel/types@^7.8.7": + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" + integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== dependencies: "@babel/helper-validator-identifier" "^7.12.11" lodash "^4.17.19" @@ -762,9 +761,9 @@ integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== "@types/node@*": - version "14.14.35" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" - integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag== + version "14.14.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" + integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== accord@^0.29.0: version "0.29.0" @@ -1709,9 +1708,9 @@ editions@^1.3.3: integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg== electron-to-chromium@^1.3.649: - version "1.3.699" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.699.tgz#854eea9db8bc8109c409a4807bfdb200dd75a2c7" - integrity sha512-fjt43CPXdPYwD9ybmKbNeLwZBmCVdLY2J5fGZub7/eMPuiqQznOGNXv/wurnpXIlE7ScHnvG9Zi+H4/i6uMKmw== + version "1.3.701" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.701.tgz#5e796ed7ce88cd77bc7bf831cf311ef6b067c389" + integrity sha512-Zd9ofdIMYHYhG1gvnejQDvC/kqSeXQvtXF0yRURGxgwGqDZm9F9Fm3dYFnm5gyuA7xpXfBlzVLN1sz0FjxpKfw== emoji-regex@^7.0.1: version "7.0.3" @@ -4407,9 +4406,9 @@ rollup-pluginutils@^2.8.1: estree-walker "^0.6.1" rollup@^2.38.0, rollup@^2.41.4: - version "2.42.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.42.4.tgz#97c910a48bd0db6aaa4271dd48745870cbbbf970" - integrity sha512-Zqv3EvNfcllBHyyEUM754npqsZw82VIjK34cDQMwrQ1d6aqxzeYu5yFb7smGkPU4C1Bj7HupIMeT6WU7uIdnMw== + version "2.44.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.44.0.tgz#8da324d1c4fd12beef9ae6e12f4068265b6d95eb" + integrity sha512-rGSF4pLwvuaH/x4nAS+zP6UNn5YUDWf/TeEU5IoXSZKBbKRNTCI3qMnYXKZgrC0D2KzS2baiOZt1OlqhMu5rnQ== optionalDependencies: fsevents "~2.3.1" @@ -4419,9 +4418,9 @@ run-async@^2.4.0: integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== rxjs@^6.6.0: - version "6.6.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" - integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" From eb69494f0cdbf6479e982c11b2655231f3bed6fd Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 30 Mar 2021 09:10:43 +0200 Subject: [PATCH 24/90] Resolve #71 --- dev/App/User.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/App/User.js b/dev/App/User.js index 36f7f2836..31adcad37 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -192,7 +192,7 @@ class AppUser extends AbstractApp { Offset: iOffset, Limit: SettingsUserStore.messagesPerPage(), Search: MessageUserStore.listSearch(), - UidNext: MessageUserStore.listThreadUid() + ThreadUid: MessageUserStore.listThreadUid() } ); } From b26e709b8f2246d38abbefdbd0d242788815337f Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 30 Mar 2021 10:18:31 +0200 Subject: [PATCH 25/90] https://github.com/the-djmaze/snappymail/issues/71#issuecomment-810013614 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e47012071..6330bb7ca 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "title": "SnappyMail", "description": "Simple, modern & fast web-based email client", "private": true, - "version": "2.4.3", + "version": "2.4.3.1", "homepage": "https://snappymail.eu", "author": { "name": "DJ Maze", From 5cb150a744bd1e8ff7ab4c665268e79fc50009ac Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 30 Mar 2021 10:58:24 +0200 Subject: [PATCH 26/90] Prepare mailbox routing for new feature --- dev/Screen/User/MailBox.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index 83e7c3e28..c445a3876 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -74,7 +74,7 @@ export class MailBoxUserScreen extends AbstractScreen { FolderUserStore.currentFolder(folder); - MessageUserStore.listPage(page); + MessageUserStore.listPage(1 > page ? 1 : page); MessageUserStore.listSearch(search); MessageUserStore.listThreadUid(threadUid); @@ -119,23 +119,15 @@ export class MailBoxUserScreen extends AbstractScreen { */ routes() { const - fNormS = (request, vals) => { - if (request) { - vals[0] = decodeURI(pString(vals[0])); - vals[1] = pInt(vals[1]); - } else { - vals[0] = getFolderInboxName(); - vals[1] = 1; - } - return [vals[0], 1 > vals[1] ? 1 : vals[1], decodeURI(pString(vals[2]))]; - }, - fNormD = (request, vals) => - [decodeURI(request ? pString(vals[0]) : getFolderInboxName()), 1, decodeURI(pString(vals[1]))]; + folder = (request, vals) => request ? decodeURI(pString(vals[0])) : getFolderInboxName(), + fNormS = (request, vals) => [folder(request, vals), request ? pInt(vals[1]) : 1, decodeURI(pString(vals[2]))]; return [ - [/^([^/]*)$/, { 'normalize_': fNormS }], - [/^([a-zA-Z0-9~]+)\/(.+)\/?$/, { 'normalize_': fNormD }], - [/^([a-zA-Z0-9~]+)\/p([1-9][0-9]*)(?:\/(.+)\/?)?$/, { 'normalize_': fNormS }] + [/^([^/]*)$/, { normalize_: fNormS }], + [/^([a-zA-Z0-9~]+)\/(.+)\/?$/, { normalize_: (request, vals) => + [folder(request, vals), 1, decodeURI(pString(vals[1]))] + }], + [/^([a-zA-Z0-9~]+)\/p([1-9][0-9]*)(?:\/(.+)\/?)?$/, { normalize_: fNormS }] ]; } } From 581c89d5023bf4a08a7b1d562ed724aa8b92e25b Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 30 Mar 2021 11:48:22 +0200 Subject: [PATCH 27/90] Cleanup unused properties --- dev/Common/Links.js | 5 +- dev/View/User/MailBox/MessageList.js | 47 ++++++++----------- dev/View/User/MailBox/MessageView.js | 5 +- .../app/libraries/MailSo/Imap/ImapClient.php | 2 - 4 files changed, 24 insertions(+), 35 deletions(-) diff --git a/dev/Common/Links.js b/dev/Common/Links.js index 47928e239..c53d87b1d 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -179,12 +179,13 @@ export function mailBox(folder, page = 1, search = '', threadUid = '') { } if (1 < page) { - result = result.replace(/[/]+$/, '') + '/p' + page; + result = result.replace(/\/+$/, '') + '/p' + page; } if (search) { - result = result.replace(/[/]+$/, '') + '/' + encodeURI(search); + result = result.replace(/\/+$/, '') + '/' + encodeURI(search); } return result; } + diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index e5238ae75..f0824c5ac 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -71,7 +71,6 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.message = MessageUserStore.message; this.messageList = MessageUserStore.list; - this.messageListDisableAutoSelect = MessageUserStore.listDisableAutoSelect; this.folderList = FolderUserStore.folderList; this.sortSupported = FolderUserStore.sortSupported; @@ -79,22 +78,16 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.composeInEdit = AppUserStore.composeInEdit; this.leftPanelDisabled = leftPanelDisabled; - this.selectorMessageSelected = MessageUserStore.selectorMessageSelected; - this.selectorMessageFocused = MessageUserStore.selectorMessageFocused; this.isMessageSelected = MessageUserStore.isMessageSelected; this.messageListSearch = MessageUserStore.listSearch; - this.messageListThreadUid = MessageUserStore.listThreadUid; this.messageListError = MessageUserStore.listError; this.folderMenuForMove = FolderUserStore.folderMenuForMove; this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList; - this.mainMessageListSearch = MessageUserStore.mainMessageListSearch; - this.messageListEndFolder = MessageUserStore.listEndFolder; this.messageListEndThreadUid = MessageUserStore.listEndThreadUid; this.messageListCheckedOrSelected = MessageUserStore.listCheckedOrSelected; - this.messageListCheckedOrSelectedUidsWithSubMails = MessageUserStore.listCheckedOrSelectedUidsWithSubMails; this.messageListCompleteLoadingThrottle = MessageUserStore.listCompleteLoading; this.messageListCompleteLoadingThrottleForAnimation = MessageUserStore.listLoadingAnimation; @@ -139,7 +132,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { }, inputProxyMessageListSearch: { - read: this.mainMessageListSearch, + read: MessageUserStore.mainMessageListSearch, write: value => this.sLastSearchValue = value }, @@ -148,19 +141,19 @@ export class MessageListMailBoxUserView extends AbstractViewRight { return c && MessageUserStore.list.length > c; }, - hasMessages: () => 0 < this.messageList.length, + hasMessages: () => 0 < MessageUserStore.list.length, - isSpamFolder: () => (FolderUserStore.spamFolder() || 0) === this.messageListEndFolder(), + isSpamFolder: () => (FolderUserStore.spamFolder() || 0) === MessageUserStore.listEndFolder(), isSpamDisabled: () => UNUSED_OPTION_VALUE === FolderUserStore.spamFolder(), - isTrashFolder: () => (FolderUserStore.trashFolder() || 0) === this.messageListEndFolder(), + isTrashFolder: () => (FolderUserStore.trashFolder() || 0) === MessageUserStore.listEndFolder(), - isDraftFolder: () => (FolderUserStore.draftFolder() || 0) === this.messageListEndFolder(), + isDraftFolder: () => (FolderUserStore.draftFolder() || 0) === MessageUserStore.listEndFolder(), - isSentFolder: () => (FolderUserStore.sentFolder() || 0) === this.messageListEndFolder(), + isSentFolder: () => (FolderUserStore.sentFolder() || 0) === MessageUserStore.listEndFolder(), - isArchiveFolder: () => (FolderUserStore.archiveFolder() || 0) === this.messageListEndFolder(), + isArchiveFolder: () => (FolderUserStore.archiveFolder() || 0) === MessageUserStore.listEndFolder(), isArchiveDisabled: () => UNUSED_OPTION_VALUE === FolderUserStore.archiveFolder(), @@ -197,9 +190,9 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.quotaTooltip = this.quotaTooltip.bind(this); this.selector = new Selector( - this.messageList, - this.selectorMessageSelected, - this.selectorMessageFocused, + MessageUserStore.list, + MessageUserStore.selectorMessageSelected, + MessageUserStore.selectorMessageFocused, '.messageListItem .actionHandle', '.messageListItem .checkboxMessage', '.messageListItem.focused' @@ -224,7 +217,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { addEventListener('mailbox.message.show', e => { const sFolder = e.detail.Folder, sUid = e.detail.Uid; - const message = this.messageList.find( + const message = MessageUserStore.list.find( item => item && sFolder === item.folder && sUid === item.uid ); @@ -411,18 +404,18 @@ export class MessageListMailBoxUserView extends AbstractViewRight { } useAutoSelect() { - return !this.messageListDisableAutoSelect() - && !/is:unseen/.test(this.mainMessageListSearch()) + return !MessageUserStore.listDisableAutoSelect() + && !/is:unseen/.test(MessageUserStore.mainMessageListSearch()) && SettingsUserStore.usePreviewPane(); } searchEnterAction() { - this.mainMessageListSearch(this.sLastSearchValue); + MessageUserStore.mainMessageListSearch(this.sLastSearchValue); this.inputMessageListSearchFocus(false); } cancelSearch() { - this.mainMessageListSearch(''); + MessageUserStore.mainMessageListSearch(''); this.inputMessageListSearchFocus(false); } @@ -676,7 +669,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { !this.messageListSearchDesc() && !this.messageListError() && !this.messageListEndThreadUid() && - this.messageList.length && + MessageUserStore.list.length && (this.isSpamFolder() || this.isTrashFolder()) ); } @@ -776,9 +769,9 @@ export class MessageListMailBoxUserView extends AbstractViewRight { } shortcuts.add('t', '', [Scope.MessageList], () => { - let message = this.selectorMessageSelected(); + let message = MessageUserStore.selectorMessageSelected(); if (!message) { - message = this.selectorMessageFocused(); + message = MessageUserStore.selectorMessageFocused(); } if (message && 0 < message.threadsLen()) { @@ -861,7 +854,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { prefetchNextTick() { if (!this.bPrefetch && !ifvisible.now() && this.viewModelVisible) { - const message = this.messageList.find( + const message = MessageUserStore.list.find( item => item && !hasRequestedMessage(item.folder, item.uid) ); if (message) { @@ -886,7 +879,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { advancedSearchClick() { Settings.capa(Capa.SearchAdv) - && showScreenPopup(AdvancedSearchPopupView, [this.mainMessageListSearch()]); + && showScreenPopup(AdvancedSearchPopupView, [MessageUserStore.mainMessageListSearch()]); } quotaTooltip() { diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 939e0526b..ee1c6b610 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -71,8 +71,6 @@ class MessageViewMailBoxUserView extends AbstractViewRight { moreDropdownTrigger: false }); - this.pswp = null; - this.moveAction = moveAction; this.allowComposer = Settings.capa(Capa.Composer); @@ -84,7 +82,6 @@ class MessageViewMailBoxUserView extends AbstractViewRight { this.message = MessageUserStore.message; this.hasCheckedMessages = MessageUserStore.hasCheckedMessages; - this.messageListCheckedOrSelectedUidsWithSubMails = MessageUserStore.listCheckedOrSelectedUidsWithSubMails; this.messageLoadingThrottle = MessageUserStore.messageLoading; this.messagesBodiesDom = MessageUserStore.messagesBodiesDom; this.useThreads = SettingsUserStore.useThreads; @@ -717,7 +714,7 @@ class MessageViewMailBoxUserView extends AbstractViewRight { * @returns {string} */ printableCheckedMessageCount() { - const cnt = this.messageListCheckedOrSelectedUidsWithSubMails().length; + const cnt = MessageUserStore.listCheckedOrSelectedUidsWithSubMails().length; return 0 < cnt ? (100 > cnt ? cnt : '99+') : ''; } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index fa1a64084..2035deaaf 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -158,8 +158,6 @@ class ImapClient extends \MailSo\Net\NetClient $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($sLogin)); - $sPassword = $sPassword; - $this->sLogginedUser = $sLogin; // $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']); From 65a302d87f449c00a9a37ac8a12fa66b5a8e5d5d Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 31 Mar 2021 17:42:50 +0200 Subject: [PATCH 28/90] Replace obsolete IDNA2 with php intl idn_to_* https://www.php.net/intl.idn And a modified Symfony\Polyfill\Intl\Idn when php intl is not available. --- .../0.0.0/app/libraries/MailSo/Base/Utils.php | 17 +- .../libraries/MailSo/Vendors/Net/IDNA2.php | 3259 ---------- .../Vendors/Net/IDNA2CustomExceptions.php | 18 - .../v/0.0.0/app/libraries/snappymail/idn.php | 119 + .../app/libraries/snappymail/intl/LICENSE | 19 + .../app/libraries/snappymail/intl/idn.php | 1215 ++++ .../intl/unidata/canonicalComposition.php | 945 +++ .../intl/unidata/canonicalDecomposition.php | 2065 ++++++ .../intl/unidata/combiningClass.php | 876 +++ .../unidata/compatibilityDecomposition.php | 3695 +++++++++++ .../snappymail/intl/unidata/deviation.php | 8 + .../snappymail/intl/unidata/disallowed.php | 2638 ++++++++ .../intl/unidata/disallowed_STD3_mapped.php | 308 + .../intl/unidata/disallowed_STD3_valid.php | 71 + .../snappymail/intl/unidata/ignored.php | 273 + .../snappymail/intl/unidata/mapped.php | 5778 +++++++++++++++++ .../snappymail/intl/unidata/virama.php | 65 + 17 files changed, 18077 insertions(+), 3292 deletions(-) delete mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2.php delete mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2CustomExceptions.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/idn.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/LICENSE create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/idn.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalComposition.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalDecomposition.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/combiningClass.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/compatibilityDecomposition.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/deviation.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_mapped.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_valid.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/ignored.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/mapped.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/virama.php diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php index 9dc0fdb48..abbb19d7b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php @@ -1826,26 +1826,13 @@ END; return !empty($sIp) && $sIp === \filter_var($sIp, FILTER_VALIDATE_IP); } - private static function idn() : \Net - { - static $oIdn = null; - if (null === $oIdn) - { - include_once dirname(__DIR__).'/Vendors/Net/IDNA2.php'; - $oIdn = new \Net_IDNA2; - $oIdn->setParams('utf8', true); - } - - return $oIdn; - } - public static function IdnToUtf8(string $sStr, bool $bLowerIfAscii = false) : string { if (0 < \strlen($sStr) && \preg_match('/(^|\.|@)xn--/i', $sStr)) { try { - $sStr = self::idn()->decode($sStr); + $sStr = \SnappyMail\IDN::anyToUtf8($sStr); } catch (\Throwable $oException) {} } @@ -1869,7 +1856,7 @@ END; { try { - $sDomain = self::idn()->encode($sDomain); + $sDomain = \SnappyMail\IDN::anyToAscii($sDomain); } catch (\Throwable $oException) {} } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2.php b/snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2.php deleted file mode 100644 index c0a985e79..000000000 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2.php +++ /dev/null @@ -1,3259 +0,0 @@ - - * @author Matthias Sommerfeld - * @author Stefan Neufeind - * @version $Id: IDNA2.php 305344 2010-11-14 23:52:42Z neufeind $ - */ -class Net_IDNA2 -{ - // {{{ npdata - /** - * These Unicode codepoints are - * mapped to nothing, See RFC3454 for details - * - * @var array - */ - private static $_np_map_nothing = array( - 0xAD, - 0x34F, - 0x1806, - 0x180B, - 0x180C, - 0x180D, - 0x200B, - 0x200C, - 0x200D, - 0x2060, - 0xFE00, - 0xFE01, - 0xFE02, - 0xFE03, - 0xFE04, - 0xFE05, - 0xFE06, - 0xFE07, - 0xFE08, - 0xFE09, - 0xFE0A, - 0xFE0B, - 0xFE0C, - 0xFE0D, - 0xFE0E, - 0xFE0F, - 0xFEFF - ); - - /** - * Prohibited codepints - * - * @var array - */ - private static $_general_prohibited = array( - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 0xA, - 0xB, - 0xC, - 0xD, - 0xE, - 0xF, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - 0x16, - 0x17, - 0x18, - 0x19, - 0x1A, - 0x1B, - 0x1C, - 0x1D, - 0x1E, - 0x1F, - 0x20, - 0x21, - 0x22, - 0x23, - 0x24, - 0x25, - 0x26, - 0x27, - 0x28, - 0x29, - 0x2A, - 0x2B, - 0x2C, - 0x2F, - 0x3B, - 0x3C, - 0x3D, - 0x3E, - 0x3F, - 0x40, - 0x5B, - 0x5C, - 0x5D, - 0x5E, - 0x5F, - 0x60, - 0x7B, - 0x7C, - 0x7D, - 0x7E, - 0x7F, - 0x3002 - ); - - /** - * Codepints prohibited by Nameprep - * @var array - */ - private static $_np_prohibit = array( - 0xA0, - 0x1680, - 0x2000, - 0x2001, - 0x2002, - 0x2003, - 0x2004, - 0x2005, - 0x2006, - 0x2007, - 0x2008, - 0x2009, - 0x200A, - 0x200B, - 0x202F, - 0x205F, - 0x3000, - 0x6DD, - 0x70F, - 0x180E, - 0x200C, - 0x200D, - 0x2028, - 0x2029, - 0xFEFF, - 0xFFF9, - 0xFFFA, - 0xFFFB, - 0xFFFC, - 0xFFFE, - 0xFFFF, - 0x1FFFE, - 0x1FFFF, - 0x2FFFE, - 0x2FFFF, - 0x3FFFE, - 0x3FFFF, - 0x4FFFE, - 0x4FFFF, - 0x5FFFE, - 0x5FFFF, - 0x6FFFE, - 0x6FFFF, - 0x7FFFE, - 0x7FFFF, - 0x8FFFE, - 0x8FFFF, - 0x9FFFE, - 0x9FFFF, - 0xAFFFE, - 0xAFFFF, - 0xBFFFE, - 0xBFFFF, - 0xCFFFE, - 0xCFFFF, - 0xDFFFE, - 0xDFFFF, - 0xEFFFE, - 0xEFFFF, - 0xFFFFE, - 0xFFFFF, - 0x10FFFE, - 0x10FFFF, - 0xFFF9, - 0xFFFA, - 0xFFFB, - 0xFFFC, - 0xFFFD, - 0x340, - 0x341, - 0x200E, - 0x200F, - 0x202A, - 0x202B, - 0x202C, - 0x202D, - 0x202E, - 0x206A, - 0x206B, - 0x206C, - 0x206D, - 0x206E, - 0x206F, - 0xE0001 - ); - - /** - * Codepoint ranges prohibited by nameprep - * - * @var array - */ - private static $_np_prohibit_ranges = array( - array(0x80, 0x9F ), - array(0x2060, 0x206F ), - array(0x1D173, 0x1D17A ), - array(0xE000, 0xF8FF ), - array(0xF0000, 0xFFFFD ), - array(0x100000, 0x10FFFD), - array(0xFDD0, 0xFDEF ), - array(0xD800, 0xDFFF ), - array(0x2FF0, 0x2FFB ), - array(0xE0020, 0xE007F ) - ); - - /** - * Replacement mappings (casemapping, replacement sequences, ...) - * - * @var array - */ - private static $_np_replacemaps = array( - 0x41 => array(0x61), - 0x42 => array(0x62), - 0x43 => array(0x63), - 0x44 => array(0x64), - 0x45 => array(0x65), - 0x46 => array(0x66), - 0x47 => array(0x67), - 0x48 => array(0x68), - 0x49 => array(0x69), - 0x4A => array(0x6A), - 0x4B => array(0x6B), - 0x4C => array(0x6C), - 0x4D => array(0x6D), - 0x4E => array(0x6E), - 0x4F => array(0x6F), - 0x50 => array(0x70), - 0x51 => array(0x71), - 0x52 => array(0x72), - 0x53 => array(0x73), - 0x54 => array(0x74), - 0x55 => array(0x75), - 0x56 => array(0x76), - 0x57 => array(0x77), - 0x58 => array(0x78), - 0x59 => array(0x79), - 0x5A => array(0x7A), - 0xB5 => array(0x3BC), - 0xC0 => array(0xE0), - 0xC1 => array(0xE1), - 0xC2 => array(0xE2), - 0xC3 => array(0xE3), - 0xC4 => array(0xE4), - 0xC5 => array(0xE5), - 0xC6 => array(0xE6), - 0xC7 => array(0xE7), - 0xC8 => array(0xE8), - 0xC9 => array(0xE9), - 0xCA => array(0xEA), - 0xCB => array(0xEB), - 0xCC => array(0xEC), - 0xCD => array(0xED), - 0xCE => array(0xEE), - 0xCF => array(0xEF), - 0xD0 => array(0xF0), - 0xD1 => array(0xF1), - 0xD2 => array(0xF2), - 0xD3 => array(0xF3), - 0xD4 => array(0xF4), - 0xD5 => array(0xF5), - 0xD6 => array(0xF6), - 0xD8 => array(0xF8), - 0xD9 => array(0xF9), - 0xDA => array(0xFA), - 0xDB => array(0xFB), - 0xDC => array(0xFC), - 0xDD => array(0xFD), - 0xDE => array(0xFE), - 0xDF => array(0x73, 0x73), - 0x100 => array(0x101), - 0x102 => array(0x103), - 0x104 => array(0x105), - 0x106 => array(0x107), - 0x108 => array(0x109), - 0x10A => array(0x10B), - 0x10C => array(0x10D), - 0x10E => array(0x10F), - 0x110 => array(0x111), - 0x112 => array(0x113), - 0x114 => array(0x115), - 0x116 => array(0x117), - 0x118 => array(0x119), - 0x11A => array(0x11B), - 0x11C => array(0x11D), - 0x11E => array(0x11F), - 0x120 => array(0x121), - 0x122 => array(0x123), - 0x124 => array(0x125), - 0x126 => array(0x127), - 0x128 => array(0x129), - 0x12A => array(0x12B), - 0x12C => array(0x12D), - 0x12E => array(0x12F), - 0x130 => array(0x69, 0x307), - 0x132 => array(0x133), - 0x134 => array(0x135), - 0x136 => array(0x137), - 0x139 => array(0x13A), - 0x13B => array(0x13C), - 0x13D => array(0x13E), - 0x13F => array(0x140), - 0x141 => array(0x142), - 0x143 => array(0x144), - 0x145 => array(0x146), - 0x147 => array(0x148), - 0x149 => array(0x2BC, 0x6E), - 0x14A => array(0x14B), - 0x14C => array(0x14D), - 0x14E => array(0x14F), - 0x150 => array(0x151), - 0x152 => array(0x153), - 0x154 => array(0x155), - 0x156 => array(0x157), - 0x158 => array(0x159), - 0x15A => array(0x15B), - 0x15C => array(0x15D), - 0x15E => array(0x15F), - 0x160 => array(0x161), - 0x162 => array(0x163), - 0x164 => array(0x165), - 0x166 => array(0x167), - 0x168 => array(0x169), - 0x16A => array(0x16B), - 0x16C => array(0x16D), - 0x16E => array(0x16F), - 0x170 => array(0x171), - 0x172 => array(0x173), - 0x174 => array(0x175), - 0x176 => array(0x177), - 0x178 => array(0xFF), - 0x179 => array(0x17A), - 0x17B => array(0x17C), - 0x17D => array(0x17E), - 0x17F => array(0x73), - 0x181 => array(0x253), - 0x182 => array(0x183), - 0x184 => array(0x185), - 0x186 => array(0x254), - 0x187 => array(0x188), - 0x189 => array(0x256), - 0x18A => array(0x257), - 0x18B => array(0x18C), - 0x18E => array(0x1DD), - 0x18F => array(0x259), - 0x190 => array(0x25B), - 0x191 => array(0x192), - 0x193 => array(0x260), - 0x194 => array(0x263), - 0x196 => array(0x269), - 0x197 => array(0x268), - 0x198 => array(0x199), - 0x19C => array(0x26F), - 0x19D => array(0x272), - 0x19F => array(0x275), - 0x1A0 => array(0x1A1), - 0x1A2 => array(0x1A3), - 0x1A4 => array(0x1A5), - 0x1A6 => array(0x280), - 0x1A7 => array(0x1A8), - 0x1A9 => array(0x283), - 0x1AC => array(0x1AD), - 0x1AE => array(0x288), - 0x1AF => array(0x1B0), - 0x1B1 => array(0x28A), - 0x1B2 => array(0x28B), - 0x1B3 => array(0x1B4), - 0x1B5 => array(0x1B6), - 0x1B7 => array(0x292), - 0x1B8 => array(0x1B9), - 0x1BC => array(0x1BD), - 0x1C4 => array(0x1C6), - 0x1C5 => array(0x1C6), - 0x1C7 => array(0x1C9), - 0x1C8 => array(0x1C9), - 0x1CA => array(0x1CC), - 0x1CB => array(0x1CC), - 0x1CD => array(0x1CE), - 0x1CF => array(0x1D0), - 0x1D1 => array(0x1D2), - 0x1D3 => array(0x1D4), - 0x1D5 => array(0x1D6), - 0x1D7 => array(0x1D8), - 0x1D9 => array(0x1DA), - 0x1DB => array(0x1DC), - 0x1DE => array(0x1DF), - 0x1E0 => array(0x1E1), - 0x1E2 => array(0x1E3), - 0x1E4 => array(0x1E5), - 0x1E6 => array(0x1E7), - 0x1E8 => array(0x1E9), - 0x1EA => array(0x1EB), - 0x1EC => array(0x1ED), - 0x1EE => array(0x1EF), - 0x1F0 => array(0x6A, 0x30C), - 0x1F1 => array(0x1F3), - 0x1F2 => array(0x1F3), - 0x1F4 => array(0x1F5), - 0x1F6 => array(0x195), - 0x1F7 => array(0x1BF), - 0x1F8 => array(0x1F9), - 0x1FA => array(0x1FB), - 0x1FC => array(0x1FD), - 0x1FE => array(0x1FF), - 0x200 => array(0x201), - 0x202 => array(0x203), - 0x204 => array(0x205), - 0x206 => array(0x207), - 0x208 => array(0x209), - 0x20A => array(0x20B), - 0x20C => array(0x20D), - 0x20E => array(0x20F), - 0x210 => array(0x211), - 0x212 => array(0x213), - 0x214 => array(0x215), - 0x216 => array(0x217), - 0x218 => array(0x219), - 0x21A => array(0x21B), - 0x21C => array(0x21D), - 0x21E => array(0x21F), - 0x220 => array(0x19E), - 0x222 => array(0x223), - 0x224 => array(0x225), - 0x226 => array(0x227), - 0x228 => array(0x229), - 0x22A => array(0x22B), - 0x22C => array(0x22D), - 0x22E => array(0x22F), - 0x230 => array(0x231), - 0x232 => array(0x233), - 0x345 => array(0x3B9), - 0x37A => array(0x20, 0x3B9), - 0x386 => array(0x3AC), - 0x388 => array(0x3AD), - 0x389 => array(0x3AE), - 0x38A => array(0x3AF), - 0x38C => array(0x3CC), - 0x38E => array(0x3CD), - 0x38F => array(0x3CE), - 0x390 => array(0x3B9, 0x308, 0x301), - 0x391 => array(0x3B1), - 0x392 => array(0x3B2), - 0x393 => array(0x3B3), - 0x394 => array(0x3B4), - 0x395 => array(0x3B5), - 0x396 => array(0x3B6), - 0x397 => array(0x3B7), - 0x398 => array(0x3B8), - 0x399 => array(0x3B9), - 0x39A => array(0x3BA), - 0x39B => array(0x3BB), - 0x39C => array(0x3BC), - 0x39D => array(0x3BD), - 0x39E => array(0x3BE), - 0x39F => array(0x3BF), - 0x3A0 => array(0x3C0), - 0x3A1 => array(0x3C1), - 0x3A3 => array(0x3C3), - 0x3A4 => array(0x3C4), - 0x3A5 => array(0x3C5), - 0x3A6 => array(0x3C6), - 0x3A7 => array(0x3C7), - 0x3A8 => array(0x3C8), - 0x3A9 => array(0x3C9), - 0x3AA => array(0x3CA), - 0x3AB => array(0x3CB), - 0x3B0 => array(0x3C5, 0x308, 0x301), - 0x3C2 => array(0x3C3), - 0x3D0 => array(0x3B2), - 0x3D1 => array(0x3B8), - 0x3D2 => array(0x3C5), - 0x3D3 => array(0x3CD), - 0x3D4 => array(0x3CB), - 0x3D5 => array(0x3C6), - 0x3D6 => array(0x3C0), - 0x3D8 => array(0x3D9), - 0x3DA => array(0x3DB), - 0x3DC => array(0x3DD), - 0x3DE => array(0x3DF), - 0x3E0 => array(0x3E1), - 0x3E2 => array(0x3E3), - 0x3E4 => array(0x3E5), - 0x3E6 => array(0x3E7), - 0x3E8 => array(0x3E9), - 0x3EA => array(0x3EB), - 0x3EC => array(0x3ED), - 0x3EE => array(0x3EF), - 0x3F0 => array(0x3BA), - 0x3F1 => array(0x3C1), - 0x3F2 => array(0x3C3), - 0x3F4 => array(0x3B8), - 0x3F5 => array(0x3B5), - 0x400 => array(0x450), - 0x401 => array(0x451), - 0x402 => array(0x452), - 0x403 => array(0x453), - 0x404 => array(0x454), - 0x405 => array(0x455), - 0x406 => array(0x456), - 0x407 => array(0x457), - 0x408 => array(0x458), - 0x409 => array(0x459), - 0x40A => array(0x45A), - 0x40B => array(0x45B), - 0x40C => array(0x45C), - 0x40D => array(0x45D), - 0x40E => array(0x45E), - 0x40F => array(0x45F), - 0x410 => array(0x430), - 0x411 => array(0x431), - 0x412 => array(0x432), - 0x413 => array(0x433), - 0x414 => array(0x434), - 0x415 => array(0x435), - 0x416 => array(0x436), - 0x417 => array(0x437), - 0x418 => array(0x438), - 0x419 => array(0x439), - 0x41A => array(0x43A), - 0x41B => array(0x43B), - 0x41C => array(0x43C), - 0x41D => array(0x43D), - 0x41E => array(0x43E), - 0x41F => array(0x43F), - 0x420 => array(0x440), - 0x421 => array(0x441), - 0x422 => array(0x442), - 0x423 => array(0x443), - 0x424 => array(0x444), - 0x425 => array(0x445), - 0x426 => array(0x446), - 0x427 => array(0x447), - 0x428 => array(0x448), - 0x429 => array(0x449), - 0x42A => array(0x44A), - 0x42B => array(0x44B), - 0x42C => array(0x44C), - 0x42D => array(0x44D), - 0x42E => array(0x44E), - 0x42F => array(0x44F), - 0x460 => array(0x461), - 0x462 => array(0x463), - 0x464 => array(0x465), - 0x466 => array(0x467), - 0x468 => array(0x469), - 0x46A => array(0x46B), - 0x46C => array(0x46D), - 0x46E => array(0x46F), - 0x470 => array(0x471), - 0x472 => array(0x473), - 0x474 => array(0x475), - 0x476 => array(0x477), - 0x478 => array(0x479), - 0x47A => array(0x47B), - 0x47C => array(0x47D), - 0x47E => array(0x47F), - 0x480 => array(0x481), - 0x48A => array(0x48B), - 0x48C => array(0x48D), - 0x48E => array(0x48F), - 0x490 => array(0x491), - 0x492 => array(0x493), - 0x494 => array(0x495), - 0x496 => array(0x497), - 0x498 => array(0x499), - 0x49A => array(0x49B), - 0x49C => array(0x49D), - 0x49E => array(0x49F), - 0x4A0 => array(0x4A1), - 0x4A2 => array(0x4A3), - 0x4A4 => array(0x4A5), - 0x4A6 => array(0x4A7), - 0x4A8 => array(0x4A9), - 0x4AA => array(0x4AB), - 0x4AC => array(0x4AD), - 0x4AE => array(0x4AF), - 0x4B0 => array(0x4B1), - 0x4B2 => array(0x4B3), - 0x4B4 => array(0x4B5), - 0x4B6 => array(0x4B7), - 0x4B8 => array(0x4B9), - 0x4BA => array(0x4BB), - 0x4BC => array(0x4BD), - 0x4BE => array(0x4BF), - 0x4C1 => array(0x4C2), - 0x4C3 => array(0x4C4), - 0x4C5 => array(0x4C6), - 0x4C7 => array(0x4C8), - 0x4C9 => array(0x4CA), - 0x4CB => array(0x4CC), - 0x4CD => array(0x4CE), - 0x4D0 => array(0x4D1), - 0x4D2 => array(0x4D3), - 0x4D4 => array(0x4D5), - 0x4D6 => array(0x4D7), - 0x4D8 => array(0x4D9), - 0x4DA => array(0x4DB), - 0x4DC => array(0x4DD), - 0x4DE => array(0x4DF), - 0x4E0 => array(0x4E1), - 0x4E2 => array(0x4E3), - 0x4E4 => array(0x4E5), - 0x4E6 => array(0x4E7), - 0x4E8 => array(0x4E9), - 0x4EA => array(0x4EB), - 0x4EC => array(0x4ED), - 0x4EE => array(0x4EF), - 0x4F0 => array(0x4F1), - 0x4F2 => array(0x4F3), - 0x4F4 => array(0x4F5), - 0x4F8 => array(0x4F9), - 0x500 => array(0x501), - 0x502 => array(0x503), - 0x504 => array(0x505), - 0x506 => array(0x507), - 0x508 => array(0x509), - 0x50A => array(0x50B), - 0x50C => array(0x50D), - 0x50E => array(0x50F), - 0x531 => array(0x561), - 0x532 => array(0x562), - 0x533 => array(0x563), - 0x534 => array(0x564), - 0x535 => array(0x565), - 0x536 => array(0x566), - 0x537 => array(0x567), - 0x538 => array(0x568), - 0x539 => array(0x569), - 0x53A => array(0x56A), - 0x53B => array(0x56B), - 0x53C => array(0x56C), - 0x53D => array(0x56D), - 0x53E => array(0x56E), - 0x53F => array(0x56F), - 0x540 => array(0x570), - 0x541 => array(0x571), - 0x542 => array(0x572), - 0x543 => array(0x573), - 0x544 => array(0x574), - 0x545 => array(0x575), - 0x546 => array(0x576), - 0x547 => array(0x577), - 0x548 => array(0x578), - 0x549 => array(0x579), - 0x54A => array(0x57A), - 0x54B => array(0x57B), - 0x54C => array(0x57C), - 0x54D => array(0x57D), - 0x54E => array(0x57E), - 0x54F => array(0x57F), - 0x550 => array(0x580), - 0x551 => array(0x581), - 0x552 => array(0x582), - 0x553 => array(0x583), - 0x554 => array(0x584), - 0x555 => array(0x585), - 0x556 => array(0x586), - 0x587 => array(0x565, 0x582), - 0x1E00 => array(0x1E01), - 0x1E02 => array(0x1E03), - 0x1E04 => array(0x1E05), - 0x1E06 => array(0x1E07), - 0x1E08 => array(0x1E09), - 0x1E0A => array(0x1E0B), - 0x1E0C => array(0x1E0D), - 0x1E0E => array(0x1E0F), - 0x1E10 => array(0x1E11), - 0x1E12 => array(0x1E13), - 0x1E14 => array(0x1E15), - 0x1E16 => array(0x1E17), - 0x1E18 => array(0x1E19), - 0x1E1A => array(0x1E1B), - 0x1E1C => array(0x1E1D), - 0x1E1E => array(0x1E1F), - 0x1E20 => array(0x1E21), - 0x1E22 => array(0x1E23), - 0x1E24 => array(0x1E25), - 0x1E26 => array(0x1E27), - 0x1E28 => array(0x1E29), - 0x1E2A => array(0x1E2B), - 0x1E2C => array(0x1E2D), - 0x1E2E => array(0x1E2F), - 0x1E30 => array(0x1E31), - 0x1E32 => array(0x1E33), - 0x1E34 => array(0x1E35), - 0x1E36 => array(0x1E37), - 0x1E38 => array(0x1E39), - 0x1E3A => array(0x1E3B), - 0x1E3C => array(0x1E3D), - 0x1E3E => array(0x1E3F), - 0x1E40 => array(0x1E41), - 0x1E42 => array(0x1E43), - 0x1E44 => array(0x1E45), - 0x1E46 => array(0x1E47), - 0x1E48 => array(0x1E49), - 0x1E4A => array(0x1E4B), - 0x1E4C => array(0x1E4D), - 0x1E4E => array(0x1E4F), - 0x1E50 => array(0x1E51), - 0x1E52 => array(0x1E53), - 0x1E54 => array(0x1E55), - 0x1E56 => array(0x1E57), - 0x1E58 => array(0x1E59), - 0x1E5A => array(0x1E5B), - 0x1E5C => array(0x1E5D), - 0x1E5E => array(0x1E5F), - 0x1E60 => array(0x1E61), - 0x1E62 => array(0x1E63), - 0x1E64 => array(0x1E65), - 0x1E66 => array(0x1E67), - 0x1E68 => array(0x1E69), - 0x1E6A => array(0x1E6B), - 0x1E6C => array(0x1E6D), - 0x1E6E => array(0x1E6F), - 0x1E70 => array(0x1E71), - 0x1E72 => array(0x1E73), - 0x1E74 => array(0x1E75), - 0x1E76 => array(0x1E77), - 0x1E78 => array(0x1E79), - 0x1E7A => array(0x1E7B), - 0x1E7C => array(0x1E7D), - 0x1E7E => array(0x1E7F), - 0x1E80 => array(0x1E81), - 0x1E82 => array(0x1E83), - 0x1E84 => array(0x1E85), - 0x1E86 => array(0x1E87), - 0x1E88 => array(0x1E89), - 0x1E8A => array(0x1E8B), - 0x1E8C => array(0x1E8D), - 0x1E8E => array(0x1E8F), - 0x1E90 => array(0x1E91), - 0x1E92 => array(0x1E93), - 0x1E94 => array(0x1E95), - 0x1E96 => array(0x68, 0x331), - 0x1E97 => array(0x74, 0x308), - 0x1E98 => array(0x77, 0x30A), - 0x1E99 => array(0x79, 0x30A), - 0x1E9A => array(0x61, 0x2BE), - 0x1E9B => array(0x1E61), - 0x1EA0 => array(0x1EA1), - 0x1EA2 => array(0x1EA3), - 0x1EA4 => array(0x1EA5), - 0x1EA6 => array(0x1EA7), - 0x1EA8 => array(0x1EA9), - 0x1EAA => array(0x1EAB), - 0x1EAC => array(0x1EAD), - 0x1EAE => array(0x1EAF), - 0x1EB0 => array(0x1EB1), - 0x1EB2 => array(0x1EB3), - 0x1EB4 => array(0x1EB5), - 0x1EB6 => array(0x1EB7), - 0x1EB8 => array(0x1EB9), - 0x1EBA => array(0x1EBB), - 0x1EBC => array(0x1EBD), - 0x1EBE => array(0x1EBF), - 0x1EC0 => array(0x1EC1), - 0x1EC2 => array(0x1EC3), - 0x1EC4 => array(0x1EC5), - 0x1EC6 => array(0x1EC7), - 0x1EC8 => array(0x1EC9), - 0x1ECA => array(0x1ECB), - 0x1ECC => array(0x1ECD), - 0x1ECE => array(0x1ECF), - 0x1ED0 => array(0x1ED1), - 0x1ED2 => array(0x1ED3), - 0x1ED4 => array(0x1ED5), - 0x1ED6 => array(0x1ED7), - 0x1ED8 => array(0x1ED9), - 0x1EDA => array(0x1EDB), - 0x1EDC => array(0x1EDD), - 0x1EDE => array(0x1EDF), - 0x1EE0 => array(0x1EE1), - 0x1EE2 => array(0x1EE3), - 0x1EE4 => array(0x1EE5), - 0x1EE6 => array(0x1EE7), - 0x1EE8 => array(0x1EE9), - 0x1EEA => array(0x1EEB), - 0x1EEC => array(0x1EED), - 0x1EEE => array(0x1EEF), - 0x1EF0 => array(0x1EF1), - 0x1EF2 => array(0x1EF3), - 0x1EF4 => array(0x1EF5), - 0x1EF6 => array(0x1EF7), - 0x1EF8 => array(0x1EF9), - 0x1F08 => array(0x1F00), - 0x1F09 => array(0x1F01), - 0x1F0A => array(0x1F02), - 0x1F0B => array(0x1F03), - 0x1F0C => array(0x1F04), - 0x1F0D => array(0x1F05), - 0x1F0E => array(0x1F06), - 0x1F0F => array(0x1F07), - 0x1F18 => array(0x1F10), - 0x1F19 => array(0x1F11), - 0x1F1A => array(0x1F12), - 0x1F1B => array(0x1F13), - 0x1F1C => array(0x1F14), - 0x1F1D => array(0x1F15), - 0x1F28 => array(0x1F20), - 0x1F29 => array(0x1F21), - 0x1F2A => array(0x1F22), - 0x1F2B => array(0x1F23), - 0x1F2C => array(0x1F24), - 0x1F2D => array(0x1F25), - 0x1F2E => array(0x1F26), - 0x1F2F => array(0x1F27), - 0x1F38 => array(0x1F30), - 0x1F39 => array(0x1F31), - 0x1F3A => array(0x1F32), - 0x1F3B => array(0x1F33), - 0x1F3C => array(0x1F34), - 0x1F3D => array(0x1F35), - 0x1F3E => array(0x1F36), - 0x1F3F => array(0x1F37), - 0x1F48 => array(0x1F40), - 0x1F49 => array(0x1F41), - 0x1F4A => array(0x1F42), - 0x1F4B => array(0x1F43), - 0x1F4C => array(0x1F44), - 0x1F4D => array(0x1F45), - 0x1F50 => array(0x3C5, 0x313), - 0x1F52 => array(0x3C5, 0x313, 0x300), - 0x1F54 => array(0x3C5, 0x313, 0x301), - 0x1F56 => array(0x3C5, 0x313, 0x342), - 0x1F59 => array(0x1F51), - 0x1F5B => array(0x1F53), - 0x1F5D => array(0x1F55), - 0x1F5F => array(0x1F57), - 0x1F68 => array(0x1F60), - 0x1F69 => array(0x1F61), - 0x1F6A => array(0x1F62), - 0x1F6B => array(0x1F63), - 0x1F6C => array(0x1F64), - 0x1F6D => array(0x1F65), - 0x1F6E => array(0x1F66), - 0x1F6F => array(0x1F67), - 0x1F80 => array(0x1F00, 0x3B9), - 0x1F81 => array(0x1F01, 0x3B9), - 0x1F82 => array(0x1F02, 0x3B9), - 0x1F83 => array(0x1F03, 0x3B9), - 0x1F84 => array(0x1F04, 0x3B9), - 0x1F85 => array(0x1F05, 0x3B9), - 0x1F86 => array(0x1F06, 0x3B9), - 0x1F87 => array(0x1F07, 0x3B9), - 0x1F88 => array(0x1F00, 0x3B9), - 0x1F89 => array(0x1F01, 0x3B9), - 0x1F8A => array(0x1F02, 0x3B9), - 0x1F8B => array(0x1F03, 0x3B9), - 0x1F8C => array(0x1F04, 0x3B9), - 0x1F8D => array(0x1F05, 0x3B9), - 0x1F8E => array(0x1F06, 0x3B9), - 0x1F8F => array(0x1F07, 0x3B9), - 0x1F90 => array(0x1F20, 0x3B9), - 0x1F91 => array(0x1F21, 0x3B9), - 0x1F92 => array(0x1F22, 0x3B9), - 0x1F93 => array(0x1F23, 0x3B9), - 0x1F94 => array(0x1F24, 0x3B9), - 0x1F95 => array(0x1F25, 0x3B9), - 0x1F96 => array(0x1F26, 0x3B9), - 0x1F97 => array(0x1F27, 0x3B9), - 0x1F98 => array(0x1F20, 0x3B9), - 0x1F99 => array(0x1F21, 0x3B9), - 0x1F9A => array(0x1F22, 0x3B9), - 0x1F9B => array(0x1F23, 0x3B9), - 0x1F9C => array(0x1F24, 0x3B9), - 0x1F9D => array(0x1F25, 0x3B9), - 0x1F9E => array(0x1F26, 0x3B9), - 0x1F9F => array(0x1F27, 0x3B9), - 0x1FA0 => array(0x1F60, 0x3B9), - 0x1FA1 => array(0x1F61, 0x3B9), - 0x1FA2 => array(0x1F62, 0x3B9), - 0x1FA3 => array(0x1F63, 0x3B9), - 0x1FA4 => array(0x1F64, 0x3B9), - 0x1FA5 => array(0x1F65, 0x3B9), - 0x1FA6 => array(0x1F66, 0x3B9), - 0x1FA7 => array(0x1F67, 0x3B9), - 0x1FA8 => array(0x1F60, 0x3B9), - 0x1FA9 => array(0x1F61, 0x3B9), - 0x1FAA => array(0x1F62, 0x3B9), - 0x1FAB => array(0x1F63, 0x3B9), - 0x1FAC => array(0x1F64, 0x3B9), - 0x1FAD => array(0x1F65, 0x3B9), - 0x1FAE => array(0x1F66, 0x3B9), - 0x1FAF => array(0x1F67, 0x3B9), - 0x1FB2 => array(0x1F70, 0x3B9), - 0x1FB3 => array(0x3B1, 0x3B9), - 0x1FB4 => array(0x3AC, 0x3B9), - 0x1FB6 => array(0x3B1, 0x342), - 0x1FB7 => array(0x3B1, 0x342, 0x3B9), - 0x1FB8 => array(0x1FB0), - 0x1FB9 => array(0x1FB1), - 0x1FBA => array(0x1F70), - 0x1FBB => array(0x1F71), - 0x1FBC => array(0x3B1, 0x3B9), - 0x1FBE => array(0x3B9), - 0x1FC2 => array(0x1F74, 0x3B9), - 0x1FC3 => array(0x3B7, 0x3B9), - 0x1FC4 => array(0x3AE, 0x3B9), - 0x1FC6 => array(0x3B7, 0x342), - 0x1FC7 => array(0x3B7, 0x342, 0x3B9), - 0x1FC8 => array(0x1F72), - 0x1FC9 => array(0x1F73), - 0x1FCA => array(0x1F74), - 0x1FCB => array(0x1F75), - 0x1FCC => array(0x3B7, 0x3B9), - 0x1FD2 => array(0x3B9, 0x308, 0x300), - 0x1FD3 => array(0x3B9, 0x308, 0x301), - 0x1FD6 => array(0x3B9, 0x342), - 0x1FD7 => array(0x3B9, 0x308, 0x342), - 0x1FD8 => array(0x1FD0), - 0x1FD9 => array(0x1FD1), - 0x1FDA => array(0x1F76), - 0x1FDB => array(0x1F77), - 0x1FE2 => array(0x3C5, 0x308, 0x300), - 0x1FE3 => array(0x3C5, 0x308, 0x301), - 0x1FE4 => array(0x3C1, 0x313), - 0x1FE6 => array(0x3C5, 0x342), - 0x1FE7 => array(0x3C5, 0x308, 0x342), - 0x1FE8 => array(0x1FE0), - 0x1FE9 => array(0x1FE1), - 0x1FEA => array(0x1F7A), - 0x1FEB => array(0x1F7B), - 0x1FEC => array(0x1FE5), - 0x1FF2 => array(0x1F7C, 0x3B9), - 0x1FF3 => array(0x3C9, 0x3B9), - 0x1FF4 => array(0x3CE, 0x3B9), - 0x1FF6 => array(0x3C9, 0x342), - 0x1FF7 => array(0x3C9, 0x342, 0x3B9), - 0x1FF8 => array(0x1F78), - 0x1FF9 => array(0x1F79), - 0x1FFA => array(0x1F7C), - 0x1FFB => array(0x1F7D), - 0x1FFC => array(0x3C9, 0x3B9), - 0x20A8 => array(0x72, 0x73), - 0x2102 => array(0x63), - 0x2103 => array(0xB0, 0x63), - 0x2107 => array(0x25B), - 0x2109 => array(0xB0, 0x66), - 0x210B => array(0x68), - 0x210C => array(0x68), - 0x210D => array(0x68), - 0x2110 => array(0x69), - 0x2111 => array(0x69), - 0x2112 => array(0x6C), - 0x2115 => array(0x6E), - 0x2116 => array(0x6E, 0x6F), - 0x2119 => array(0x70), - 0x211A => array(0x71), - 0x211B => array(0x72), - 0x211C => array(0x72), - 0x211D => array(0x72), - 0x2120 => array(0x73, 0x6D), - 0x2121 => array(0x74, 0x65, 0x6C), - 0x2122 => array(0x74, 0x6D), - 0x2124 => array(0x7A), - 0x2126 => array(0x3C9), - 0x2128 => array(0x7A), - 0x212A => array(0x6B), - 0x212B => array(0xE5), - 0x212C => array(0x62), - 0x212D => array(0x63), - 0x2130 => array(0x65), - 0x2131 => array(0x66), - 0x2133 => array(0x6D), - 0x213E => array(0x3B3), - 0x213F => array(0x3C0), - 0x2145 => array(0x64), - 0x2160 => array(0x2170), - 0x2161 => array(0x2171), - 0x2162 => array(0x2172), - 0x2163 => array(0x2173), - 0x2164 => array(0x2174), - 0x2165 => array(0x2175), - 0x2166 => array(0x2176), - 0x2167 => array(0x2177), - 0x2168 => array(0x2178), - 0x2169 => array(0x2179), - 0x216A => array(0x217A), - 0x216B => array(0x217B), - 0x216C => array(0x217C), - 0x216D => array(0x217D), - 0x216E => array(0x217E), - 0x216F => array(0x217F), - 0x24B6 => array(0x24D0), - 0x24B7 => array(0x24D1), - 0x24B8 => array(0x24D2), - 0x24B9 => array(0x24D3), - 0x24BA => array(0x24D4), - 0x24BB => array(0x24D5), - 0x24BC => array(0x24D6), - 0x24BD => array(0x24D7), - 0x24BE => array(0x24D8), - 0x24BF => array(0x24D9), - 0x24C0 => array(0x24DA), - 0x24C1 => array(0x24DB), - 0x24C2 => array(0x24DC), - 0x24C3 => array(0x24DD), - 0x24C4 => array(0x24DE), - 0x24C5 => array(0x24DF), - 0x24C6 => array(0x24E0), - 0x24C7 => array(0x24E1), - 0x24C8 => array(0x24E2), - 0x24C9 => array(0x24E3), - 0x24CA => array(0x24E4), - 0x24CB => array(0x24E5), - 0x24CC => array(0x24E6), - 0x24CD => array(0x24E7), - 0x24CE => array(0x24E8), - 0x24CF => array(0x24E9), - 0x3371 => array(0x68, 0x70, 0x61), - 0x3373 => array(0x61, 0x75), - 0x3375 => array(0x6F, 0x76), - 0x3380 => array(0x70, 0x61), - 0x3381 => array(0x6E, 0x61), - 0x3382 => array(0x3BC, 0x61), - 0x3383 => array(0x6D, 0x61), - 0x3384 => array(0x6B, 0x61), - 0x3385 => array(0x6B, 0x62), - 0x3386 => array(0x6D, 0x62), - 0x3387 => array(0x67, 0x62), - 0x338A => array(0x70, 0x66), - 0x338B => array(0x6E, 0x66), - 0x338C => array(0x3BC, 0x66), - 0x3390 => array(0x68, 0x7A), - 0x3391 => array(0x6B, 0x68, 0x7A), - 0x3392 => array(0x6D, 0x68, 0x7A), - 0x3393 => array(0x67, 0x68, 0x7A), - 0x3394 => array(0x74, 0x68, 0x7A), - 0x33A9 => array(0x70, 0x61), - 0x33AA => array(0x6B, 0x70, 0x61), - 0x33AB => array(0x6D, 0x70, 0x61), - 0x33AC => array(0x67, 0x70, 0x61), - 0x33B4 => array(0x70, 0x76), - 0x33B5 => array(0x6E, 0x76), - 0x33B6 => array(0x3BC, 0x76), - 0x33B7 => array(0x6D, 0x76), - 0x33B8 => array(0x6B, 0x76), - 0x33B9 => array(0x6D, 0x76), - 0x33BA => array(0x70, 0x77), - 0x33BB => array(0x6E, 0x77), - 0x33BC => array(0x3BC, 0x77), - 0x33BD => array(0x6D, 0x77), - 0x33BE => array(0x6B, 0x77), - 0x33BF => array(0x6D, 0x77), - 0x33C0 => array(0x6B, 0x3C9), - 0x33C1 => array(0x6D, 0x3C9), - /* 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ - 0x33C3 => array(0x62, 0x71), - 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), - 0x33C7 => array(0x63, 0x6F, 0x2E), - 0x33C8 => array(0x64, 0x62), - 0x33C9 => array(0x67, 0x79), - 0x33CB => array(0x68, 0x70), - 0x33CD => array(0x6B, 0x6B), - 0x33CE => array(0x6B, 0x6D), - 0x33D7 => array(0x70, 0x68), - 0x33D9 => array(0x70, 0x70, 0x6D), - 0x33DA => array(0x70, 0x72), - 0x33DC => array(0x73, 0x76), - 0x33DD => array(0x77, 0x62), - 0xFB00 => array(0x66, 0x66), - 0xFB01 => array(0x66, 0x69), - 0xFB02 => array(0x66, 0x6C), - 0xFB03 => array(0x66, 0x66, 0x69), - 0xFB04 => array(0x66, 0x66, 0x6C), - 0xFB05 => array(0x73, 0x74), - 0xFB06 => array(0x73, 0x74), - 0xFB13 => array(0x574, 0x576), - 0xFB14 => array(0x574, 0x565), - 0xFB15 => array(0x574, 0x56B), - 0xFB16 => array(0x57E, 0x576), - 0xFB17 => array(0x574, 0x56D), - 0xFF21 => array(0xFF41), - 0xFF22 => array(0xFF42), - 0xFF23 => array(0xFF43), - 0xFF24 => array(0xFF44), - 0xFF25 => array(0xFF45), - 0xFF26 => array(0xFF46), - 0xFF27 => array(0xFF47), - 0xFF28 => array(0xFF48), - 0xFF29 => array(0xFF49), - 0xFF2A => array(0xFF4A), - 0xFF2B => array(0xFF4B), - 0xFF2C => array(0xFF4C), - 0xFF2D => array(0xFF4D), - 0xFF2E => array(0xFF4E), - 0xFF2F => array(0xFF4F), - 0xFF30 => array(0xFF50), - 0xFF31 => array(0xFF51), - 0xFF32 => array(0xFF52), - 0xFF33 => array(0xFF53), - 0xFF34 => array(0xFF54), - 0xFF35 => array(0xFF55), - 0xFF36 => array(0xFF56), - 0xFF37 => array(0xFF57), - 0xFF38 => array(0xFF58), - 0xFF39 => array(0xFF59), - 0xFF3A => array(0xFF5A), - 0x10400 => array(0x10428), - 0x10401 => array(0x10429), - 0x10402 => array(0x1042A), - 0x10403 => array(0x1042B), - 0x10404 => array(0x1042C), - 0x10405 => array(0x1042D), - 0x10406 => array(0x1042E), - 0x10407 => array(0x1042F), - 0x10408 => array(0x10430), - 0x10409 => array(0x10431), - 0x1040A => array(0x10432), - 0x1040B => array(0x10433), - 0x1040C => array(0x10434), - 0x1040D => array(0x10435), - 0x1040E => array(0x10436), - 0x1040F => array(0x10437), - 0x10410 => array(0x10438), - 0x10411 => array(0x10439), - 0x10412 => array(0x1043A), - 0x10413 => array(0x1043B), - 0x10414 => array(0x1043C), - 0x10415 => array(0x1043D), - 0x10416 => array(0x1043E), - 0x10417 => array(0x1043F), - 0x10418 => array(0x10440), - 0x10419 => array(0x10441), - 0x1041A => array(0x10442), - 0x1041B => array(0x10443), - 0x1041C => array(0x10444), - 0x1041D => array(0x10445), - 0x1041E => array(0x10446), - 0x1041F => array(0x10447), - 0x10420 => array(0x10448), - 0x10421 => array(0x10449), - 0x10422 => array(0x1044A), - 0x10423 => array(0x1044B), - 0x10424 => array(0x1044C), - 0x10425 => array(0x1044D), - 0x1D400 => array(0x61), - 0x1D401 => array(0x62), - 0x1D402 => array(0x63), - 0x1D403 => array(0x64), - 0x1D404 => array(0x65), - 0x1D405 => array(0x66), - 0x1D406 => array(0x67), - 0x1D407 => array(0x68), - 0x1D408 => array(0x69), - 0x1D409 => array(0x6A), - 0x1D40A => array(0x6B), - 0x1D40B => array(0x6C), - 0x1D40C => array(0x6D), - 0x1D40D => array(0x6E), - 0x1D40E => array(0x6F), - 0x1D40F => array(0x70), - 0x1D410 => array(0x71), - 0x1D411 => array(0x72), - 0x1D412 => array(0x73), - 0x1D413 => array(0x74), - 0x1D414 => array(0x75), - 0x1D415 => array(0x76), - 0x1D416 => array(0x77), - 0x1D417 => array(0x78), - 0x1D418 => array(0x79), - 0x1D419 => array(0x7A), - 0x1D434 => array(0x61), - 0x1D435 => array(0x62), - 0x1D436 => array(0x63), - 0x1D437 => array(0x64), - 0x1D438 => array(0x65), - 0x1D439 => array(0x66), - 0x1D43A => array(0x67), - 0x1D43B => array(0x68), - 0x1D43C => array(0x69), - 0x1D43D => array(0x6A), - 0x1D43E => array(0x6B), - 0x1D43F => array(0x6C), - 0x1D440 => array(0x6D), - 0x1D441 => array(0x6E), - 0x1D442 => array(0x6F), - 0x1D443 => array(0x70), - 0x1D444 => array(0x71), - 0x1D445 => array(0x72), - 0x1D446 => array(0x73), - 0x1D447 => array(0x74), - 0x1D448 => array(0x75), - 0x1D449 => array(0x76), - 0x1D44A => array(0x77), - 0x1D44B => array(0x78), - 0x1D44C => array(0x79), - 0x1D44D => array(0x7A), - 0x1D468 => array(0x61), - 0x1D469 => array(0x62), - 0x1D46A => array(0x63), - 0x1D46B => array(0x64), - 0x1D46C => array(0x65), - 0x1D46D => array(0x66), - 0x1D46E => array(0x67), - 0x1D46F => array(0x68), - 0x1D470 => array(0x69), - 0x1D471 => array(0x6A), - 0x1D472 => array(0x6B), - 0x1D473 => array(0x6C), - 0x1D474 => array(0x6D), - 0x1D475 => array(0x6E), - 0x1D476 => array(0x6F), - 0x1D477 => array(0x70), - 0x1D478 => array(0x71), - 0x1D479 => array(0x72), - 0x1D47A => array(0x73), - 0x1D47B => array(0x74), - 0x1D47C => array(0x75), - 0x1D47D => array(0x76), - 0x1D47E => array(0x77), - 0x1D47F => array(0x78), - 0x1D480 => array(0x79), - 0x1D481 => array(0x7A), - 0x1D49C => array(0x61), - 0x1D49E => array(0x63), - 0x1D49F => array(0x64), - 0x1D4A2 => array(0x67), - 0x1D4A5 => array(0x6A), - 0x1D4A6 => array(0x6B), - 0x1D4A9 => array(0x6E), - 0x1D4AA => array(0x6F), - 0x1D4AB => array(0x70), - 0x1D4AC => array(0x71), - 0x1D4AE => array(0x73), - 0x1D4AF => array(0x74), - 0x1D4B0 => array(0x75), - 0x1D4B1 => array(0x76), - 0x1D4B2 => array(0x77), - 0x1D4B3 => array(0x78), - 0x1D4B4 => array(0x79), - 0x1D4B5 => array(0x7A), - 0x1D4D0 => array(0x61), - 0x1D4D1 => array(0x62), - 0x1D4D2 => array(0x63), - 0x1D4D3 => array(0x64), - 0x1D4D4 => array(0x65), - 0x1D4D5 => array(0x66), - 0x1D4D6 => array(0x67), - 0x1D4D7 => array(0x68), - 0x1D4D8 => array(0x69), - 0x1D4D9 => array(0x6A), - 0x1D4DA => array(0x6B), - 0x1D4DB => array(0x6C), - 0x1D4DC => array(0x6D), - 0x1D4DD => array(0x6E), - 0x1D4DE => array(0x6F), - 0x1D4DF => array(0x70), - 0x1D4E0 => array(0x71), - 0x1D4E1 => array(0x72), - 0x1D4E2 => array(0x73), - 0x1D4E3 => array(0x74), - 0x1D4E4 => array(0x75), - 0x1D4E5 => array(0x76), - 0x1D4E6 => array(0x77), - 0x1D4E7 => array(0x78), - 0x1D4E8 => array(0x79), - 0x1D4E9 => array(0x7A), - 0x1D504 => array(0x61), - 0x1D505 => array(0x62), - 0x1D507 => array(0x64), - 0x1D508 => array(0x65), - 0x1D509 => array(0x66), - 0x1D50A => array(0x67), - 0x1D50D => array(0x6A), - 0x1D50E => array(0x6B), - 0x1D50F => array(0x6C), - 0x1D510 => array(0x6D), - 0x1D511 => array(0x6E), - 0x1D512 => array(0x6F), - 0x1D513 => array(0x70), - 0x1D514 => array(0x71), - 0x1D516 => array(0x73), - 0x1D517 => array(0x74), - 0x1D518 => array(0x75), - 0x1D519 => array(0x76), - 0x1D51A => array(0x77), - 0x1D51B => array(0x78), - 0x1D51C => array(0x79), - 0x1D538 => array(0x61), - 0x1D539 => array(0x62), - 0x1D53B => array(0x64), - 0x1D53C => array(0x65), - 0x1D53D => array(0x66), - 0x1D53E => array(0x67), - 0x1D540 => array(0x69), - 0x1D541 => array(0x6A), - 0x1D542 => array(0x6B), - 0x1D543 => array(0x6C), - 0x1D544 => array(0x6D), - 0x1D546 => array(0x6F), - 0x1D54A => array(0x73), - 0x1D54B => array(0x74), - 0x1D54C => array(0x75), - 0x1D54D => array(0x76), - 0x1D54E => array(0x77), - 0x1D54F => array(0x78), - 0x1D550 => array(0x79), - 0x1D56C => array(0x61), - 0x1D56D => array(0x62), - 0x1D56E => array(0x63), - 0x1D56F => array(0x64), - 0x1D570 => array(0x65), - 0x1D571 => array(0x66), - 0x1D572 => array(0x67), - 0x1D573 => array(0x68), - 0x1D574 => array(0x69), - 0x1D575 => array(0x6A), - 0x1D576 => array(0x6B), - 0x1D577 => array(0x6C), - 0x1D578 => array(0x6D), - 0x1D579 => array(0x6E), - 0x1D57A => array(0x6F), - 0x1D57B => array(0x70), - 0x1D57C => array(0x71), - 0x1D57D => array(0x72), - 0x1D57E => array(0x73), - 0x1D57F => array(0x74), - 0x1D580 => array(0x75), - 0x1D581 => array(0x76), - 0x1D582 => array(0x77), - 0x1D583 => array(0x78), - 0x1D584 => array(0x79), - 0x1D585 => array(0x7A), - 0x1D5A0 => array(0x61), - 0x1D5A1 => array(0x62), - 0x1D5A2 => array(0x63), - 0x1D5A3 => array(0x64), - 0x1D5A4 => array(0x65), - 0x1D5A5 => array(0x66), - 0x1D5A6 => array(0x67), - 0x1D5A7 => array(0x68), - 0x1D5A8 => array(0x69), - 0x1D5A9 => array(0x6A), - 0x1D5AA => array(0x6B), - 0x1D5AB => array(0x6C), - 0x1D5AC => array(0x6D), - 0x1D5AD => array(0x6E), - 0x1D5AE => array(0x6F), - 0x1D5AF => array(0x70), - 0x1D5B0 => array(0x71), - 0x1D5B1 => array(0x72), - 0x1D5B2 => array(0x73), - 0x1D5B3 => array(0x74), - 0x1D5B4 => array(0x75), - 0x1D5B5 => array(0x76), - 0x1D5B6 => array(0x77), - 0x1D5B7 => array(0x78), - 0x1D5B8 => array(0x79), - 0x1D5B9 => array(0x7A), - 0x1D5D4 => array(0x61), - 0x1D5D5 => array(0x62), - 0x1D5D6 => array(0x63), - 0x1D5D7 => array(0x64), - 0x1D5D8 => array(0x65), - 0x1D5D9 => array(0x66), - 0x1D5DA => array(0x67), - 0x1D5DB => array(0x68), - 0x1D5DC => array(0x69), - 0x1D5DD => array(0x6A), - 0x1D5DE => array(0x6B), - 0x1D5DF => array(0x6C), - 0x1D5E0 => array(0x6D), - 0x1D5E1 => array(0x6E), - 0x1D5E2 => array(0x6F), - 0x1D5E3 => array(0x70), - 0x1D5E4 => array(0x71), - 0x1D5E5 => array(0x72), - 0x1D5E6 => array(0x73), - 0x1D5E7 => array(0x74), - 0x1D5E8 => array(0x75), - 0x1D5E9 => array(0x76), - 0x1D5EA => array(0x77), - 0x1D5EB => array(0x78), - 0x1D5EC => array(0x79), - 0x1D5ED => array(0x7A), - 0x1D608 => array(0x61), - 0x1D609 => array(0x62), - 0x1D60A => array(0x63), - 0x1D60B => array(0x64), - 0x1D60C => array(0x65), - 0x1D60D => array(0x66), - 0x1D60E => array(0x67), - 0x1D60F => array(0x68), - 0x1D610 => array(0x69), - 0x1D611 => array(0x6A), - 0x1D612 => array(0x6B), - 0x1D613 => array(0x6C), - 0x1D614 => array(0x6D), - 0x1D615 => array(0x6E), - 0x1D616 => array(0x6F), - 0x1D617 => array(0x70), - 0x1D618 => array(0x71), - 0x1D619 => array(0x72), - 0x1D61A => array(0x73), - 0x1D61B => array(0x74), - 0x1D61C => array(0x75), - 0x1D61D => array(0x76), - 0x1D61E => array(0x77), - 0x1D61F => array(0x78), - 0x1D620 => array(0x79), - 0x1D621 => array(0x7A), - 0x1D63C => array(0x61), - 0x1D63D => array(0x62), - 0x1D63E => array(0x63), - 0x1D63F => array(0x64), - 0x1D640 => array(0x65), - 0x1D641 => array(0x66), - 0x1D642 => array(0x67), - 0x1D643 => array(0x68), - 0x1D644 => array(0x69), - 0x1D645 => array(0x6A), - 0x1D646 => array(0x6B), - 0x1D647 => array(0x6C), - 0x1D648 => array(0x6D), - 0x1D649 => array(0x6E), - 0x1D64A => array(0x6F), - 0x1D64B => array(0x70), - 0x1D64C => array(0x71), - 0x1D64D => array(0x72), - 0x1D64E => array(0x73), - 0x1D64F => array(0x74), - 0x1D650 => array(0x75), - 0x1D651 => array(0x76), - 0x1D652 => array(0x77), - 0x1D653 => array(0x78), - 0x1D654 => array(0x79), - 0x1D655 => array(0x7A), - 0x1D670 => array(0x61), - 0x1D671 => array(0x62), - 0x1D672 => array(0x63), - 0x1D673 => array(0x64), - 0x1D674 => array(0x65), - 0x1D675 => array(0x66), - 0x1D676 => array(0x67), - 0x1D677 => array(0x68), - 0x1D678 => array(0x69), - 0x1D679 => array(0x6A), - 0x1D67A => array(0x6B), - 0x1D67B => array(0x6C), - 0x1D67C => array(0x6D), - 0x1D67D => array(0x6E), - 0x1D67E => array(0x6F), - 0x1D67F => array(0x70), - 0x1D680 => array(0x71), - 0x1D681 => array(0x72), - 0x1D682 => array(0x73), - 0x1D683 => array(0x74), - 0x1D684 => array(0x75), - 0x1D685 => array(0x76), - 0x1D686 => array(0x77), - 0x1D687 => array(0x78), - 0x1D688 => array(0x79), - 0x1D689 => array(0x7A), - 0x1D6A8 => array(0x3B1), - 0x1D6A9 => array(0x3B2), - 0x1D6AA => array(0x3B3), - 0x1D6AB => array(0x3B4), - 0x1D6AC => array(0x3B5), - 0x1D6AD => array(0x3B6), - 0x1D6AE => array(0x3B7), - 0x1D6AF => array(0x3B8), - 0x1D6B0 => array(0x3B9), - 0x1D6B1 => array(0x3BA), - 0x1D6B2 => array(0x3BB), - 0x1D6B3 => array(0x3BC), - 0x1D6B4 => array(0x3BD), - 0x1D6B5 => array(0x3BE), - 0x1D6B6 => array(0x3BF), - 0x1D6B7 => array(0x3C0), - 0x1D6B8 => array(0x3C1), - 0x1D6B9 => array(0x3B8), - 0x1D6BA => array(0x3C3), - 0x1D6BB => array(0x3C4), - 0x1D6BC => array(0x3C5), - 0x1D6BD => array(0x3C6), - 0x1D6BE => array(0x3C7), - 0x1D6BF => array(0x3C8), - 0x1D6C0 => array(0x3C9), - 0x1D6D3 => array(0x3C3), - 0x1D6E2 => array(0x3B1), - 0x1D6E3 => array(0x3B2), - 0x1D6E4 => array(0x3B3), - 0x1D6E5 => array(0x3B4), - 0x1D6E6 => array(0x3B5), - 0x1D6E7 => array(0x3B6), - 0x1D6E8 => array(0x3B7), - 0x1D6E9 => array(0x3B8), - 0x1D6EA => array(0x3B9), - 0x1D6EB => array(0x3BA), - 0x1D6EC => array(0x3BB), - 0x1D6ED => array(0x3BC), - 0x1D6EE => array(0x3BD), - 0x1D6EF => array(0x3BE), - 0x1D6F0 => array(0x3BF), - 0x1D6F1 => array(0x3C0), - 0x1D6F2 => array(0x3C1), - 0x1D6F3 => array(0x3B8), - 0x1D6F4 => array(0x3C3), - 0x1D6F5 => array(0x3C4), - 0x1D6F6 => array(0x3C5), - 0x1D6F7 => array(0x3C6), - 0x1D6F8 => array(0x3C7), - 0x1D6F9 => array(0x3C8), - 0x1D6FA => array(0x3C9), - 0x1D70D => array(0x3C3), - 0x1D71C => array(0x3B1), - 0x1D71D => array(0x3B2), - 0x1D71E => array(0x3B3), - 0x1D71F => array(0x3B4), - 0x1D720 => array(0x3B5), - 0x1D721 => array(0x3B6), - 0x1D722 => array(0x3B7), - 0x1D723 => array(0x3B8), - 0x1D724 => array(0x3B9), - 0x1D725 => array(0x3BA), - 0x1D726 => array(0x3BB), - 0x1D727 => array(0x3BC), - 0x1D728 => array(0x3BD), - 0x1D729 => array(0x3BE), - 0x1D72A => array(0x3BF), - 0x1D72B => array(0x3C0), - 0x1D72C => array(0x3C1), - 0x1D72D => array(0x3B8), - 0x1D72E => array(0x3C3), - 0x1D72F => array(0x3C4), - 0x1D730 => array(0x3C5), - 0x1D731 => array(0x3C6), - 0x1D732 => array(0x3C7), - 0x1D733 => array(0x3C8), - 0x1D734 => array(0x3C9), - 0x1D747 => array(0x3C3), - 0x1D756 => array(0x3B1), - 0x1D757 => array(0x3B2), - 0x1D758 => array(0x3B3), - 0x1D759 => array(0x3B4), - 0x1D75A => array(0x3B5), - 0x1D75B => array(0x3B6), - 0x1D75C => array(0x3B7), - 0x1D75D => array(0x3B8), - 0x1D75E => array(0x3B9), - 0x1D75F => array(0x3BA), - 0x1D760 => array(0x3BB), - 0x1D761 => array(0x3BC), - 0x1D762 => array(0x3BD), - 0x1D763 => array(0x3BE), - 0x1D764 => array(0x3BF), - 0x1D765 => array(0x3C0), - 0x1D766 => array(0x3C1), - 0x1D767 => array(0x3B8), - 0x1D768 => array(0x3C3), - 0x1D769 => array(0x3C4), - 0x1D76A => array(0x3C5), - 0x1D76B => array(0x3C6), - 0x1D76C => array(0x3C7), - 0x1D76D => array(0x3C8), - 0x1D76E => array(0x3C9), - 0x1D781 => array(0x3C3), - 0x1D790 => array(0x3B1), - 0x1D791 => array(0x3B2), - 0x1D792 => array(0x3B3), - 0x1D793 => array(0x3B4), - 0x1D794 => array(0x3B5), - 0x1D795 => array(0x3B6), - 0x1D796 => array(0x3B7), - 0x1D797 => array(0x3B8), - 0x1D798 => array(0x3B9), - 0x1D799 => array(0x3BA), - 0x1D79A => array(0x3BB), - 0x1D79B => array(0x3BC), - 0x1D79C => array(0x3BD), - 0x1D79D => array(0x3BE), - 0x1D79E => array(0x3BF), - 0x1D79F => array(0x3C0), - 0x1D7A0 => array(0x3C1), - 0x1D7A1 => array(0x3B8), - 0x1D7A2 => array(0x3C3), - 0x1D7A3 => array(0x3C4), - 0x1D7A4 => array(0x3C5), - 0x1D7A5 => array(0x3C6), - 0x1D7A6 => array(0x3C7), - 0x1D7A7 => array(0x3C8), - 0x1D7A8 => array(0x3C9), - 0x1D7BB => array(0x3C3), - 0x3F9 => array(0x3C3), - 0x1D2C => array(0x61), - 0x1D2D => array(0xE6), - 0x1D2E => array(0x62), - 0x1D30 => array(0x64), - 0x1D31 => array(0x65), - 0x1D32 => array(0x1DD), - 0x1D33 => array(0x67), - 0x1D34 => array(0x68), - 0x1D35 => array(0x69), - 0x1D36 => array(0x6A), - 0x1D37 => array(0x6B), - 0x1D38 => array(0x6C), - 0x1D39 => array(0x6D), - 0x1D3A => array(0x6E), - 0x1D3C => array(0x6F), - 0x1D3D => array(0x223), - 0x1D3E => array(0x70), - 0x1D3F => array(0x72), - 0x1D40 => array(0x74), - 0x1D41 => array(0x75), - 0x1D42 => array(0x77), - 0x213B => array(0x66, 0x61, 0x78), - 0x3250 => array(0x70, 0x74, 0x65), - 0x32CC => array(0x68, 0x67), - 0x32CE => array(0x65, 0x76), - 0x32CF => array(0x6C, 0x74, 0x64), - 0x337A => array(0x69, 0x75), - 0x33DE => array(0x76, 0x2215, 0x6D), - 0x33DF => array(0x61, 0x2215, 0x6D) - ); - - /** - * Normalization Combining Classes; Code Points not listed - * got Combining Class 0. - * - * @var array - */ - private static $_np_norm_combcls = array( - 0x334 => 1, - 0x335 => 1, - 0x336 => 1, - 0x337 => 1, - 0x338 => 1, - 0x93C => 7, - 0x9BC => 7, - 0xA3C => 7, - 0xABC => 7, - 0xB3C => 7, - 0xCBC => 7, - 0x1037 => 7, - 0x3099 => 8, - 0x309A => 8, - 0x94D => 9, - 0x9CD => 9, - 0xA4D => 9, - 0xACD => 9, - 0xB4D => 9, - 0xBCD => 9, - 0xC4D => 9, - 0xCCD => 9, - 0xD4D => 9, - 0xDCA => 9, - 0xE3A => 9, - 0xF84 => 9, - 0x1039 => 9, - 0x1714 => 9, - 0x1734 => 9, - 0x17D2 => 9, - 0x5B0 => 10, - 0x5B1 => 11, - 0x5B2 => 12, - 0x5B3 => 13, - 0x5B4 => 14, - 0x5B5 => 15, - 0x5B6 => 16, - 0x5B7 => 17, - 0x5B8 => 18, - 0x5B9 => 19, - 0x5BB => 20, - 0x5Bc => 21, - 0x5BD => 22, - 0x5BF => 23, - 0x5C1 => 24, - 0x5C2 => 25, - 0xFB1E => 26, - 0x64B => 27, - 0x64C => 28, - 0x64D => 29, - 0x64E => 30, - 0x64F => 31, - 0x650 => 32, - 0x651 => 33, - 0x652 => 34, - 0x670 => 35, - 0x711 => 36, - 0xC55 => 84, - 0xC56 => 91, - 0xE38 => 103, - 0xE39 => 103, - 0xE48 => 107, - 0xE49 => 107, - 0xE4A => 107, - 0xE4B => 107, - 0xEB8 => 118, - 0xEB9 => 118, - 0xEC8 => 122, - 0xEC9 => 122, - 0xECA => 122, - 0xECB => 122, - 0xF71 => 129, - 0xF72 => 130, - 0xF7A => 130, - 0xF7B => 130, - 0xF7C => 130, - 0xF7D => 130, - 0xF80 => 130, - 0xF74 => 132, - 0x321 => 202, - 0x322 => 202, - 0x327 => 202, - 0x328 => 202, - 0x31B => 216, - 0xF39 => 216, - 0x1D165 => 216, - 0x1D166 => 216, - 0x1D16E => 216, - 0x1D16F => 216, - 0x1D170 => 216, - 0x1D171 => 216, - 0x1D172 => 216, - 0x302A => 218, - 0x316 => 220, - 0x317 => 220, - 0x318 => 220, - 0x319 => 220, - 0x31C => 220, - 0x31D => 220, - 0x31E => 220, - 0x31F => 220, - 0x320 => 220, - 0x323 => 220, - 0x324 => 220, - 0x325 => 220, - 0x326 => 220, - 0x329 => 220, - 0x32A => 220, - 0x32B => 220, - 0x32C => 220, - 0x32D => 220, - 0x32E => 220, - 0x32F => 220, - 0x330 => 220, - 0x331 => 220, - 0x332 => 220, - 0x333 => 220, - 0x339 => 220, - 0x33A => 220, - 0x33B => 220, - 0x33C => 220, - 0x347 => 220, - 0x348 => 220, - 0x349 => 220, - 0x34D => 220, - 0x34E => 220, - 0x353 => 220, - 0x354 => 220, - 0x355 => 220, - 0x356 => 220, - 0x591 => 220, - 0x596 => 220, - 0x59B => 220, - 0x5A3 => 220, - 0x5A4 => 220, - 0x5A5 => 220, - 0x5A6 => 220, - 0x5A7 => 220, - 0x5AA => 220, - 0x655 => 220, - 0x656 => 220, - 0x6E3 => 220, - 0x6EA => 220, - 0x6ED => 220, - 0x731 => 220, - 0x734 => 220, - 0x737 => 220, - 0x738 => 220, - 0x739 => 220, - 0x73B => 220, - 0x73C => 220, - 0x73E => 220, - 0x742 => 220, - 0x744 => 220, - 0x746 => 220, - 0x748 => 220, - 0x952 => 220, - 0xF18 => 220, - 0xF19 => 220, - 0xF35 => 220, - 0xF37 => 220, - 0xFC6 => 220, - 0x193B => 220, - 0x20E8 => 220, - 0x1D17B => 220, - 0x1D17C => 220, - 0x1D17D => 220, - 0x1D17E => 220, - 0x1D17F => 220, - 0x1D180 => 220, - 0x1D181 => 220, - 0x1D182 => 220, - 0x1D18A => 220, - 0x1D18B => 220, - 0x59A => 222, - 0x5AD => 222, - 0x1929 => 222, - 0x302D => 222, - 0x302E => 224, - 0x302F => 224, - 0x1D16D => 226, - 0x5AE => 228, - 0x18A9 => 228, - 0x302B => 228, - 0x300 => 230, - 0x301 => 230, - 0x302 => 230, - 0x303 => 230, - 0x304 => 230, - 0x305 => 230, - 0x306 => 230, - 0x307 => 230, - 0x308 => 230, - 0x309 => 230, - 0x30A => 230, - 0x30B => 230, - 0x30C => 230, - 0x30D => 230, - 0x30E => 230, - 0x30F => 230, - 0x310 => 230, - 0x311 => 230, - 0x312 => 230, - 0x313 => 230, - 0x314 => 230, - 0x33D => 230, - 0x33E => 230, - 0x33F => 230, - 0x340 => 230, - 0x341 => 230, - 0x342 => 230, - 0x343 => 230, - 0x344 => 230, - 0x346 => 230, - 0x34A => 230, - 0x34B => 230, - 0x34C => 230, - 0x350 => 230, - 0x351 => 230, - 0x352 => 230, - 0x357 => 230, - 0x363 => 230, - 0x364 => 230, - 0x365 => 230, - 0x366 => 230, - 0x367 => 230, - 0x368 => 230, - 0x369 => 230, - 0x36A => 230, - 0x36B => 230, - 0x36C => 230, - 0x36D => 230, - 0x36E => 230, - 0x36F => 230, - 0x483 => 230, - 0x484 => 230, - 0x485 => 230, - 0x486 => 230, - 0x592 => 230, - 0x593 => 230, - 0x594 => 230, - 0x595 => 230, - 0x597 => 230, - 0x598 => 230, - 0x599 => 230, - 0x59C => 230, - 0x59D => 230, - 0x59E => 230, - 0x59F => 230, - 0x5A0 => 230, - 0x5A1 => 230, - 0x5A8 => 230, - 0x5A9 => 230, - 0x5AB => 230, - 0x5AC => 230, - 0x5AF => 230, - 0x5C4 => 230, - 0x610 => 230, - 0x611 => 230, - 0x612 => 230, - 0x613 => 230, - 0x614 => 230, - 0x615 => 230, - 0x653 => 230, - 0x654 => 230, - 0x657 => 230, - 0x658 => 230, - 0x6D6 => 230, - 0x6D7 => 230, - 0x6D8 => 230, - 0x6D9 => 230, - 0x6DA => 230, - 0x6DB => 230, - 0x6DC => 230, - 0x6DF => 230, - 0x6E0 => 230, - 0x6E1 => 230, - 0x6E2 => 230, - 0x6E4 => 230, - 0x6E7 => 230, - 0x6E8 => 230, - 0x6EB => 230, - 0x6EC => 230, - 0x730 => 230, - 0x732 => 230, - 0x733 => 230, - 0x735 => 230, - 0x736 => 230, - 0x73A => 230, - 0x73D => 230, - 0x73F => 230, - 0x740 => 230, - 0x741 => 230, - 0x743 => 230, - 0x745 => 230, - 0x747 => 230, - 0x749 => 230, - 0x74A => 230, - 0x951 => 230, - 0x953 => 230, - 0x954 => 230, - 0xF82 => 230, - 0xF83 => 230, - 0xF86 => 230, - 0xF87 => 230, - 0x170D => 230, - 0x193A => 230, - 0x20D0 => 230, - 0x20D1 => 230, - 0x20D4 => 230, - 0x20D5 => 230, - 0x20D6 => 230, - 0x20D7 => 230, - 0x20DB => 230, - 0x20DC => 230, - 0x20E1 => 230, - 0x20E7 => 230, - 0x20E9 => 230, - 0xFE20 => 230, - 0xFE21 => 230, - 0xFE22 => 230, - 0xFE23 => 230, - 0x1D185 => 230, - 0x1D186 => 230, - 0x1D187 => 230, - 0x1D189 => 230, - 0x1D188 => 230, - 0x1D1AA => 230, - 0x1D1AB => 230, - 0x1D1AC => 230, - 0x1D1AD => 230, - 0x315 => 232, - 0x31A => 232, - 0x302C => 232, - 0x35F => 233, - 0x362 => 233, - 0x35D => 234, - 0x35E => 234, - 0x360 => 234, - 0x361 => 234, - 0x345 => 240 - ); - // }}} - - // {{{ properties - /** - * @var string - */ - private $_punycode_prefix = 'xn--'; - - /** - */ - private $_invalid_ucs = 0x80000000; - - /** - */ - private $_max_ucs = 0x10FFFF; - - /** - * @var int - */ - private $_base = 36; - - /** - * @var int - */ - private $_tmin = 1; - - /** - * @var int - */ - private $_tmax = 26; - - /** - * @var int - */ - private $_skew = 38; - - /** - * @var int - */ - private $_damp = 700; - - /** - * @var int - */ - private $_initial_bias = 72; - - /** - * @var int - */ - private $_initial_n = 0x80; - - /** - * @var int - */ - private $_slast; - - /** - */ - private $_sbase = 0xAC00; - - /** - */ - private $_lbase = 0x1100; - - /** - */ - private $_vbase = 0x1161; - - /** - */ - private $_tbase = 0x11a7; - - /** - * @var int - */ - private $_lcount = 19; - - /** - * @var int - */ - private $_vcount = 21; - - /** - * @var int - */ - private $_tcount = 28; - - /** - * vcount * tcount - * - * @var int - */ - private $_ncount = 588; - - /** - * lcount * tcount * vcount - * - * @var int - */ - private $_scount = 11172; - - /** - * Default encoding for encode()'s input and decode()'s output is UTF-8; - * Other possible encodings are ucs4_string and ucs4_array - * See {@link setParams()} for how to select these - * - * @var bool - */ - private $_api_encoding = 'utf8'; - - /** - * Overlong UTF-8 encodings are forbidden - * - * @var bool - */ - private $_allow_overlong = false; - - /** - * Behave strict or not - * - * @var bool - */ - private $_strict_mode = false; - - /** - * IDNA-version to use - * - * Values are "2003" and "2008". - * Defaults to "2003", since that was the original version and for - * compatibility with previous versions of this library. - * If you need to encode "new" characters like the German "Eszett", - * please switch to 2008 first before encoding. - * - * @var bool - */ - private $_version = '2003'; - - /** - * Cached value indicating whether or not mbstring function overloading is - * on for strlen - * - * This is cached for optimal performance. - * - * @var boolean - * @see Net_IDNA2::_byteLength() - */ - private static $_mb_string_overload = null; - // }}} - - - // {{{ constructor - /** - * Constructor - * - * @see setParams() - */ - public function __construct($options = null) - { - $this->_slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; - - if (is_array($options)) { - $this->setParams($options); - } - - // populate mbstring overloading cache if not set - if (self::$_mb_string_overload === null) { - self::$_mb_string_overload = (extension_loaded('mbstring') - && (ini_get('mbstring.func_overload') & 0x02) === 0x02); - } - } - // }}} - - - /** - * Sets a new option value. Available options and values: - * - * [utf8 - Use either UTF-8 or ISO-8859-1 as input (true for UTF-8, false - * otherwise); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead] - * - * @param mixed $option Parameter to set (string: single parameter; array of Parameter => Value pairs) - * - */ - public function setParams($option, $value = false) : bool - { - if (!is_array($option)) { - $option = array($option => $value); - } - - foreach ($option as $k => $v) { - switch ($k) { - case 'encoding': - switch ($v) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - $this->_api_encoding = $v; - break; - - default: - throw new InvalidArgumentException('Set Parameter: Unknown parameter '.$v.' for option '.$k); - } - - break; - - case 'overlong': - $this->_allow_overlong = ($v) ? true : false; - break; - - case 'strict': - $this->_strict_mode = ($v) ? true : false; - break; - - case 'version': - if (in_array($v, array('2003', '2008'))) { - $this->_version = $v; - } else { - throw new InvalidArgumentException('Set Parameter: Invalid parameter '.$v.' for option '.$k); - } - break; - - default: - return false; - } - } - - return true; - } - - /** - * Encode a given UTF-8 domain name. - * - * If not given will use default-encoding - * - * @return mixed processed string - * @throws Exception - */ - public function encode($decoded, $one_time_encoding = false) - { - // Forcing conversion of input to UCS4 array - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - $decoded = $this->_utf8_to_ucs4($decoded); - break; - case 'ucs4_string': - $decoded = $this->_ucs4_string_to_ucs4($decoded); - case 'ucs4_array': // No break; before this line. Catch case, but do nothing - break; - default: - throw new InvalidArgumentException('Unsupported input format'); - } - - // No input, no output, what else did you expect? - if (empty($decoded)) return ''; - - // Anchors for iteration - $last_begin = 0; - // Output string - $output = ''; - - foreach ($decoded as $k => $v) { - // Make sure to use just the plain dot - switch($v) { - case 0x3002: - case 0xFF0E: - case 0xFF61: - $decoded[$k] = 0x2E; - // It's right, no break here - // The codepoints above have to be converted to dots anyway - - // Stumbling across an anchoring character - case 0x2E: - case 0x2F: - case 0x3A: - case 0x3F: - case 0x40: - // Neither email addresses nor URLs allowed in strict mode - if ($this->_strict_mode) { - throw new InvalidArgumentException('Neither email addresses nor URLs are allowed in strict mode.'); - } - // Skip first char - if ($k) { - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); - } - $output .= chr($decoded[$k]); - } - $last_begin = $k + 1; - } - } - // Catch the rest of the string - if ($last_begin) { - $inp_len = sizeof($decoded); - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - } - return $output; - } - - if ($output = $this->_encode($decoded)) { - return $output; - } - - return $this->_ucs4_to_utf8($decoded); - } - - /** - * Decode a given ACE domain name. - * - * @throws Exception - */ - public function decode($input, $one_time_encoding = false) - { - // Optionally set - if ($one_time_encoding) { - switch ($one_time_encoding) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - break; - default: - throw new InvalidArgumentException('Unknown encoding '.$one_time_encoding); - } - } - // Make sure to drop any newline characters around - $input = trim($input); - - // Negotiate input and try to determine, wether it is a plain string, - // an email address or something like a complete URL - if (strpos($input, '@')) { // Maybe it is an email address - // No no in strict mode - if ($this->_strict_mode) { - throw new InvalidArgumentException('Only simple domain name parts can be handled in strict mode'); - } - list($email_pref, $input) = explode('@', $input, 2); - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $return = $email_pref . '@' . join('.', $arr); - } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) - // No no in strict mode - if ($this->_strict_mode) { - throw new InvalidArgumentException('Only simple domain name parts can be handled in strict mode'); - } - - $parsed = parse_url($input); - if (isset($parsed['host'])) { - $arr = explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $parsed['host'] = join('.', $arr); - if (isset($parsed['scheme'])) { - $parsed['scheme'] .= (strtolower($parsed['scheme']) == 'mailto') ? ':' : '://'; - } - $return = $this->_unparse_url($parsed); - } else { // parse_url seems to have failed, try without it - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $return = join('.', $arr); - } - } else { // Otherwise we consider it being a pure domain name string - $return = $this->_decode($input); - } - // The output is UTF-8 by default, other output formats need conversion here - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - return $return; - break; - case 'ucs4_string': - return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); - break; - case 'ucs4_array': - return $this->_utf8_to_ucs4($return); - break; - default: - throw new InvalidArgumentException('Unsupported output format'); - } - } - - - // {{{ private - /** - * Opposite function to parse_url() - * - * Inspired by code from comments of php.net-documentation for parse_url() - */ - private function _unparse_url($parts_arr) : string - { - if (!empty($parts_arr['scheme'])) { - $ret_url = $parts_arr['scheme']; - } - if (!empty($parts_arr['user'])) { - $ret_url .= $parts_arr['user']; - if (!empty($parts_arr['pass'])) { - $ret_url .= ':' . $parts_arr['pass']; - } - $ret_url .= '@'; - } - $ret_url .= $parts_arr['host']; - if (!empty($parts_arr['port'])) { - $ret_url .= ':' . $parts_arr['port']; - } - $ret_url .= $parts_arr['path']; - if (!empty($parts_arr['query'])) { - $ret_url .= '?' . $parts_arr['query']; - } - if (!empty($parts_arr['fragment'])) { - $ret_url .= '#' . $parts_arr['fragment']; - } - return $ret_url; - } - - /** - * The actual encoding algorithm. - * - * @throws Exception - */ - private function _encode($decoded) : string - { - // We cannot encode a domain name containing the Punycode prefix - $extract = self::_byteLength($this->_punycode_prefix); - $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); - $check_deco = array_slice($decoded, 0, $extract); - - if ($check_pref == $check_deco) { - throw new InvalidArgumentException('This is already a punycode string'); - } - - // We will not try to encode strings consisting of basic code points only - $encodable = false; - foreach ($decoded as $k => $v) { - if ($v > 0x7a) { - $encodable = true; - break; - } - } - if (!$encodable) { - if ($this->_strict_mode) { - throw new InvalidArgumentException('The given string does not contain encodable chars'); - } - - return false; - } - - // Do NAMEPREP - $decoded = $this->_nameprep($decoded); - - $deco_len = count($decoded); - - // Empty array - if (!$deco_len) { - return false; - } - - // How many chars have been consumed - $codecount = 0; - - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix; - - $encoded = ''; - // Copy all basic code points to output - for ($i = 0; $i < $deco_len; ++$i) { - $test = $decoded[$i]; - // Will match [0-9a-zA-Z-] - if ((0x2F < $test && $test < 0x40) - || (0x40 < $test && $test < 0x5B) - || (0x60 < $test && $test <= 0x7B) - || (0x2D == $test) - ) { - $encoded .= chr($decoded[$i]); - $codecount++; - } - } - - // All codepoints were basic ones - if ($codecount == $deco_len) { - return $encoded; - } - - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix . $encoded; - - // If we have basic code points in output, add an hyphen to the end - if ($codecount) { - $encoded .= '-'; - } - - // Now find and encode all non-basic code points - $is_first = true; - $cur_code = $this->_initial_n; - $bias = $this->_initial_bias; - $delta = 0; - - while ($codecount < $deco_len) { - // Find the smallest code point >= the current code point and - // remember the last ouccrence of it in the input - for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { - if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { - $next_code = $decoded[$i]; - } - } - - $delta += ($next_code - $cur_code) * ($codecount + 1); - $cur_code = $next_code; - - // Scan input again and encode all characters whose code point is $cur_code - for ($i = 0; $i < $deco_len; $i++) { - if ($decoded[$i] < $cur_code) { - $delta++; - } else if ($decoded[$i] == $cur_code) { - for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { - $t = ($k <= $bias)? - $this->_tmin : - (($k >= $bias + $this->_tmax)? $this->_tmax : $k - $bias); - - if ($q < $t) { - break; - } - - $encoded .= $this->_encodeDigit(ceil($t + (($q - $t) % ($this->_base - $t)))); - $q = ($q - $t) / ($this->_base - $t); - } - - $encoded .= $this->_encodeDigit($q); - $bias = $this->_adapt($delta, $codecount + 1, $is_first); - $codecount++; - $delta = 0; - $is_first = false; - } - } - - $delta++; - $cur_code++; - } - - return $encoded; - } - - /** - * The actual decoding algorithm. - * - * @throws Exception - */ - private function _decode($encoded) : string - { - // We do need to find the Punycode prefix - if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) { - return false; - } - - $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded); - - // If nothing left after removing the prefix, it is hopeless - if (!$encode_test) { - return false; - } - - // Find last occurence of the delimiter - $delim_pos = strrpos($encoded, '-'); - - if ($delim_pos > self::_byteLength($this->_punycode_prefix)) { - for ($k = self::_byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) { - $decoded[] = ord($encoded[$k]); - } - } else { - $decoded = array(); - } - - $deco_len = count($decoded); - $enco_len = self::_byteLength($encoded); - - // Wandering through the strings; init - $is_first = true; - $bias = $this->_initial_bias; - $idx = 0; - $char = $this->_initial_n; - - for ($enco_idx = ($delim_pos)? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { - for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { - $digit = $this->_decodeDigit($encoded[$enco_idx++]); - $idx += $digit * $w; - - $t = ($k <= $bias) ? - $this->_tmin : - (($k >= $bias + $this->_tmax)? $this->_tmax : ($k - $bias)); - - if ($digit < $t) { - break; - } - - $w = (int)($w * ($this->_base - $t)); - } - - $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); - $is_first = false; - $char += (int) ($idx / ($deco_len + 1)); - $idx %= ($deco_len + 1); - - if ($deco_len > 0) { - // Make room for the decoded char - for ($i = $deco_len; $i > $idx; $i--) { - $decoded[$i] = $decoded[($i - 1)]; - } - } - - $decoded[$idx++] = $char; - } - - return $this->_ucs4_to_utf8($decoded); - } - - /** - * Adapt the bias according to the current code point and position. - */ - private function _adapt($delta, $npoints, $is_first) : int - { - $delta = (int) ($is_first ? ($delta / $this->_damp) : ($delta / 2)); - $delta += (int) ($delta / $npoints); - - for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { - $delta = (int) ($delta / ($this->_base - $this->_tmin)); - } - - return (int) ($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); - } - - /** - * Encoding a certain digit. - * - * @return char Encoded digit - */ - private function _encodeDigit($d) - { - return chr($d + 22 + 75 * ($d < 26)); - } - - /** - * Decode a certain digit. - * - * @param char $cp One digit (character) to decode - */ - private function _decodeDigit($cp) : int - { - $cp = ord($cp); - return ($cp - 48 < 10)? $cp - 22 : (($cp - 65 < 26)? $cp - 65 : (($cp - 97 < 26)? $cp - 97 : $this->_base)); - } - - /** - * Do Nameprep according to RFC3491 and RFC3454. - * - * @throws Exception - */ - private function _nameprep($input) : string - { - $output = array(); - - // Walking through the input array, performing the required steps on each of - // the input chars and putting the result into the output array - // While mapping required chars we apply the cannonical ordering - - foreach ($input as $v) { - // Map to nothing == skip that code point - if (in_array($v, self::$_np_map_nothing)) { - continue; - } - - // Try to find prohibited input - if (in_array($v, self::$_np_prohibit) || in_array($v, self::$_general_prohibited)) { - throw new Net_IDNA2_Exception_Nameprep('Prohibited input U+' . sprintf('%08X', $v)); - } - - foreach (self::$_np_prohibit_ranges as $range) { - if ($range[0] <= $v && $v <= $range[1]) { - throw new Net_IDNA2_Exception_Nameprep('Prohibited input U+' . sprintf('%08X', $v)); - } - } - - // Hangul syllable decomposition - if (0xAC00 <= $v && $v <= 0xD7AF) { - foreach ($this->_hangulDecompose($v) as $out) { - $output[] = $out; - } - } else if (($this->_version == '2003') && isset(self::$_np_replacemaps[$v])) { - // There's a decomposition mapping for that code point - // Decompositions only in version 2003 (original) of IDNA - foreach ($this->_applyCannonicalOrdering(self::$_np_replacemaps[$v]) as $out) { - $output[] = $out; - } - } else { - $output[] = $v; - } - } - - // Combine code points - - $last_class = 0; - $last_starter = 0; - $out_len = count($output); - - for ($i = 0; $i < $out_len; ++$i) { - $class = $this->_getCombiningClass($output[$i]); - - if ((!$last_class || $last_class != $class) && $class) { - // Try to match - $seq_len = $i - $last_starter; - $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); - - // On match: Replace the last starter with the composed character and remove - // the now redundant non-starter(s) - if ($out) { - $output[$last_starter] = $out; - - if (count($out) != $seq_len) { - for ($j = $i + 1; $j < $out_len; ++$j) { - $output[$j - 1] = $output[$j]; - } - - unset($output[$out_len]); - } - - // Rewind the for loop by one, since there can be more possible compositions - $i--; - $out_len--; - $last_class = ($i == $last_starter)? 0 : $this->_getCombiningClass($output[$i - 1]); - - continue; - } - } - - // The current class is 0 - if (!$class) { - $last_starter = $i; - } - - $last_class = $class; - } - - return $output; - } - - /** - * Decomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul). - * - * value as one value array - */ - private function _hangulDecompose($char) : array - { - $sindex = $char - $this->_sbase; - - if ($sindex < 0 || $sindex >= $this->_scount) { - return array($char); - } - - $result = array(); - $T = $this->_tbase + $sindex % $this->_tcount; - $result[] = (int)($this->_lbase + $sindex / $this->_ncount); - $result[] = (int)($this->_vbase + ($sindex % $this->_ncount) / $this->_tcount); - - if ($T != $this->_tbase) { - $result[] = $T; - } - - return $result; - } - - /** - * Ccomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul). - */ - private function _hangulCompose($input) : array - { - $inp_len = count($input); - - if (!$inp_len) { - return array(); - } - - $result = array(); - $last = $input[0]; - $result[] = $last; // copy first char from input to output - - for ($i = 1; $i < $inp_len; ++$i) { - $char = $input[$i]; - - // Find out, wether two current characters from L and V - $lindex = $last - $this->_lbase; - - if (0 <= $lindex && $lindex < $this->_lcount) { - $vindex = $char - $this->_vbase; - - if (0 <= $vindex && $vindex < $this->_vcount) { - // create syllable of form LV - $last = ($this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount); - $out_off = count($result) - 1; - $result[$out_off] = $last; // reset last - - // discard char - continue; - } - } - - // Find out, wether two current characters are LV and T - $sindex = $last - $this->_sbase; - - if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount) == 0) { - $tindex = $char - $this->_tbase; - - if (0 <= $tindex && $tindex <= $this->_tcount) { - // create syllable of form LVT - $last += $tindex; - $out_off = count($result) - 1; - $result[$out_off] = $last; // reset last - - // discard char - continue; - } - } - - // if neither case was true, just add the character - $last = $char; - $result[] = $char; - } - - return $result; - } - - /** - * Returns the combining class of a certain wide char. - */ - private function _getCombiningClass($char) : int - { - return isset(self::$_np_norm_combcls[$char])? self::$_np_norm_combcls[$char] : 0; - } - - /** - * Apllies the cannonical ordering of a decomposed UCS4 sequence. - */ - private function _applyCannonicalOrdering($input) : array - { - $swap = true; - $size = count($input); - - while ($swap) { - $swap = false; - $last = $this->_getCombiningClass($input[0]); - - for ($i = 0; $i < $size - 1; ++$i) { - $next = $this->_getCombiningClass($input[$i + 1]); - - if ($next != 0 && $last > $next) { - // Move item leftward until it fits - for ($j = $i + 1; $j > 0; --$j) { - if ($this->_getCombiningClass($input[$j - 1]) <= $next) { - break; - } - - $t = $input[$j]; - $input[$j] = $input[$j - 1]; - $input[$j - 1] = $t; - $swap = 1; - } - - // Reentering the loop looking at the old character again - $next = $last; - } - - $last = $next; - } - } - - return $input; - } - - /** - * Do composition of a sequence of starter and non-starter. - */ - private function _combine($input) : array - { - $inp_len = count($input); - - // Is it a Hangul syllable? - if (1 != $inp_len) { - $hangul = $this->_hangulCompose($input); - - // This place is probably wrong - if (count($hangul) != $inp_len) { - return $hangul; - } - } - - foreach (self::$_np_replacemaps as $np_src => $np_target) { - if ($np_target[0] != $input[0]) { - continue; - } - - if (count($np_target) != $inp_len) { - continue; - } - - $hit = false; - - foreach ($input as $k2 => $v2) { - if ($v2 == $np_target[$k2]) { - $hit = true; - } else { - $hit = false; - break; - } - } - - if ($hit) { - return $np_src; - } - } - - return false; - } - - /** - * This converts an UTF-8 encoded string to its UCS-4 (array) representation - * By talking about UCS-4 we mean arrays of 32bit integers representing - * each of the "chars". This is due to PHP not being able to handle strings with - * bit depth different from 8. This applies to the reverse method _ucs4_to_utf8(), too. - * The following UTF-8 encodings are supported: - * - * bytes bits representation - * 1 7 0xxxxxxx - * 2 11 110xxxxx 10xxxxxx - * 3 16 1110xxxx 10xxxxxx 10xxxxxx - * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * - * Each x represents a bit that can be used to store character data. - * - * @throws Exception - */ - private function _utf8_to_ucs4($input) : array - { - $output = array(); - $out_len = 0; - $inp_len = self::_byteLength($input, '8bit'); - $mode = 'next'; - $test = 'none'; - for ($k = 0; $k < $inp_len; ++$k) { - $v = ord($input[$k]); // Extract byte from input string - - if ($v < 128) { // We found an ASCII char - put into stirng as is - $output[$out_len] = $v; - ++$out_len; - if ('add' == $mode) { - throw new UnexpectedValueException('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - } - continue; - } - if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char - $start_byte = $v; - $mode = 'add'; - $test = 'range'; - if ($v >> 5 == 6) { // &110xxxxx 10xxxxx - $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left - $v = ($v - 192) << 6; - } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx - $next_byte = 1; - $v = ($v - 224) << 12; - } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 2; - $v = ($v - 240) << 18; - } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 3; - $v = ($v - 248) << 24; - } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 4; - $v = ($v - 252) << 30; - } else { - throw new UnexpectedValueException('This might be UTF-8, but I don\'t understand it at byte '.$k); - } - if ('add' == $mode) { - $output[$out_len] = (int) $v; - ++$out_len; - continue; - } - } - if ('add' == $mode) { - if (!$this->_allow_overlong && $test == 'range') { - $test = 'none'; - if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { - throw new OutOfRangeException('Bogus UTF-8 character detected (out of legal range) at byte '.$k); - } - } - if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx - $v = ($v - 128) << ($next_byte * 6); - $output[($out_len - 1)] += $v; - --$next_byte; - } else { - throw new UnexpectedValueException('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - } - if ($next_byte < 0) { - $mode = 'next'; - } - } - } // for - return $output; - } - - /** - * Convert UCS-4 array into UTF-8 string - * - * @throws Exception - */ - private function _ucs4_to_utf8($input) : string - { - $output = ''; - - foreach ($input as $v) { - // $v = ord($v); - - if ($v < 128) { - // 7bit are transferred literally - $output .= chr($v); - } else if ($v < 1 << 11) { - // 2 bytes - $output .= chr(192 + ($v >> 6)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 16) { - // 3 bytes - $output .= chr(224 + ($v >> 12)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 21) { - // 4 bytes - $output .= chr(240 + ($v >> 18)) - . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 26) { - // 5 bytes - $output .= chr(248 + ($v >> 24)) - . chr(128 + (($v >> 18) & 63)) - . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 31) { - // 6 bytes - $output .= chr(252 + ($v >> 30)) - . chr(128 + (($v >> 24) & 63)) - . chr(128 + (($v >> 18) & 63)) - . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else { - throw new UnexpectedValueException('Conversion from UCS-4 to UTF-8 failed: malformed input'); - } - } - - return $output; - } - - /** - * Convert UCS-4 array into UCS-4 string - * - * @throws Exception - */ - private function _ucs4_to_ucs4_string($input) : string - { - $output = ''; - // Take array values and split output to 4 bytes per value - // The bit mask is 255, which reads &11111111 - foreach ($input as $v) { - $output .= ($v & (255 << 24) >> 24) . ($v & (255 << 16) >> 16) . ($v & (255 << 8) >> 8) . ($v & 255); - } - return $output; - } - - /** - * Convert UCS-4 string into UCS-4 array - * - * @throws InvalidArgumentException - */ - private function _ucs4_string_to_ucs4($input) : array - { - $output = array(); - - $inp_len = self::_byteLength($input); - // Input length must be dividable by 4 - if ($inp_len % 4) { - throw new InvalidArgumentException('Input UCS4 string is broken'); - } - - // Empty input - return empty output - if (!$inp_len) { - return $output; - } - - for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { - // Increment output position every 4 input bytes - if (!$i % 4) { - $out_len++; - $output[$out_len] = 0; - } - $output[$out_len] += ord($input[$i]) << (8 * (3 - ($i % 4) ) ); - } - return $output; - } - - /** - * Echo hex representation of UCS4 sequence. - */ - private static function _showHex($input, $include_bit = false) : void - { - foreach ($input as $k => $v) { - echo '[', $k, '] => ', sprintf('%X', $v); - - if ($include_bit) { - echo ' (', Net_IDNA2::_showBitmask($v), ')'; - } - - echo "\n"; - } - } - - /** - * Gives you a bit representation of given Byte (8 bits), Word (16 bits) or DWord (32 bits) - * Output width is automagically determined - */ - private static function _showBitmask($octet) : string - { - if ($octet >= (1 << 16)) { - $w = 31; - } else if ($octet >= (1 << 8)) { - $w = 15; - } else { - $w = 7; - } - - $return = ''; - - for ($i = $w; $i > -1; $i--) { - $return .= ($octet & (1 << $i))? '1' : '0'; - } - - return $return; - } - - /** - * Gets the length of a string in bytes even if mbstring function - * overloading is turned on - * - * @see Net_IDNA2::$_mb_string_overload - */ - private static function _byteLength($string) : int - { - if (self::$_mb_string_overload) { - return mb_strlen($string, '8bit'); - } - return strlen((binary)$string); - } - - // }}}} - - // {{{ factory - /** - * Attempts to return a concrete IDNA instance for either php4 or php5. - * - * @return Net_IDNA2 - */ - function getInstance($params = array()) - { - return new Net_IDNA2($params); - } - // }}} - - // {{{ singleton - /** - * Attempts to return a concrete IDNA instance for either php4 or php5, - * only creating a new instance if no IDNA instance with the same - * parameters currently exists. - * - * @return object Net_IDNA2 - */ - function singleton($params = array()) - { - static $instances; - if (!isset($instances)) { - $instances = array(); - } - - $signature = serialize($params); - if (!isset($instances[$signature])) { - $instances[$signature] = Net_IDNA2::getInstance($params); - } - - return $instances[$signature]; - } - // }}} -} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2CustomExceptions.php b/snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2CustomExceptions.php deleted file mode 100644 index 69b0f6914..000000000 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Vendors/Net/IDNA2CustomExceptions.php +++ /dev/null @@ -1,18 +0,0 @@ - $v) { + $conv = $toAscii ? static::toAscii($v) : static::toUtf8($v); + if ($conv) $arr[$k] = $conv; + } + return $local . '@' . \implode('.', $arr); + } + return $toAscii ? static::toAscii($address) : static::toUtf8($address); + } + + private static function uri(string $address, bool $toAscii) : string + { + if (\preg_match('#[:\./]#', $address)) { + $parsed = \parse_url($address); + if (isset($parsed['host'])) { + $arr = \explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $toAscii ? static::toAscii($v) : static::toUtf8($v); + if ($conv) $arr[$k] = $conv; + } + $parsed['host'] = \implode('.', $arr); + + $url = '//'; + if (!empty($parsed['scheme'])) { $url = $parsed['scheme'] . (\strtolower($parsed['scheme']) === 'mailto' ? ':' : '://'); } +/* + if (!empty($parsed['user']) || !empty($parsed['pass'])) { + $ret_url .= $parts_arr['user']; + if (!empty($parts_arr['pass'])) { + $ret_url .= ':' . $parts_arr['pass']; + } + $ret_url .= '@'; + } +*/ + if (!empty($parsed['host'])) { $url .= $parsed['host']; } + if (!empty($parsed['port'])) { $url .= ':'.$parsed['port']; } + if (!empty($parsed['path'])) { $url .= $parsed['path']; } + if (!empty($parsed['query'])) { $url .= '?'.$parsed['query']; } + if (!empty($parsed['fragment'])) { $url .= '#'.$parsed['fragment']; } + return $url; + } + + // parse_url seems to have failed, try without it + $arr = \explode('.', $address); + foreach ($arr as $k => $v) { + $conv = $toAscii ? static::toAscii($v) : static::toUtf8($v); + if ($conv) $arr[$k] = $conv; + } + return \implode('.', $arr); + } + return $toAscii ? static::toAscii($address) : static::toUtf8($address); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/LICENSE b/snappymail/v/0.0.0/app/libraries/snappymail/intl/LICENSE new file mode 100644 index 000000000..4cd8bdd30 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/idn.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/idn.php new file mode 100644 index 000000000..47d3e612e --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/idn.php @@ -0,0 +1,1215 @@ + and Trevor Rowbotham + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Modified to the needs of SnappyMail + */ + +namespace SnappyMail\Intl; + +class Info +{ + public + $bidiDomain = false, + $errors = 0, + $validBidiDomain = true, + $transitionalDifferent = false; +} + +/** + * @see https://www.unicode.org/reports/tr46/ + */ +final class Idn +{ + public const + ERROR_EMPTY_LABEL = 1, + ERROR_LABEL_TOO_LONG = 2, + ERROR_DOMAIN_NAME_TOO_LONG = 4, + ERROR_LEADING_HYPHEN = 8, + ERROR_TRAILING_HYPHEN = 0x10, + ERROR_HYPHEN_3_4 = 0x20, + ERROR_LEADING_COMBINING_MARK = 0x40, + ERROR_DISALLOWED = 0x80, + ERROR_PUNYCODE = 0x100, + ERROR_LABEL_HAS_DOT = 0x200, + ERROR_INVALID_ACE_LABEL = 0x400, + ERROR_BIDI = 0x800, + ERROR_CONTEXTJ = 0x1000, + ERROR_CONTEXTO_PUNCTUATION = 0x2000, + ERROR_CONTEXTO_DIGITS = 0x4000, + + INTL_IDNA_VARIANT_UTS46 = 1, + + IDNA_DEFAULT = 0, + IDNA_ALLOW_UNASSIGNED = 1, + IDNA_USE_STD3_RULES = 2, + IDNA_CHECK_BIDI = 4, + IDNA_CHECK_CONTEXTJ = 8, + IDNA_NONTRANSITIONAL_TO_ASCII = 16, + IDNA_NONTRANSITIONAL_TO_UNICODE = 32, + + MAX_DOMAIN_SIZE = 253, + MAX_LABEL_SIZE = 63, + + BASE = 36, + TMIN = 1, + TMAX = 26, + SKEW = 38, + DAMP = 700, + INITIAL_BIAS = 72, + INITIAL_N = 128, + DELIMITER = '-', + MAX_INT = 2147483647, + + REGEX_COMBINING_MARK = '/^[\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{0903}\x{093A}\x{093B}\x{093C}\x{093E}-\x{0940}\x{0941}-\x{0948}\x{0949}-\x{094C}\x{094D}\x{094E}-\x{094F}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{0982}-\x{0983}\x{09BC}\x{09BE}-\x{09C0}\x{09C1}-\x{09C4}\x{09C7}-\x{09C8}\x{09CB}-\x{09CC}\x{09CD}\x{09D7}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A03}\x{0A3C}\x{0A3E}-\x{0A40}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0A83}\x{0ABC}\x{0ABE}-\x{0AC0}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0AC9}\x{0ACB}-\x{0ACC}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B02}-\x{0B03}\x{0B3C}\x{0B3E}\x{0B3F}\x{0B40}\x{0B41}-\x{0B44}\x{0B47}-\x{0B48}\x{0B4B}-\x{0B4C}\x{0B4D}\x{0B55}-\x{0B56}\x{0B57}\x{0B62}-\x{0B63}\x{0B82}\x{0BBE}-\x{0BBF}\x{0BC0}\x{0BC1}-\x{0BC2}\x{0BC6}-\x{0BC8}\x{0BCA}-\x{0BCC}\x{0BCD}\x{0BD7}\x{0C00}\x{0C01}-\x{0C03}\x{0C04}\x{0C3E}-\x{0C40}\x{0C41}-\x{0C44}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0C82}-\x{0C83}\x{0CBC}\x{0CBE}\x{0CBF}\x{0CC0}-\x{0CC4}\x{0CC6}\x{0CC7}-\x{0CC8}\x{0CCA}-\x{0CCB}\x{0CCC}-\x{0CCD}\x{0CD5}-\x{0CD6}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D02}-\x{0D03}\x{0D3B}-\x{0D3C}\x{0D3E}-\x{0D40}\x{0D41}-\x{0D44}\x{0D46}-\x{0D48}\x{0D4A}-\x{0D4C}\x{0D4D}\x{0D57}\x{0D62}-\x{0D63}\x{0D81}\x{0D82}-\x{0D83}\x{0DCA}\x{0DCF}-\x{0DD1}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0DD8}-\x{0DDF}\x{0DF2}-\x{0DF3}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3E}-\x{0F3F}\x{0F71}-\x{0F7E}\x{0F7F}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102B}-\x{102C}\x{102D}-\x{1030}\x{1031}\x{1032}-\x{1037}\x{1038}\x{1039}-\x{103A}\x{103B}-\x{103C}\x{103D}-\x{103E}\x{1056}-\x{1057}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106D}\x{1071}-\x{1074}\x{1082}\x{1083}-\x{1084}\x{1085}-\x{1086}\x{1087}-\x{108C}\x{108D}\x{108F}\x{109A}-\x{109C}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B6}\x{17B7}-\x{17BD}\x{17BE}-\x{17C5}\x{17C6}\x{17C7}-\x{17C8}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1923}-\x{1926}\x{1927}-\x{1928}\x{1929}-\x{192B}\x{1930}-\x{1931}\x{1932}\x{1933}-\x{1938}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A19}-\x{1A1A}\x{1A1B}\x{1A55}\x{1A56}\x{1A57}\x{1A58}-\x{1A5E}\x{1A60}\x{1A61}\x{1A62}\x{1A63}-\x{1A64}\x{1A65}-\x{1A6C}\x{1A6D}-\x{1A72}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B04}\x{1B34}\x{1B35}\x{1B36}-\x{1B3A}\x{1B3B}\x{1B3C}\x{1B3D}-\x{1B41}\x{1B42}\x{1B43}-\x{1B44}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1B82}\x{1BA1}\x{1BA2}-\x{1BA5}\x{1BA6}-\x{1BA7}\x{1BA8}-\x{1BA9}\x{1BAA}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE7}\x{1BE8}-\x{1BE9}\x{1BEA}-\x{1BEC}\x{1BED}\x{1BEE}\x{1BEF}-\x{1BF1}\x{1BF2}-\x{1BF3}\x{1C24}-\x{1C2B}\x{1C2C}-\x{1C33}\x{1C34}-\x{1C35}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE1}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF7}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{302E}-\x{302F}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A823}-\x{A824}\x{A825}-\x{A826}\x{A827}\x{A82C}\x{A880}-\x{A881}\x{A8B4}-\x{A8C3}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A952}-\x{A953}\x{A980}-\x{A982}\x{A983}\x{A9B3}\x{A9B4}-\x{A9B5}\x{A9B6}-\x{A9B9}\x{A9BA}-\x{A9BB}\x{A9BC}-\x{A9BD}\x{A9BE}-\x{A9C0}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA2F}-\x{AA30}\x{AA31}-\x{AA32}\x{AA33}-\x{AA34}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA4D}\x{AA7B}\x{AA7C}\x{AA7D}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEB}\x{AAEC}-\x{AAED}\x{AAEE}-\x{AAEF}\x{AAF5}\x{AAF6}\x{ABE3}-\x{ABE4}\x{ABE5}\x{ABE6}-\x{ABE7}\x{ABE8}\x{ABE9}-\x{ABEA}\x{ABEC}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11000}\x{11001}\x{11002}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{11082}\x{110B0}-\x{110B2}\x{110B3}-\x{110B6}\x{110B7}-\x{110B8}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112C}\x{1112D}-\x{11134}\x{11145}-\x{11146}\x{11173}\x{11180}-\x{11181}\x{11182}\x{111B3}-\x{111B5}\x{111B6}-\x{111BE}\x{111BF}-\x{111C0}\x{111C9}-\x{111CC}\x{111CE}\x{111CF}\x{1122C}-\x{1122E}\x{1122F}-\x{11231}\x{11232}-\x{11233}\x{11234}\x{11235}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E0}-\x{112E2}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{11302}-\x{11303}\x{1133B}-\x{1133C}\x{1133E}-\x{1133F}\x{11340}\x{11341}-\x{11344}\x{11347}-\x{11348}\x{1134B}-\x{1134D}\x{11357}\x{11362}-\x{11363}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11435}-\x{11437}\x{11438}-\x{1143F}\x{11440}-\x{11441}\x{11442}-\x{11444}\x{11445}\x{11446}\x{1145E}\x{114B0}-\x{114B2}\x{114B3}-\x{114B8}\x{114B9}\x{114BA}\x{114BB}-\x{114BE}\x{114BF}-\x{114C0}\x{114C1}\x{114C2}-\x{114C3}\x{115AF}-\x{115B1}\x{115B2}-\x{115B5}\x{115B8}-\x{115BB}\x{115BC}-\x{115BD}\x{115BE}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11630}-\x{11632}\x{11633}-\x{1163A}\x{1163B}-\x{1163C}\x{1163D}\x{1163E}\x{1163F}-\x{11640}\x{116AB}\x{116AC}\x{116AD}\x{116AE}-\x{116AF}\x{116B0}-\x{116B5}\x{116B6}\x{116B7}\x{1171D}-\x{1171F}\x{11720}-\x{11721}\x{11722}-\x{11725}\x{11726}\x{11727}-\x{1172B}\x{1182C}-\x{1182E}\x{1182F}-\x{11837}\x{11838}\x{11839}-\x{1183A}\x{11930}-\x{11935}\x{11937}-\x{11938}\x{1193B}-\x{1193C}\x{1193D}\x{1193E}\x{11940}\x{11942}\x{11943}\x{119D1}-\x{119D3}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119DC}-\x{119DF}\x{119E0}\x{119E4}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A39}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A57}-\x{11A58}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A97}\x{11A98}-\x{11A99}\x{11C2F}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3E}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CA9}\x{11CAA}-\x{11CB0}\x{11CB1}\x{11CB2}-\x{11CB3}\x{11CB4}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D8A}-\x{11D8E}\x{11D90}-\x{11D91}\x{11D93}-\x{11D94}\x{11D95}\x{11D96}\x{11D97}\x{11EF3}-\x{11EF4}\x{11EF5}-\x{11EF6}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F51}-\x{16F87}\x{16F8F}-\x{16F92}\x{16FE4}\x{16FF0}-\x{16FF1}\x{1BC9D}-\x{1BC9E}\x{1D165}-\x{1D166}\x{1D167}-\x{1D169}\x{1D16D}-\x{1D172}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{E0100}-\x{E01EF}]/u', + + REGEX_RTL_LABEL = '/[\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06DD}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08E2}\x{200F}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}]/u', + + REGEX_BIDI_STEP_1_LTR = '/^[^\x{0000}-\x{0008}\x{0009}\x{000A}\x{000B}\x{000C}\x{000D}\x{000E}-\x{001B}\x{001C}-\x{001E}\x{001F}\x{0020}\x{0021}-\x{0022}\x{0023}\x{0024}\x{0025}\x{0026}-\x{0027}\x{0028}\x{0029}\x{002A}\x{002B}\x{002C}\x{002D}\x{002E}-\x{002F}\x{0030}-\x{0039}\x{003A}\x{003B}\x{003C}-\x{003E}\x{003F}-\x{0040}\x{005B}\x{005C}\x{005D}\x{005E}\x{005F}\x{0060}\x{007B}\x{007C}\x{007D}\x{007E}\x{007F}-\x{0084}\x{0085}\x{0086}-\x{009F}\x{00A0}\x{00A1}\x{00A2}-\x{00A5}\x{00A6}\x{00A7}\x{00A8}\x{00A9}\x{00AB}\x{00AC}\x{00AD}\x{00AE}\x{00AF}\x{00B0}\x{00B1}\x{00B2}-\x{00B3}\x{00B4}\x{00B6}-\x{00B7}\x{00B8}\x{00B9}\x{00BB}\x{00BC}-\x{00BE}\x{00BF}\x{00D7}\x{00F7}\x{02B9}-\x{02BA}\x{02C2}-\x{02C5}\x{02C6}-\x{02CF}\x{02D2}-\x{02DF}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037E}\x{0384}-\x{0385}\x{0387}\x{03F6}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{058A}\x{058D}-\x{058E}\x{058F}\x{0590}\x{0591}-\x{05BD}\x{05BE}\x{05BF}\x{05C0}\x{05C1}-\x{05C2}\x{05C3}\x{05C4}-\x{05C5}\x{05C6}\x{05C7}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0606}-\x{0607}\x{0608}\x{0609}-\x{060A}\x{060B}\x{060C}\x{060D}\x{060E}-\x{060F}\x{0610}-\x{061A}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{064B}-\x{065F}\x{0660}-\x{0669}\x{066A}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0670}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06D6}-\x{06DC}\x{06DD}\x{06DE}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06E9}\x{06EA}-\x{06ED}\x{06EE}-\x{06EF}\x{06F0}-\x{06F9}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0711}\x{0712}-\x{072F}\x{0730}-\x{074A}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07A6}-\x{07B0}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07F6}\x{07F7}-\x{07F9}\x{07FA}\x{07FB}-\x{07FC}\x{07FD}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{0859}-\x{085B}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09F2}-\x{09F3}\x{09FB}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AF1}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0BF3}-\x{0BF8}\x{0BF9}\x{0BFA}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C78}-\x{0C7E}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E3F}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3A}\x{0F3B}\x{0F3C}\x{0F3D}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1390}-\x{1399}\x{1400}\x{1680}\x{169B}\x{169C}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DB}\x{17DD}\x{17F0}-\x{17F9}\x{1800}-\x{1805}\x{1806}\x{1807}-\x{180A}\x{180B}-\x{180D}\x{180E}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1940}\x{1944}-\x{1945}\x{19DE}-\x{19FF}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{2000}-\x{200A}\x{200B}-\x{200D}\x{200F}\x{2010}-\x{2015}\x{2016}-\x{2017}\x{2018}\x{2019}\x{201A}\x{201B}-\x{201C}\x{201D}\x{201E}\x{201F}\x{2020}-\x{2027}\x{2028}\x{2029}\x{202A}\x{202B}\x{202C}\x{202D}\x{202E}\x{202F}\x{2030}-\x{2034}\x{2035}-\x{2038}\x{2039}\x{203A}\x{203B}-\x{203E}\x{203F}-\x{2040}\x{2041}-\x{2043}\x{2044}\x{2045}\x{2046}\x{2047}-\x{2051}\x{2052}\x{2053}\x{2054}\x{2055}-\x{205E}\x{205F}\x{2060}-\x{2064}\x{2065}\x{2066}\x{2067}\x{2068}\x{2069}\x{206A}-\x{206F}\x{2070}\x{2074}-\x{2079}\x{207A}-\x{207B}\x{207C}\x{207D}\x{207E}\x{2080}-\x{2089}\x{208A}-\x{208B}\x{208C}\x{208D}\x{208E}\x{20A0}-\x{20BF}\x{20C0}-\x{20CF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}\x{2116}-\x{2117}\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}\x{214B}\x{214C}-\x{214D}\x{2150}-\x{215F}\x{2189}\x{218A}-\x{218B}\x{2190}-\x{2194}\x{2195}-\x{2199}\x{219A}-\x{219B}\x{219C}-\x{219F}\x{21A0}\x{21A1}-\x{21A2}\x{21A3}\x{21A4}-\x{21A5}\x{21A6}\x{21A7}-\x{21AD}\x{21AE}\x{21AF}-\x{21CD}\x{21CE}-\x{21CF}\x{21D0}-\x{21D1}\x{21D2}\x{21D3}\x{21D4}\x{21D5}-\x{21F3}\x{21F4}-\x{2211}\x{2212}\x{2213}\x{2214}-\x{22FF}\x{2300}-\x{2307}\x{2308}\x{2309}\x{230A}\x{230B}\x{230C}-\x{231F}\x{2320}-\x{2321}\x{2322}-\x{2328}\x{2329}\x{232A}\x{232B}-\x{2335}\x{237B}\x{237C}\x{237D}-\x{2394}\x{2396}-\x{239A}\x{239B}-\x{23B3}\x{23B4}-\x{23DB}\x{23DC}-\x{23E1}\x{23E2}-\x{2426}\x{2440}-\x{244A}\x{2460}-\x{2487}\x{2488}-\x{249B}\x{24EA}-\x{24FF}\x{2500}-\x{25B6}\x{25B7}\x{25B8}-\x{25C0}\x{25C1}\x{25C2}-\x{25F7}\x{25F8}-\x{25FF}\x{2600}-\x{266E}\x{266F}\x{2670}-\x{26AB}\x{26AD}-\x{2767}\x{2768}\x{2769}\x{276A}\x{276B}\x{276C}\x{276D}\x{276E}\x{276F}\x{2770}\x{2771}\x{2772}\x{2773}\x{2774}\x{2775}\x{2776}-\x{2793}\x{2794}-\x{27BF}\x{27C0}-\x{27C4}\x{27C5}\x{27C6}\x{27C7}-\x{27E5}\x{27E6}\x{27E7}\x{27E8}\x{27E9}\x{27EA}\x{27EB}\x{27EC}\x{27ED}\x{27EE}\x{27EF}\x{27F0}-\x{27FF}\x{2900}-\x{2982}\x{2983}\x{2984}\x{2985}\x{2986}\x{2987}\x{2988}\x{2989}\x{298A}\x{298B}\x{298C}\x{298D}\x{298E}\x{298F}\x{2990}\x{2991}\x{2992}\x{2993}\x{2994}\x{2995}\x{2996}\x{2997}\x{2998}\x{2999}-\x{29D7}\x{29D8}\x{29D9}\x{29DA}\x{29DB}\x{29DC}-\x{29FB}\x{29FC}\x{29FD}\x{29FE}-\x{2AFF}\x{2B00}-\x{2B2F}\x{2B30}-\x{2B44}\x{2B45}-\x{2B46}\x{2B47}-\x{2B4C}\x{2B4D}-\x{2B73}\x{2B76}-\x{2B95}\x{2B97}-\x{2BFF}\x{2CE5}-\x{2CEA}\x{2CEF}-\x{2CF1}\x{2CF9}-\x{2CFC}\x{2CFD}\x{2CFE}-\x{2CFF}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E00}-\x{2E01}\x{2E02}\x{2E03}\x{2E04}\x{2E05}\x{2E06}-\x{2E08}\x{2E09}\x{2E0A}\x{2E0B}\x{2E0C}\x{2E0D}\x{2E0E}-\x{2E16}\x{2E17}\x{2E18}-\x{2E19}\x{2E1A}\x{2E1B}\x{2E1C}\x{2E1D}\x{2E1E}-\x{2E1F}\x{2E20}\x{2E21}\x{2E22}\x{2E23}\x{2E24}\x{2E25}\x{2E26}\x{2E27}\x{2E28}\x{2E29}\x{2E2A}-\x{2E2E}\x{2E2F}\x{2E30}-\x{2E39}\x{2E3A}-\x{2E3B}\x{2E3C}-\x{2E3F}\x{2E40}\x{2E41}\x{2E42}\x{2E43}-\x{2E4F}\x{2E50}-\x{2E51}\x{2E52}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{2FFB}\x{3000}\x{3001}-\x{3003}\x{3004}\x{3008}\x{3009}\x{300A}\x{300B}\x{300C}\x{300D}\x{300E}\x{300F}\x{3010}\x{3011}\x{3012}-\x{3013}\x{3014}\x{3015}\x{3016}\x{3017}\x{3018}\x{3019}\x{301A}\x{301B}\x{301C}\x{301D}\x{301E}-\x{301F}\x{3020}\x{302A}-\x{302D}\x{3030}\x{3036}-\x{3037}\x{303D}\x{303E}-\x{303F}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{31C0}-\x{31E3}\x{321D}-\x{321E}\x{3250}\x{3251}-\x{325F}\x{327C}-\x{327E}\x{32B1}-\x{32BF}\x{32CC}-\x{32CF}\x{3377}-\x{337A}\x{33DE}-\x{33DF}\x{33FF}\x{4DC0}-\x{4DFF}\x{A490}-\x{A4C6}\x{A60D}-\x{A60F}\x{A66F}\x{A670}-\x{A672}\x{A673}\x{A674}-\x{A67D}\x{A67E}\x{A67F}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A788}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A828}-\x{A82B}\x{A82C}\x{A838}\x{A839}\x{A874}-\x{A877}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{AB6A}-\x{AB6B}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1D}\x{FB1E}\x{FB1F}-\x{FB28}\x{FB29}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD3E}\x{FD3F}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDD0}-\x{FDEF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFD}\x{FDFE}-\x{FDFF}\x{FE00}-\x{FE0F}\x{FE10}-\x{FE16}\x{FE17}\x{FE18}\x{FE19}\x{FE20}-\x{FE2F}\x{FE30}\x{FE31}-\x{FE32}\x{FE33}-\x{FE34}\x{FE35}\x{FE36}\x{FE37}\x{FE38}\x{FE39}\x{FE3A}\x{FE3B}\x{FE3C}\x{FE3D}\x{FE3E}\x{FE3F}\x{FE40}\x{FE41}\x{FE42}\x{FE43}\x{FE44}\x{FE45}-\x{FE46}\x{FE47}\x{FE48}\x{FE49}-\x{FE4C}\x{FE4D}-\x{FE4F}\x{FE50}\x{FE51}\x{FE52}\x{FE54}\x{FE55}\x{FE56}-\x{FE57}\x{FE58}\x{FE59}\x{FE5A}\x{FE5B}\x{FE5C}\x{FE5D}\x{FE5E}\x{FE5F}\x{FE60}-\x{FE61}\x{FE62}\x{FE63}\x{FE64}-\x{FE66}\x{FE68}\x{FE69}\x{FE6A}\x{FE6B}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FEFF}\x{FF01}-\x{FF02}\x{FF03}\x{FF04}\x{FF05}\x{FF06}-\x{FF07}\x{FF08}\x{FF09}\x{FF0A}\x{FF0B}\x{FF0C}\x{FF0D}\x{FF0E}-\x{FF0F}\x{FF10}-\x{FF19}\x{FF1A}\x{FF1B}\x{FF1C}-\x{FF1E}\x{FF1F}-\x{FF20}\x{FF3B}\x{FF3C}\x{FF3D}\x{FF3E}\x{FF3F}\x{FF40}\x{FF5B}\x{FF5C}\x{FF5D}\x{FF5E}\x{FF5F}\x{FF60}\x{FF61}\x{FF62}\x{FF63}\x{FF64}-\x{FF65}\x{FFE0}-\x{FFE1}\x{FFE2}\x{FFE3}\x{FFE4}\x{FFE5}-\x{FFE6}\x{FFE8}\x{FFE9}-\x{FFEC}\x{FFED}-\x{FFEE}\x{FFF0}-\x{FFF8}\x{FFF9}-\x{FFFB}\x{FFFC}-\x{FFFD}\x{FFFE}-\x{FFFF}\x{10101}\x{10140}-\x{10174}\x{10175}-\x{10178}\x{10179}-\x{10189}\x{1018A}-\x{1018B}\x{1018C}\x{10190}-\x{1019C}\x{101A0}\x{101FD}\x{102E0}\x{102E1}-\x{102FB}\x{10376}-\x{1037A}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{1091F}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A01}-\x{10A03}\x{10A04}\x{10A05}-\x{10A06}\x{10A07}-\x{10A0B}\x{10A0C}-\x{10A0F}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A38}-\x{10A3A}\x{10A3B}-\x{10A3E}\x{10A3F}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE5}-\x{10AE6}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B39}-\x{10B3F}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D24}-\x{10D27}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAB}-\x{10EAC}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F46}-\x{10F50}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{11001}\x{11038}-\x{11046}\x{11052}-\x{11065}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{11660}-\x{1166C}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{11FD5}-\x{11FDC}\x{11FDD}-\x{11FE0}\x{11FE1}-\x{11FF1}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE2}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D200}-\x{1D241}\x{1D242}-\x{1D244}\x{1D245}\x{1D300}-\x{1D356}\x{1D6DB}\x{1D715}\x{1D74F}\x{1D789}\x{1D7C3}\x{1D7CE}-\x{1D7FF}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E2FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D0}-\x{1E8D6}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E944}-\x{1E94A}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF0}-\x{1EEF1}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F000}-\x{1F02B}\x{1F030}-\x{1F093}\x{1F0A0}-\x{1F0AE}\x{1F0B1}-\x{1F0BF}\x{1F0C1}-\x{1F0CF}\x{1F0D1}-\x{1F0F5}\x{1F100}-\x{1F10A}\x{1F10B}-\x{1F10C}\x{1F10D}-\x{1F10F}\x{1F12F}\x{1F16A}-\x{1F16F}\x{1F1AD}\x{1F260}-\x{1F265}\x{1F300}-\x{1F3FA}\x{1F3FB}-\x{1F3FF}\x{1F400}-\x{1F6D7}\x{1F6E0}-\x{1F6EC}\x{1F6F0}-\x{1F6FC}\x{1F700}-\x{1F773}\x{1F780}-\x{1F7D8}\x{1F7E0}-\x{1F7EB}\x{1F800}-\x{1F80B}\x{1F810}-\x{1F847}\x{1F850}-\x{1F859}\x{1F860}-\x{1F887}\x{1F890}-\x{1F8AD}\x{1F8B0}-\x{1F8B1}\x{1F900}-\x{1F978}\x{1F97A}-\x{1F9CB}\x{1F9CD}-\x{1FA53}\x{1FA60}-\x{1FA6D}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7A}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAA8}\x{1FAB0}-\x{1FAB6}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD6}\x{1FB00}-\x{1FB92}\x{1FB94}-\x{1FBCA}\x{1FBF0}-\x{1FBF9}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{E0000}\x{E0001}\x{E0002}-\x{E001F}\x{E0020}-\x{E007F}\x{E0080}-\x{E00FF}\x{E0100}-\x{E01EF}\x{E01F0}-\x{E0FFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}]/u', + REGEX_BIDI_STEP_1_RTL = '/^[\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{200F}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}]/u', + REGEX_BIDI_STEP_2 = '/[^\x{0000}-\x{0008}\x{000E}-\x{001B}\x{0021}-\x{0022}\x{0023}\x{0024}\x{0025}\x{0026}-\x{0027}\x{0028}\x{0029}\x{002A}\x{002B}\x{002C}\x{002D}\x{002E}-\x{002F}\x{0030}-\x{0039}\x{003A}\x{003B}\x{003C}-\x{003E}\x{003F}-\x{0040}\x{005B}\x{005C}\x{005D}\x{005E}\x{005F}\x{0060}\x{007B}\x{007C}\x{007D}\x{007E}\x{007F}-\x{0084}\x{0086}-\x{009F}\x{00A0}\x{00A1}\x{00A2}-\x{00A5}\x{00A6}\x{00A7}\x{00A8}\x{00A9}\x{00AB}\x{00AC}\x{00AD}\x{00AE}\x{00AF}\x{00B0}\x{00B1}\x{00B2}-\x{00B3}\x{00B4}\x{00B6}-\x{00B7}\x{00B8}\x{00B9}\x{00BB}\x{00BC}-\x{00BE}\x{00BF}\x{00D7}\x{00F7}\x{02B9}-\x{02BA}\x{02C2}-\x{02C5}\x{02C6}-\x{02CF}\x{02D2}-\x{02DF}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037E}\x{0384}-\x{0385}\x{0387}\x{03F6}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{058A}\x{058D}-\x{058E}\x{058F}\x{0590}\x{0591}-\x{05BD}\x{05BE}\x{05BF}\x{05C0}\x{05C1}-\x{05C2}\x{05C3}\x{05C4}-\x{05C5}\x{05C6}\x{05C7}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0606}-\x{0607}\x{0608}\x{0609}-\x{060A}\x{060B}\x{060C}\x{060D}\x{060E}-\x{060F}\x{0610}-\x{061A}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{064B}-\x{065F}\x{0660}-\x{0669}\x{066A}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0670}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06D6}-\x{06DC}\x{06DD}\x{06DE}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06E9}\x{06EA}-\x{06ED}\x{06EE}-\x{06EF}\x{06F0}-\x{06F9}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0711}\x{0712}-\x{072F}\x{0730}-\x{074A}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07A6}-\x{07B0}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07F6}\x{07F7}-\x{07F9}\x{07FA}\x{07FB}-\x{07FC}\x{07FD}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{0859}-\x{085B}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09F2}-\x{09F3}\x{09FB}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AF1}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0BF3}-\x{0BF8}\x{0BF9}\x{0BFA}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C78}-\x{0C7E}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E3F}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3A}\x{0F3B}\x{0F3C}\x{0F3D}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1390}-\x{1399}\x{1400}\x{169B}\x{169C}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DB}\x{17DD}\x{17F0}-\x{17F9}\x{1800}-\x{1805}\x{1806}\x{1807}-\x{180A}\x{180B}-\x{180D}\x{180E}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1940}\x{1944}-\x{1945}\x{19DE}-\x{19FF}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200D}\x{200F}\x{2010}-\x{2015}\x{2016}-\x{2017}\x{2018}\x{2019}\x{201A}\x{201B}-\x{201C}\x{201D}\x{201E}\x{201F}\x{2020}-\x{2027}\x{202F}\x{2030}-\x{2034}\x{2035}-\x{2038}\x{2039}\x{203A}\x{203B}-\x{203E}\x{203F}-\x{2040}\x{2041}-\x{2043}\x{2044}\x{2045}\x{2046}\x{2047}-\x{2051}\x{2052}\x{2053}\x{2054}\x{2055}-\x{205E}\x{2060}-\x{2064}\x{2065}\x{206A}-\x{206F}\x{2070}\x{2074}-\x{2079}\x{207A}-\x{207B}\x{207C}\x{207D}\x{207E}\x{2080}-\x{2089}\x{208A}-\x{208B}\x{208C}\x{208D}\x{208E}\x{20A0}-\x{20BF}\x{20C0}-\x{20CF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}\x{2116}-\x{2117}\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}\x{214B}\x{214C}-\x{214D}\x{2150}-\x{215F}\x{2189}\x{218A}-\x{218B}\x{2190}-\x{2194}\x{2195}-\x{2199}\x{219A}-\x{219B}\x{219C}-\x{219F}\x{21A0}\x{21A1}-\x{21A2}\x{21A3}\x{21A4}-\x{21A5}\x{21A6}\x{21A7}-\x{21AD}\x{21AE}\x{21AF}-\x{21CD}\x{21CE}-\x{21CF}\x{21D0}-\x{21D1}\x{21D2}\x{21D3}\x{21D4}\x{21D5}-\x{21F3}\x{21F4}-\x{2211}\x{2212}\x{2213}\x{2214}-\x{22FF}\x{2300}-\x{2307}\x{2308}\x{2309}\x{230A}\x{230B}\x{230C}-\x{231F}\x{2320}-\x{2321}\x{2322}-\x{2328}\x{2329}\x{232A}\x{232B}-\x{2335}\x{237B}\x{237C}\x{237D}-\x{2394}\x{2396}-\x{239A}\x{239B}-\x{23B3}\x{23B4}-\x{23DB}\x{23DC}-\x{23E1}\x{23E2}-\x{2426}\x{2440}-\x{244A}\x{2460}-\x{2487}\x{2488}-\x{249B}\x{24EA}-\x{24FF}\x{2500}-\x{25B6}\x{25B7}\x{25B8}-\x{25C0}\x{25C1}\x{25C2}-\x{25F7}\x{25F8}-\x{25FF}\x{2600}-\x{266E}\x{266F}\x{2670}-\x{26AB}\x{26AD}-\x{2767}\x{2768}\x{2769}\x{276A}\x{276B}\x{276C}\x{276D}\x{276E}\x{276F}\x{2770}\x{2771}\x{2772}\x{2773}\x{2774}\x{2775}\x{2776}-\x{2793}\x{2794}-\x{27BF}\x{27C0}-\x{27C4}\x{27C5}\x{27C6}\x{27C7}-\x{27E5}\x{27E6}\x{27E7}\x{27E8}\x{27E9}\x{27EA}\x{27EB}\x{27EC}\x{27ED}\x{27EE}\x{27EF}\x{27F0}-\x{27FF}\x{2900}-\x{2982}\x{2983}\x{2984}\x{2985}\x{2986}\x{2987}\x{2988}\x{2989}\x{298A}\x{298B}\x{298C}\x{298D}\x{298E}\x{298F}\x{2990}\x{2991}\x{2992}\x{2993}\x{2994}\x{2995}\x{2996}\x{2997}\x{2998}\x{2999}-\x{29D7}\x{29D8}\x{29D9}\x{29DA}\x{29DB}\x{29DC}-\x{29FB}\x{29FC}\x{29FD}\x{29FE}-\x{2AFF}\x{2B00}-\x{2B2F}\x{2B30}-\x{2B44}\x{2B45}-\x{2B46}\x{2B47}-\x{2B4C}\x{2B4D}-\x{2B73}\x{2B76}-\x{2B95}\x{2B97}-\x{2BFF}\x{2CE5}-\x{2CEA}\x{2CEF}-\x{2CF1}\x{2CF9}-\x{2CFC}\x{2CFD}\x{2CFE}-\x{2CFF}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E00}-\x{2E01}\x{2E02}\x{2E03}\x{2E04}\x{2E05}\x{2E06}-\x{2E08}\x{2E09}\x{2E0A}\x{2E0B}\x{2E0C}\x{2E0D}\x{2E0E}-\x{2E16}\x{2E17}\x{2E18}-\x{2E19}\x{2E1A}\x{2E1B}\x{2E1C}\x{2E1D}\x{2E1E}-\x{2E1F}\x{2E20}\x{2E21}\x{2E22}\x{2E23}\x{2E24}\x{2E25}\x{2E26}\x{2E27}\x{2E28}\x{2E29}\x{2E2A}-\x{2E2E}\x{2E2F}\x{2E30}-\x{2E39}\x{2E3A}-\x{2E3B}\x{2E3C}-\x{2E3F}\x{2E40}\x{2E41}\x{2E42}\x{2E43}-\x{2E4F}\x{2E50}-\x{2E51}\x{2E52}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{2FFB}\x{3001}-\x{3003}\x{3004}\x{3008}\x{3009}\x{300A}\x{300B}\x{300C}\x{300D}\x{300E}\x{300F}\x{3010}\x{3011}\x{3012}-\x{3013}\x{3014}\x{3015}\x{3016}\x{3017}\x{3018}\x{3019}\x{301A}\x{301B}\x{301C}\x{301D}\x{301E}-\x{301F}\x{3020}\x{302A}-\x{302D}\x{3030}\x{3036}-\x{3037}\x{303D}\x{303E}-\x{303F}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{31C0}-\x{31E3}\x{321D}-\x{321E}\x{3250}\x{3251}-\x{325F}\x{327C}-\x{327E}\x{32B1}-\x{32BF}\x{32CC}-\x{32CF}\x{3377}-\x{337A}\x{33DE}-\x{33DF}\x{33FF}\x{4DC0}-\x{4DFF}\x{A490}-\x{A4C6}\x{A60D}-\x{A60F}\x{A66F}\x{A670}-\x{A672}\x{A673}\x{A674}-\x{A67D}\x{A67E}\x{A67F}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A788}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A828}-\x{A82B}\x{A82C}\x{A838}\x{A839}\x{A874}-\x{A877}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{AB6A}-\x{AB6B}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1D}\x{FB1E}\x{FB1F}-\x{FB28}\x{FB29}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD3E}\x{FD3F}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDD0}-\x{FDEF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFD}\x{FDFE}-\x{FDFF}\x{FE00}-\x{FE0F}\x{FE10}-\x{FE16}\x{FE17}\x{FE18}\x{FE19}\x{FE20}-\x{FE2F}\x{FE30}\x{FE31}-\x{FE32}\x{FE33}-\x{FE34}\x{FE35}\x{FE36}\x{FE37}\x{FE38}\x{FE39}\x{FE3A}\x{FE3B}\x{FE3C}\x{FE3D}\x{FE3E}\x{FE3F}\x{FE40}\x{FE41}\x{FE42}\x{FE43}\x{FE44}\x{FE45}-\x{FE46}\x{FE47}\x{FE48}\x{FE49}-\x{FE4C}\x{FE4D}-\x{FE4F}\x{FE50}\x{FE51}\x{FE52}\x{FE54}\x{FE55}\x{FE56}-\x{FE57}\x{FE58}\x{FE59}\x{FE5A}\x{FE5B}\x{FE5C}\x{FE5D}\x{FE5E}\x{FE5F}\x{FE60}-\x{FE61}\x{FE62}\x{FE63}\x{FE64}-\x{FE66}\x{FE68}\x{FE69}\x{FE6A}\x{FE6B}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FEFF}\x{FF01}-\x{FF02}\x{FF03}\x{FF04}\x{FF05}\x{FF06}-\x{FF07}\x{FF08}\x{FF09}\x{FF0A}\x{FF0B}\x{FF0C}\x{FF0D}\x{FF0E}-\x{FF0F}\x{FF10}-\x{FF19}\x{FF1A}\x{FF1B}\x{FF1C}-\x{FF1E}\x{FF1F}-\x{FF20}\x{FF3B}\x{FF3C}\x{FF3D}\x{FF3E}\x{FF3F}\x{FF40}\x{FF5B}\x{FF5C}\x{FF5D}\x{FF5E}\x{FF5F}\x{FF60}\x{FF61}\x{FF62}\x{FF63}\x{FF64}-\x{FF65}\x{FFE0}-\x{FFE1}\x{FFE2}\x{FFE3}\x{FFE4}\x{FFE5}-\x{FFE6}\x{FFE8}\x{FFE9}-\x{FFEC}\x{FFED}-\x{FFEE}\x{FFF0}-\x{FFF8}\x{FFF9}-\x{FFFB}\x{FFFC}-\x{FFFD}\x{FFFE}-\x{FFFF}\x{10101}\x{10140}-\x{10174}\x{10175}-\x{10178}\x{10179}-\x{10189}\x{1018A}-\x{1018B}\x{1018C}\x{10190}-\x{1019C}\x{101A0}\x{101FD}\x{102E0}\x{102E1}-\x{102FB}\x{10376}-\x{1037A}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{1091F}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A01}-\x{10A03}\x{10A04}\x{10A05}-\x{10A06}\x{10A07}-\x{10A0B}\x{10A0C}-\x{10A0F}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A38}-\x{10A3A}\x{10A3B}-\x{10A3E}\x{10A3F}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE5}-\x{10AE6}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B39}-\x{10B3F}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D24}-\x{10D27}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAB}-\x{10EAC}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F46}-\x{10F50}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{11001}\x{11038}-\x{11046}\x{11052}-\x{11065}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{11660}-\x{1166C}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{11FD5}-\x{11FDC}\x{11FDD}-\x{11FE0}\x{11FE1}-\x{11FF1}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE2}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D200}-\x{1D241}\x{1D242}-\x{1D244}\x{1D245}\x{1D300}-\x{1D356}\x{1D6DB}\x{1D715}\x{1D74F}\x{1D789}\x{1D7C3}\x{1D7CE}-\x{1D7FF}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E2FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D0}-\x{1E8D6}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E944}-\x{1E94A}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF0}-\x{1EEF1}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F000}-\x{1F02B}\x{1F030}-\x{1F093}\x{1F0A0}-\x{1F0AE}\x{1F0B1}-\x{1F0BF}\x{1F0C1}-\x{1F0CF}\x{1F0D1}-\x{1F0F5}\x{1F100}-\x{1F10A}\x{1F10B}-\x{1F10C}\x{1F10D}-\x{1F10F}\x{1F12F}\x{1F16A}-\x{1F16F}\x{1F1AD}\x{1F260}-\x{1F265}\x{1F300}-\x{1F3FA}\x{1F3FB}-\x{1F3FF}\x{1F400}-\x{1F6D7}\x{1F6E0}-\x{1F6EC}\x{1F6F0}-\x{1F6FC}\x{1F700}-\x{1F773}\x{1F780}-\x{1F7D8}\x{1F7E0}-\x{1F7EB}\x{1F800}-\x{1F80B}\x{1F810}-\x{1F847}\x{1F850}-\x{1F859}\x{1F860}-\x{1F887}\x{1F890}-\x{1F8AD}\x{1F8B0}-\x{1F8B1}\x{1F900}-\x{1F978}\x{1F97A}-\x{1F9CB}\x{1F9CD}-\x{1FA53}\x{1FA60}-\x{1FA6D}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7A}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAA8}\x{1FAB0}-\x{1FAB6}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD6}\x{1FB00}-\x{1FB92}\x{1FB94}-\x{1FBCA}\x{1FBF0}-\x{1FBF9}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{E0000}\x{E0001}\x{E0002}-\x{E001F}\x{E0020}-\x{E007F}\x{E0080}-\x{E00FF}\x{E0100}-\x{E01EF}\x{E01F0}-\x{E0FFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}]/u', + REGEX_BIDI_STEP_3 = '/[\x{0030}-\x{0039}\x{00B2}-\x{00B3}\x{00B9}\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06DD}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06F0}-\x{06F9}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08E2}\x{200F}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2488}-\x{249B}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FF10}-\x{FF19}\x{102E1}-\x{102FB}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1D7CE}-\x{1D7FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F100}-\x{1F10A}\x{1FBF0}-\x{1FBF9}][\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1D167}-\x{1D169}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{E0100}-\x{E01EF}]*$/u', + REGEX_BIDI_STEP_4_AN = '/[\x{0600}-\x{0605}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{06DD}\x{08E2}\x{10D30}-\x{10D39}\x{10E60}-\x{10E7E}]/u', + REGEX_BIDI_STEP_4_EN = '/[\x{0030}-\x{0039}\x{00B2}-\x{00B3}\x{00B9}\x{06F0}-\x{06F9}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2488}-\x{249B}\x{FF10}-\x{FF19}\x{102E1}-\x{102FB}\x{1D7CE}-\x{1D7FF}\x{1F100}-\x{1F10A}\x{1FBF0}-\x{1FBF9}]/u', + REGEX_BIDI_STEP_5 = '/[\x{0009}\x{000A}\x{000B}\x{000C}\x{000D}\x{001C}-\x{001E}\x{001F}\x{0020}\x{0085}\x{0590}\x{05BE}\x{05C0}\x{05C3}\x{05C6}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0608}\x{060B}\x{060D}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{0660}-\x{0669}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06DD}\x{06E5}-\x{06E6}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0712}-\x{072F}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07F4}-\x{07F5}\x{07FA}\x{07FB}-\x{07FC}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{081A}\x{0824}\x{0828}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08E2}\x{1680}\x{2000}-\x{200A}\x{200F}\x{2028}\x{2029}\x{202A}\x{202B}\x{202C}\x{202D}\x{202E}\x{205F}\x{2066}\x{2067}\x{2068}\x{2069}\x{3000}\x{FB1D}\x{FB1F}-\x{FB28}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFE}-\x{FDFF}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A04}\x{10A07}-\x{10A0B}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A3B}-\x{10A3E}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}]/u', + REGEX_BIDI_STEP_6 = '/[^\x{0000}-\x{0008}\x{0009}\x{000A}\x{000B}\x{000C}\x{000D}\x{000E}-\x{001B}\x{001C}-\x{001E}\x{001F}\x{0020}\x{0021}-\x{0022}\x{0023}\x{0024}\x{0025}\x{0026}-\x{0027}\x{0028}\x{0029}\x{002A}\x{002B}\x{002C}\x{002D}\x{002E}-\x{002F}\x{003A}\x{003B}\x{003C}-\x{003E}\x{003F}-\x{0040}\x{005B}\x{005C}\x{005D}\x{005E}\x{005F}\x{0060}\x{007B}\x{007C}\x{007D}\x{007E}\x{007F}-\x{0084}\x{0085}\x{0086}-\x{009F}\x{00A0}\x{00A1}\x{00A2}-\x{00A5}\x{00A6}\x{00A7}\x{00A8}\x{00A9}\x{00AB}\x{00AC}\x{00AD}\x{00AE}\x{00AF}\x{00B0}\x{00B1}\x{00B4}\x{00B6}-\x{00B7}\x{00B8}\x{00BB}\x{00BC}-\x{00BE}\x{00BF}\x{00D7}\x{00F7}\x{02B9}-\x{02BA}\x{02C2}-\x{02C5}\x{02C6}-\x{02CF}\x{02D2}-\x{02DF}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037E}\x{0384}-\x{0385}\x{0387}\x{03F6}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{058A}\x{058D}-\x{058E}\x{058F}\x{0590}\x{0591}-\x{05BD}\x{05BE}\x{05BF}\x{05C0}\x{05C1}-\x{05C2}\x{05C3}\x{05C4}-\x{05C5}\x{05C6}\x{05C7}\x{05C8}-\x{05CF}\x{05D0}-\x{05EA}\x{05EB}-\x{05EE}\x{05EF}-\x{05F2}\x{05F3}-\x{05F4}\x{05F5}-\x{05FF}\x{0600}-\x{0605}\x{0606}-\x{0607}\x{0608}\x{0609}-\x{060A}\x{060B}\x{060C}\x{060D}\x{060E}-\x{060F}\x{0610}-\x{061A}\x{061B}\x{061C}\x{061D}\x{061E}-\x{061F}\x{0620}-\x{063F}\x{0640}\x{0641}-\x{064A}\x{064B}-\x{065F}\x{0660}-\x{0669}\x{066A}\x{066B}-\x{066C}\x{066D}\x{066E}-\x{066F}\x{0670}\x{0671}-\x{06D3}\x{06D4}\x{06D5}\x{06D6}-\x{06DC}\x{06DD}\x{06DE}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06E9}\x{06EA}-\x{06ED}\x{06EE}-\x{06EF}\x{06FA}-\x{06FC}\x{06FD}-\x{06FE}\x{06FF}\x{0700}-\x{070D}\x{070E}\x{070F}\x{0710}\x{0711}\x{0712}-\x{072F}\x{0730}-\x{074A}\x{074B}-\x{074C}\x{074D}-\x{07A5}\x{07A6}-\x{07B0}\x{07B1}\x{07B2}-\x{07BF}\x{07C0}-\x{07C9}\x{07CA}-\x{07EA}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07F6}\x{07F7}-\x{07F9}\x{07FA}\x{07FB}-\x{07FC}\x{07FD}\x{07FE}-\x{07FF}\x{0800}-\x{0815}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{082E}-\x{082F}\x{0830}-\x{083E}\x{083F}\x{0840}-\x{0858}\x{0859}-\x{085B}\x{085C}-\x{085D}\x{085E}\x{085F}\x{0860}-\x{086A}\x{086B}-\x{086F}\x{0870}-\x{089F}\x{08A0}-\x{08B4}\x{08B5}\x{08B6}-\x{08C7}\x{08C8}-\x{08D2}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09F2}-\x{09F3}\x{09FB}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AF1}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0BF3}-\x{0BF8}\x{0BF9}\x{0BFA}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C78}-\x{0C7E}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E3F}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F3A}\x{0F3B}\x{0F3C}\x{0F3D}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1390}-\x{1399}\x{1400}\x{1680}\x{169B}\x{169C}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DB}\x{17DD}\x{17F0}-\x{17F9}\x{1800}-\x{1805}\x{1806}\x{1807}-\x{180A}\x{180B}-\x{180D}\x{180E}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1940}\x{1944}-\x{1945}\x{19DE}-\x{19FF}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{2000}-\x{200A}\x{200B}-\x{200D}\x{200F}\x{2010}-\x{2015}\x{2016}-\x{2017}\x{2018}\x{2019}\x{201A}\x{201B}-\x{201C}\x{201D}\x{201E}\x{201F}\x{2020}-\x{2027}\x{2028}\x{2029}\x{202A}\x{202B}\x{202C}\x{202D}\x{202E}\x{202F}\x{2030}-\x{2034}\x{2035}-\x{2038}\x{2039}\x{203A}\x{203B}-\x{203E}\x{203F}-\x{2040}\x{2041}-\x{2043}\x{2044}\x{2045}\x{2046}\x{2047}-\x{2051}\x{2052}\x{2053}\x{2054}\x{2055}-\x{205E}\x{205F}\x{2060}-\x{2064}\x{2065}\x{2066}\x{2067}\x{2068}\x{2069}\x{206A}-\x{206F}\x{207A}-\x{207B}\x{207C}\x{207D}\x{207E}\x{208A}-\x{208B}\x{208C}\x{208D}\x{208E}\x{20A0}-\x{20BF}\x{20C0}-\x{20CF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2100}-\x{2101}\x{2103}-\x{2106}\x{2108}-\x{2109}\x{2114}\x{2116}-\x{2117}\x{2118}\x{211E}-\x{2123}\x{2125}\x{2127}\x{2129}\x{212E}\x{213A}-\x{213B}\x{2140}-\x{2144}\x{214A}\x{214B}\x{214C}-\x{214D}\x{2150}-\x{215F}\x{2189}\x{218A}-\x{218B}\x{2190}-\x{2194}\x{2195}-\x{2199}\x{219A}-\x{219B}\x{219C}-\x{219F}\x{21A0}\x{21A1}-\x{21A2}\x{21A3}\x{21A4}-\x{21A5}\x{21A6}\x{21A7}-\x{21AD}\x{21AE}\x{21AF}-\x{21CD}\x{21CE}-\x{21CF}\x{21D0}-\x{21D1}\x{21D2}\x{21D3}\x{21D4}\x{21D5}-\x{21F3}\x{21F4}-\x{2211}\x{2212}\x{2213}\x{2214}-\x{22FF}\x{2300}-\x{2307}\x{2308}\x{2309}\x{230A}\x{230B}\x{230C}-\x{231F}\x{2320}-\x{2321}\x{2322}-\x{2328}\x{2329}\x{232A}\x{232B}-\x{2335}\x{237B}\x{237C}\x{237D}-\x{2394}\x{2396}-\x{239A}\x{239B}-\x{23B3}\x{23B4}-\x{23DB}\x{23DC}-\x{23E1}\x{23E2}-\x{2426}\x{2440}-\x{244A}\x{2460}-\x{2487}\x{24EA}-\x{24FF}\x{2500}-\x{25B6}\x{25B7}\x{25B8}-\x{25C0}\x{25C1}\x{25C2}-\x{25F7}\x{25F8}-\x{25FF}\x{2600}-\x{266E}\x{266F}\x{2670}-\x{26AB}\x{26AD}-\x{2767}\x{2768}\x{2769}\x{276A}\x{276B}\x{276C}\x{276D}\x{276E}\x{276F}\x{2770}\x{2771}\x{2772}\x{2773}\x{2774}\x{2775}\x{2776}-\x{2793}\x{2794}-\x{27BF}\x{27C0}-\x{27C4}\x{27C5}\x{27C6}\x{27C7}-\x{27E5}\x{27E6}\x{27E7}\x{27E8}\x{27E9}\x{27EA}\x{27EB}\x{27EC}\x{27ED}\x{27EE}\x{27EF}\x{27F0}-\x{27FF}\x{2900}-\x{2982}\x{2983}\x{2984}\x{2985}\x{2986}\x{2987}\x{2988}\x{2989}\x{298A}\x{298B}\x{298C}\x{298D}\x{298E}\x{298F}\x{2990}\x{2991}\x{2992}\x{2993}\x{2994}\x{2995}\x{2996}\x{2997}\x{2998}\x{2999}-\x{29D7}\x{29D8}\x{29D9}\x{29DA}\x{29DB}\x{29DC}-\x{29FB}\x{29FC}\x{29FD}\x{29FE}-\x{2AFF}\x{2B00}-\x{2B2F}\x{2B30}-\x{2B44}\x{2B45}-\x{2B46}\x{2B47}-\x{2B4C}\x{2B4D}-\x{2B73}\x{2B76}-\x{2B95}\x{2B97}-\x{2BFF}\x{2CE5}-\x{2CEA}\x{2CEF}-\x{2CF1}\x{2CF9}-\x{2CFC}\x{2CFD}\x{2CFE}-\x{2CFF}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E00}-\x{2E01}\x{2E02}\x{2E03}\x{2E04}\x{2E05}\x{2E06}-\x{2E08}\x{2E09}\x{2E0A}\x{2E0B}\x{2E0C}\x{2E0D}\x{2E0E}-\x{2E16}\x{2E17}\x{2E18}-\x{2E19}\x{2E1A}\x{2E1B}\x{2E1C}\x{2E1D}\x{2E1E}-\x{2E1F}\x{2E20}\x{2E21}\x{2E22}\x{2E23}\x{2E24}\x{2E25}\x{2E26}\x{2E27}\x{2E28}\x{2E29}\x{2E2A}-\x{2E2E}\x{2E2F}\x{2E30}-\x{2E39}\x{2E3A}-\x{2E3B}\x{2E3C}-\x{2E3F}\x{2E40}\x{2E41}\x{2E42}\x{2E43}-\x{2E4F}\x{2E50}-\x{2E51}\x{2E52}\x{2E80}-\x{2E99}\x{2E9B}-\x{2EF3}\x{2F00}-\x{2FD5}\x{2FF0}-\x{2FFB}\x{3000}\x{3001}-\x{3003}\x{3004}\x{3008}\x{3009}\x{300A}\x{300B}\x{300C}\x{300D}\x{300E}\x{300F}\x{3010}\x{3011}\x{3012}-\x{3013}\x{3014}\x{3015}\x{3016}\x{3017}\x{3018}\x{3019}\x{301A}\x{301B}\x{301C}\x{301D}\x{301E}-\x{301F}\x{3020}\x{302A}-\x{302D}\x{3030}\x{3036}-\x{3037}\x{303D}\x{303E}-\x{303F}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{30A0}\x{30FB}\x{31C0}-\x{31E3}\x{321D}-\x{321E}\x{3250}\x{3251}-\x{325F}\x{327C}-\x{327E}\x{32B1}-\x{32BF}\x{32CC}-\x{32CF}\x{3377}-\x{337A}\x{33DE}-\x{33DF}\x{33FF}\x{4DC0}-\x{4DFF}\x{A490}-\x{A4C6}\x{A60D}-\x{A60F}\x{A66F}\x{A670}-\x{A672}\x{A673}\x{A674}-\x{A67D}\x{A67E}\x{A67F}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A788}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A828}-\x{A82B}\x{A82C}\x{A838}\x{A839}\x{A874}-\x{A877}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{AB6A}-\x{AB6B}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1D}\x{FB1E}\x{FB1F}-\x{FB28}\x{FB29}\x{FB2A}-\x{FB36}\x{FB37}\x{FB38}-\x{FB3C}\x{FB3D}\x{FB3E}\x{FB3F}\x{FB40}-\x{FB41}\x{FB42}\x{FB43}-\x{FB44}\x{FB45}\x{FB46}-\x{FB4F}\x{FB50}-\x{FBB1}\x{FBB2}-\x{FBC1}\x{FBC2}-\x{FBD2}\x{FBD3}-\x{FD3D}\x{FD3E}\x{FD3F}\x{FD40}-\x{FD4F}\x{FD50}-\x{FD8F}\x{FD90}-\x{FD91}\x{FD92}-\x{FDC7}\x{FDC8}-\x{FDCF}\x{FDD0}-\x{FDEF}\x{FDF0}-\x{FDFB}\x{FDFC}\x{FDFD}\x{FDFE}-\x{FDFF}\x{FE00}-\x{FE0F}\x{FE10}-\x{FE16}\x{FE17}\x{FE18}\x{FE19}\x{FE20}-\x{FE2F}\x{FE30}\x{FE31}-\x{FE32}\x{FE33}-\x{FE34}\x{FE35}\x{FE36}\x{FE37}\x{FE38}\x{FE39}\x{FE3A}\x{FE3B}\x{FE3C}\x{FE3D}\x{FE3E}\x{FE3F}\x{FE40}\x{FE41}\x{FE42}\x{FE43}\x{FE44}\x{FE45}-\x{FE46}\x{FE47}\x{FE48}\x{FE49}-\x{FE4C}\x{FE4D}-\x{FE4F}\x{FE50}\x{FE51}\x{FE52}\x{FE54}\x{FE55}\x{FE56}-\x{FE57}\x{FE58}\x{FE59}\x{FE5A}\x{FE5B}\x{FE5C}\x{FE5D}\x{FE5E}\x{FE5F}\x{FE60}-\x{FE61}\x{FE62}\x{FE63}\x{FE64}-\x{FE66}\x{FE68}\x{FE69}\x{FE6A}\x{FE6B}\x{FE70}-\x{FE74}\x{FE75}\x{FE76}-\x{FEFC}\x{FEFD}-\x{FEFE}\x{FEFF}\x{FF01}-\x{FF02}\x{FF03}\x{FF04}\x{FF05}\x{FF06}-\x{FF07}\x{FF08}\x{FF09}\x{FF0A}\x{FF0B}\x{FF0C}\x{FF0D}\x{FF0E}-\x{FF0F}\x{FF1A}\x{FF1B}\x{FF1C}-\x{FF1E}\x{FF1F}-\x{FF20}\x{FF3B}\x{FF3C}\x{FF3D}\x{FF3E}\x{FF3F}\x{FF40}\x{FF5B}\x{FF5C}\x{FF5D}\x{FF5E}\x{FF5F}\x{FF60}\x{FF61}\x{FF62}\x{FF63}\x{FF64}-\x{FF65}\x{FFE0}-\x{FFE1}\x{FFE2}\x{FFE3}\x{FFE4}\x{FFE5}-\x{FFE6}\x{FFE8}\x{FFE9}-\x{FFEC}\x{FFED}-\x{FFEE}\x{FFF0}-\x{FFF8}\x{FFF9}-\x{FFFB}\x{FFFC}-\x{FFFD}\x{FFFE}-\x{FFFF}\x{10101}\x{10140}-\x{10174}\x{10175}-\x{10178}\x{10179}-\x{10189}\x{1018A}-\x{1018B}\x{1018C}\x{10190}-\x{1019C}\x{101A0}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10800}-\x{10805}\x{10806}-\x{10807}\x{10808}\x{10809}\x{1080A}-\x{10835}\x{10836}\x{10837}-\x{10838}\x{10839}-\x{1083B}\x{1083C}\x{1083D}-\x{1083E}\x{1083F}-\x{10855}\x{10856}\x{10857}\x{10858}-\x{1085F}\x{10860}-\x{10876}\x{10877}-\x{10878}\x{10879}-\x{1087F}\x{10880}-\x{1089E}\x{1089F}-\x{108A6}\x{108A7}-\x{108AF}\x{108B0}-\x{108DF}\x{108E0}-\x{108F2}\x{108F3}\x{108F4}-\x{108F5}\x{108F6}-\x{108FA}\x{108FB}-\x{108FF}\x{10900}-\x{10915}\x{10916}-\x{1091B}\x{1091C}-\x{1091E}\x{1091F}\x{10920}-\x{10939}\x{1093A}-\x{1093E}\x{1093F}\x{10940}-\x{1097F}\x{10980}-\x{109B7}\x{109B8}-\x{109BB}\x{109BC}-\x{109BD}\x{109BE}-\x{109BF}\x{109C0}-\x{109CF}\x{109D0}-\x{109D1}\x{109D2}-\x{109FF}\x{10A00}\x{10A01}-\x{10A03}\x{10A04}\x{10A05}-\x{10A06}\x{10A07}-\x{10A0B}\x{10A0C}-\x{10A0F}\x{10A10}-\x{10A13}\x{10A14}\x{10A15}-\x{10A17}\x{10A18}\x{10A19}-\x{10A35}\x{10A36}-\x{10A37}\x{10A38}-\x{10A3A}\x{10A3B}-\x{10A3E}\x{10A3F}\x{10A40}-\x{10A48}\x{10A49}-\x{10A4F}\x{10A50}-\x{10A58}\x{10A59}-\x{10A5F}\x{10A60}-\x{10A7C}\x{10A7D}-\x{10A7E}\x{10A7F}\x{10A80}-\x{10A9C}\x{10A9D}-\x{10A9F}\x{10AA0}-\x{10ABF}\x{10AC0}-\x{10AC7}\x{10AC8}\x{10AC9}-\x{10AE4}\x{10AE5}-\x{10AE6}\x{10AE7}-\x{10AEA}\x{10AEB}-\x{10AEF}\x{10AF0}-\x{10AF6}\x{10AF7}-\x{10AFF}\x{10B00}-\x{10B35}\x{10B36}-\x{10B38}\x{10B39}-\x{10B3F}\x{10B40}-\x{10B55}\x{10B56}-\x{10B57}\x{10B58}-\x{10B5F}\x{10B60}-\x{10B72}\x{10B73}-\x{10B77}\x{10B78}-\x{10B7F}\x{10B80}-\x{10B91}\x{10B92}-\x{10B98}\x{10B99}-\x{10B9C}\x{10B9D}-\x{10BA8}\x{10BA9}-\x{10BAF}\x{10BB0}-\x{10BFF}\x{10C00}-\x{10C48}\x{10C49}-\x{10C7F}\x{10C80}-\x{10CB2}\x{10CB3}-\x{10CBF}\x{10CC0}-\x{10CF2}\x{10CF3}-\x{10CF9}\x{10CFA}-\x{10CFF}\x{10D00}-\x{10D23}\x{10D24}-\x{10D27}\x{10D28}-\x{10D2F}\x{10D30}-\x{10D39}\x{10D3A}-\x{10D3F}\x{10D40}-\x{10E5F}\x{10E60}-\x{10E7E}\x{10E7F}\x{10E80}-\x{10EA9}\x{10EAA}\x{10EAB}-\x{10EAC}\x{10EAD}\x{10EAE}-\x{10EAF}\x{10EB0}-\x{10EB1}\x{10EB2}-\x{10EFF}\x{10F00}-\x{10F1C}\x{10F1D}-\x{10F26}\x{10F27}\x{10F28}-\x{10F2F}\x{10F30}-\x{10F45}\x{10F46}-\x{10F50}\x{10F51}-\x{10F54}\x{10F55}-\x{10F59}\x{10F5A}-\x{10F6F}\x{10F70}-\x{10FAF}\x{10FB0}-\x{10FC4}\x{10FC5}-\x{10FCB}\x{10FCC}-\x{10FDF}\x{10FE0}-\x{10FF6}\x{10FF7}-\x{10FFF}\x{11001}\x{11038}-\x{11046}\x{11052}-\x{11065}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{11660}-\x{1166C}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{11FD5}-\x{11FDC}\x{11FDD}-\x{11FE0}\x{11FE1}-\x{11FF1}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE2}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D200}-\x{1D241}\x{1D242}-\x{1D244}\x{1D245}\x{1D300}-\x{1D356}\x{1D6DB}\x{1D715}\x{1D74F}\x{1D789}\x{1D7C3}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E2FF}\x{1E800}-\x{1E8C4}\x{1E8C5}-\x{1E8C6}\x{1E8C7}-\x{1E8CF}\x{1E8D0}-\x{1E8D6}\x{1E8D7}-\x{1E8FF}\x{1E900}-\x{1E943}\x{1E944}-\x{1E94A}\x{1E94B}\x{1E94C}-\x{1E94F}\x{1E950}-\x{1E959}\x{1E95A}-\x{1E95D}\x{1E95E}-\x{1E95F}\x{1E960}-\x{1EC6F}\x{1EC70}\x{1EC71}-\x{1ECAB}\x{1ECAC}\x{1ECAD}-\x{1ECAF}\x{1ECB0}\x{1ECB1}-\x{1ECB4}\x{1ECB5}-\x{1ECBF}\x{1ECC0}-\x{1ECFF}\x{1ED00}\x{1ED01}-\x{1ED2D}\x{1ED2E}\x{1ED2F}-\x{1ED3D}\x{1ED3E}-\x{1ED4F}\x{1ED50}-\x{1EDFF}\x{1EE00}-\x{1EE03}\x{1EE04}\x{1EE05}-\x{1EE1F}\x{1EE20}\x{1EE21}-\x{1EE22}\x{1EE23}\x{1EE24}\x{1EE25}-\x{1EE26}\x{1EE27}\x{1EE28}\x{1EE29}-\x{1EE32}\x{1EE33}\x{1EE34}-\x{1EE37}\x{1EE38}\x{1EE39}\x{1EE3A}\x{1EE3B}\x{1EE3C}-\x{1EE41}\x{1EE42}\x{1EE43}-\x{1EE46}\x{1EE47}\x{1EE48}\x{1EE49}\x{1EE4A}\x{1EE4B}\x{1EE4C}\x{1EE4D}-\x{1EE4F}\x{1EE50}\x{1EE51}-\x{1EE52}\x{1EE53}\x{1EE54}\x{1EE55}-\x{1EE56}\x{1EE57}\x{1EE58}\x{1EE59}\x{1EE5A}\x{1EE5B}\x{1EE5C}\x{1EE5D}\x{1EE5E}\x{1EE5F}\x{1EE60}\x{1EE61}-\x{1EE62}\x{1EE63}\x{1EE64}\x{1EE65}-\x{1EE66}\x{1EE67}-\x{1EE6A}\x{1EE6B}\x{1EE6C}-\x{1EE72}\x{1EE73}\x{1EE74}-\x{1EE77}\x{1EE78}\x{1EE79}-\x{1EE7C}\x{1EE7D}\x{1EE7E}\x{1EE7F}\x{1EE80}-\x{1EE89}\x{1EE8A}\x{1EE8B}-\x{1EE9B}\x{1EE9C}-\x{1EEA0}\x{1EEA1}-\x{1EEA3}\x{1EEA4}\x{1EEA5}-\x{1EEA9}\x{1EEAA}\x{1EEAB}-\x{1EEBB}\x{1EEBC}-\x{1EEEF}\x{1EEF0}-\x{1EEF1}\x{1EEF2}-\x{1EEFF}\x{1EF00}-\x{1EFFF}\x{1F000}-\x{1F02B}\x{1F030}-\x{1F093}\x{1F0A0}-\x{1F0AE}\x{1F0B1}-\x{1F0BF}\x{1F0C1}-\x{1F0CF}\x{1F0D1}-\x{1F0F5}\x{1F10B}-\x{1F10C}\x{1F10D}-\x{1F10F}\x{1F12F}\x{1F16A}-\x{1F16F}\x{1F1AD}\x{1F260}-\x{1F265}\x{1F300}-\x{1F3FA}\x{1F3FB}-\x{1F3FF}\x{1F400}-\x{1F6D7}\x{1F6E0}-\x{1F6EC}\x{1F6F0}-\x{1F6FC}\x{1F700}-\x{1F773}\x{1F780}-\x{1F7D8}\x{1F7E0}-\x{1F7EB}\x{1F800}-\x{1F80B}\x{1F810}-\x{1F847}\x{1F850}-\x{1F859}\x{1F860}-\x{1F887}\x{1F890}-\x{1F8AD}\x{1F8B0}-\x{1F8B1}\x{1F900}-\x{1F978}\x{1F97A}-\x{1F9CB}\x{1F9CD}-\x{1FA53}\x{1FA60}-\x{1FA6D}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7A}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAA8}\x{1FAB0}-\x{1FAB6}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD6}\x{1FB00}-\x{1FB92}\x{1FB94}-\x{1FBCA}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{E0000}\x{E0001}\x{E0002}-\x{E001F}\x{E0020}-\x{E007F}\x{E0080}-\x{E00FF}\x{E0100}-\x{E01EF}\x{E01F0}-\x{E0FFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}][\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A06}\x{11A09}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1D167}-\x{1D169}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{E0100}-\x{E01EF}]*$/u', + + REGEX_ZWNJ = '/([\x{A872}\x{10ACD}\x{10AD7}\x{10D00}\x{10FCB}\x{0620}\x{0626}\x{0628}\x{062A}-\x{062E}\x{0633}-\x{063F}\x{0641}-\x{0647}\x{0649}-\x{064A}\x{066E}-\x{066F}\x{0678}-\x{0687}\x{069A}-\x{06BF}\x{06C1}-\x{06C2}\x{06CC}\x{06CE}\x{06D0}-\x{06D1}\x{06FA}-\x{06FC}\x{06FF}\x{0712}-\x{0714}\x{071A}-\x{071D}\x{071F}-\x{0727}\x{0729}\x{072B}\x{072D}-\x{072E}\x{074E}-\x{0758}\x{075C}-\x{076A}\x{076D}-\x{0770}\x{0772}\x{0775}-\x{0777}\x{077A}-\x{077F}\x{07CA}-\x{07EA}\x{0841}-\x{0845}\x{0848}\x{084A}-\x{0853}\x{0855}\x{0860}\x{0862}-\x{0865}\x{0868}\x{08A0}-\x{08A9}\x{08AF}-\x{08B0}\x{08B3}-\x{08B4}\x{08B6}-\x{08B8}\x{08BA}-\x{08C7}\x{1807}\x{1820}-\x{1842}\x{1843}\x{1844}-\x{1878}\x{1887}-\x{18A8}\x{18AA}\x{A840}-\x{A871}\x{10AC0}-\x{10AC4}\x{10AD3}-\x{10AD6}\x{10AD8}-\x{10ADC}\x{10ADE}-\x{10AE0}\x{10AEB}-\x{10AEE}\x{10B80}\x{10B82}\x{10B86}-\x{10B88}\x{10B8A}-\x{10B8B}\x{10B8D}\x{10B90}\x{10BAD}-\x{10BAE}\x{10D01}-\x{10D21}\x{10D23}\x{10F30}-\x{10F32}\x{10F34}-\x{10F44}\x{10F51}-\x{10F53}\x{10FB0}\x{10FB2}-\x{10FB3}\x{10FB8}\x{10FBB}-\x{10FBC}\x{10FBE}-\x{10FBF}\x{10FC1}\x{10FC4}\x{10FCA}\x{1E900}-\x{1E943}][\x{00AD}\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{061C}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{200B}\x{200E}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{206A}-\x{206F}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{FEFF}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{13430}-\x{13438}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1E94B}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}]*\x{200C}[\x{00AD}\x{0300}-\x{036F}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{0610}-\x{061A}\x{061C}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DF}-\x{06E4}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07FD}\x{0816}-\x{0819}\x{081B}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B55}-\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0D81}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EBC}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17DD}\x{180B}-\x{180D}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1ABF}-\x{1AC0}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{200B}\x{200E}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{206A}-\x{206F}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2CEF}-\x{2CF1}\x{2D7F}\x{2DE0}-\x{2DFF}\x{302A}-\x{302D}\x{3099}-\x{309A}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A82C}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}-\x{A9BD}\x{A9E5}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AAEC}-\x{AAED}\x{AAF6}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FE00}-\x{FE0F}\x{FE20}-\x{FE2F}\x{FEFF}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10EAB}-\x{10EAC}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{111CF}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{1193B}-\x{1193C}\x{1193E}\x{11943}\x{119D4}-\x{119D7}\x{119DA}-\x{119DB}\x{119E0}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{13430}-\x{13438}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16F4F}\x{16F8F}-\x{16F92}\x{16FE4}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E130}-\x{1E136}\x{1E2EC}-\x{1E2EF}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1E94B}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}]*)[\x{0622}-\x{0625}\x{0627}\x{0629}\x{062F}-\x{0632}\x{0648}\x{0671}-\x{0673}\x{0675}-\x{0677}\x{0688}-\x{0699}\x{06C0}\x{06C3}-\x{06CB}\x{06CD}\x{06CF}\x{06D2}-\x{06D3}\x{06D5}\x{06EE}-\x{06EF}\x{0710}\x{0715}-\x{0719}\x{071E}\x{0728}\x{072A}\x{072C}\x{072F}\x{074D}\x{0759}-\x{075B}\x{076B}-\x{076C}\x{0771}\x{0773}-\x{0774}\x{0778}-\x{0779}\x{0840}\x{0846}-\x{0847}\x{0849}\x{0854}\x{0856}-\x{0858}\x{0867}\x{0869}-\x{086A}\x{08AA}-\x{08AC}\x{08AE}\x{08B1}-\x{08B2}\x{08B9}\x{10AC5}\x{10AC7}\x{10AC9}-\x{10ACA}\x{10ACE}-\x{10AD2}\x{10ADD}\x{10AE1}\x{10AE4}\x{10AEF}\x{10B81}\x{10B83}-\x{10B85}\x{10B89}\x{10B8C}\x{10B8E}-\x{10B8F}\x{10B91}\x{10BA9}-\x{10BAC}\x{10D22}\x{10F33}\x{10F54}\x{10FB4}-\x{10FB6}\x{10FB9}-\x{10FBA}\x{10FBD}\x{10FC2}-\x{10FC3}\x{10FC9}\x{0620}\x{0626}\x{0628}\x{062A}-\x{062E}\x{0633}-\x{063F}\x{0641}-\x{0647}\x{0649}-\x{064A}\x{066E}-\x{066F}\x{0678}-\x{0687}\x{069A}-\x{06BF}\x{06C1}-\x{06C2}\x{06CC}\x{06CE}\x{06D0}-\x{06D1}\x{06FA}-\x{06FC}\x{06FF}\x{0712}-\x{0714}\x{071A}-\x{071D}\x{071F}-\x{0727}\x{0729}\x{072B}\x{072D}-\x{072E}\x{074E}-\x{0758}\x{075C}-\x{076A}\x{076D}-\x{0770}\x{0772}\x{0775}-\x{0777}\x{077A}-\x{077F}\x{07CA}-\x{07EA}\x{0841}-\x{0845}\x{0848}\x{084A}-\x{0853}\x{0855}\x{0860}\x{0862}-\x{0865}\x{0868}\x{08A0}-\x{08A9}\x{08AF}-\x{08B0}\x{08B3}-\x{08B4}\x{08B6}-\x{08B8}\x{08BA}-\x{08C7}\x{1807}\x{1820}-\x{1842}\x{1843}\x{1844}-\x{1878}\x{1887}-\x{18A8}\x{18AA}\x{A840}-\x{A871}\x{10AC0}-\x{10AC4}\x{10AD3}-\x{10AD6}\x{10AD8}-\x{10ADC}\x{10ADE}-\x{10AE0}\x{10AEB}-\x{10AEE}\x{10B80}\x{10B82}\x{10B86}-\x{10B88}\x{10B8A}-\x{10B8B}\x{10B8D}\x{10B90}\x{10BAD}-\x{10BAE}\x{10D01}-\x{10D21}\x{10D23}\x{10F30}-\x{10F32}\x{10F34}-\x{10F44}\x{10F51}-\x{10F53}\x{10FB0}\x{10FB2}-\x{10FB3}\x{10FB8}\x{10FBB}-\x{10FBC}\x{10FBE}-\x{10FBF}\x{10FC1}\x{10FC4}\x{10FCA}\x{1E900}-\x{1E943}]/u'; + + /** + * Contains the numeric value of a basic code point (for use in representing integers) in the + * range 0 to BASE-1, or -1 if b is does not represent a value. + * + * @var array + */ + private static $basicToDigit = [ + 48 => 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 65 => 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 97 => 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + ]; + + /** + * @var array + */ + private static $virama; + + /** + * @var array + */ + private static $mapped; + + /** + * @var array + */ + private static $ignored; + + /** + * @var array + */ + private static $deviation; + + /** + * @var array + */ + private static $disallowed; + + /** + * @var array + */ + private static $disallowed_STD3_mapped; + + /** + * @var array + */ + private static $disallowed_STD3_valid; + + /** + * @var bool + */ + private static $mappingTableLoaded = false; + + /** + * @see https://www.unicode.org/reports/tr46/#ToASCII + * + * @param string $domainName + * + * @return string|false + */ + public static function toAscii($domainName) + { + $info = new Info(); + $labels = self::process((string) $domainName, $info); + + foreach ($labels as $i => $label) { + // Only convert labels to punycode that contain non-ASCII code points + if (1 === \preg_match('/[^\x00-\x7F]/', $label)) { + try { + $label = 'xn--'.self::punycodeEncode($label); + } catch (\Exception $e) { + $info->errors |= self::ERROR_PUNYCODE; + } + + $labels[$i] = $label; + } + } + + self::validateDomainAndLabelLength($labels, $info); + + return $info->errors ? false : \implode('.', $labels); + } + + /** + * @see https://www.unicode.org/reports/tr46/#ToUnicode + * + * @param string $domainName + * + * @return string|false + */ + public static function toUtf8($domainName) + { + $info = new Info(); + $labels = self::process((string) $domainName, $info); + return 0 === $info->errors ? \implode('.', $labels) : false; + } + + /** + * @param string $label + * + * @return bool + */ + private static function isValidContextJ(array $codePoints, $label) + { + if (!isset(self::$virama)) { + self::$virama = static::getData('virama'); + } + + $offset = 0; + + foreach ($codePoints as $i => $codePoint) { + if (0x200C !== $codePoint && 0x200D !== $codePoint) { + continue; + } + + if (!isset($codePoints[$i - 1])) { + return false; + } + + // If Canonical_Combining_Class(Before(cp)) .eq. Virama Then True; + if (isset(self::$virama[$codePoints[$i - 1]])) { + continue; + } + + // If RegExpMatch((Joining_Type:{L,D})(Joining_Type:T)*\u200C(Joining_Type:T)*(Joining_Type:{R,D})) Then + // True; + // Generated RegExp = ([Joining_Type:{L,D}][Joining_Type:T]*\u200C[Joining_Type:T]*)[Joining_Type:{R,D}] + if (0x200C === $codePoint && 1 === \preg_match(static::REGEX_ZWNJ, $label, $matches, \PREG_OFFSET_CAPTURE, $offset)) { + $offset += \strlen($matches[1][0]); + + continue; + } + + return false; + } + + return true; + } + + /** + * @see https://www.unicode.org/reports/tr46/#ProcessingStepMap + * + * @param string $input + * @param array $options + * + * @return string + */ + private static function mapCodePoints($input, array $options, Info $info) + { + $str = ''; + $transitional = $options['Transitional_Processing']; + + foreach (self::utf8Decode($input) as $codePoint) { + $data = self::lookupCodePointStatus($codePoint); + + switch ($data['status']) { + case 'disallowed': + $info->errors |= self::ERROR_DISALLOWED; + + // no break. + + case 'valid': + $str .= \mb_chr($codePoint, 'utf-8'); + + break; + + case 'ignored': + // Do nothing. + break; + + case 'mapped': + $str .= $data['mapping']; + + break; + + case 'deviation': + $info->transitionalDifferent = true; + $str .= ($transitional ? $data['mapping'] : mb_chr($codePoint, 'utf-8')); + + break; + } + } + + return $str; + } + + /** + * @see https://www.unicode.org/reports/tr46/#Processing + * + * @param string $domain + * + * @return array + */ + private static function process($domain, Info $info) + { + if ('' === $domain) { + $info->errors |= self::ERROR_EMPTY_LABEL; + + return [$domain]; + } + + $options = [ + 'CheckBidi' => false, + 'Transitional_Processing' => true + ]; + + // Step 1. Map each code point in the domain name string + $domain = self::mapCodePoints($domain, $options, $info); + + // Step 2. Normalize the domain name string to Unicode Normalization Form C. + $domain = static::normalize($domain); + + // Step 3. Break the string into labels at U+002E (.) FULL STOP. + $labels = explode('.', $domain); + $lastLabelIndex = \count($labels) - 1; + + // Step 4. Convert and validate each label in the domain name string. + foreach ($labels as $i => $label) { + $validationOptions = $options; + + if ('xn--' === \substr($label, 0, 4)) { + try { + $label = self::punycodeDecode(substr($label, 4)); + } catch (\Exception $e) { + $info->errors |= self::ERROR_PUNYCODE; + + continue; + } + + $validationOptions['Transitional_Processing'] = false; + $labels[$i] = $label; + } + + self::validateLabel($label, $info, $validationOptions, $i > 0 && $i === $lastLabelIndex); + } + + if ($info->bidiDomain && !$info->validBidiDomain) { + $info->errors |= self::ERROR_BIDI; + } + + // Any input domain name string that does not record an error has been successfully + // processed according to this specification. Conversely, if an input domain_name string + // causes an error, then the processing of the input domain_name string fails. Determining + // what to do with error input is up to the caller, and not in the scope of this document. + return $labels; + } + + /** + * @see https://tools.ietf.org/html/rfc5893#section-2 + * + * @param string $label + */ + private static function validateBidiLabel($label, Info $info) + { + if (1 === \preg_match(static::REGEX_RTL_LABEL, $label)) { + $info->bidiDomain = true; + + // Step 1. The first character must be a character with Bidi property L, R, or AL. + // If it has the R or AL property, it is an RTL label + if (1 !== \preg_match(static::REGEX_BIDI_STEP_1_RTL, $label)) { + $info->validBidiDomain = false; + + return; + } + + // Step 2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES, + // CS, ET, ON, BN, or NSM are allowed. + if (1 === \preg_match(static::REGEX_BIDI_STEP_2, $label)) { + $info->validBidiDomain = false; + + return; + } + + // Step 3. In an RTL label, the end of the label must be a character with Bidi property + // R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM. + if (1 !== \preg_match(static::REGEX_BIDI_STEP_3, $label)) { + $info->validBidiDomain = false; + + return; + } + + // Step 4. In an RTL label, if an EN is present, no AN may be present, and vice versa. + if (1 === \preg_match(static::REGEX_BIDI_STEP_4_AN, $label) && 1 === \preg_match(static::REGEX_BIDI_STEP_4_EN, $label)) { + $info->validBidiDomain = false; + + return; + } + + return; + } + + // We are a LTR label + // Step 1. The first character must be a character with Bidi property L, R, or AL. + // If it has the L property, it is an LTR label. + if (1 !== preg_match(static::REGEX_BIDI_STEP_1_LTR, $label)) { + $info->validBidiDomain = false; + + return; + } + + // Step 5. In an LTR label, only characters with the Bidi properties L, EN, + // ES, CS, ET, ON, BN, or NSM are allowed. + if (1 === preg_match(static::REGEX_BIDI_STEP_5, $label)) { + $info->validBidiDomain = false; + + return; + } + + // Step 6.In an LTR label, the end of the label must be a character with Bidi property L or + // EN, followed by zero or more characters with Bidi property NSM. + if (1 !== preg_match(static::REGEX_BIDI_STEP_6, $label)) { + $info->validBidiDomain = false; + + return; + } + } + + /** + * @param array $labels + */ + private static function validateDomainAndLabelLength(array $labels, Info $info) + { + $maxDomainSize = self::MAX_DOMAIN_SIZE; + $length = \count($labels); + + // Number of "." delimiters. + $domainLength = $length - 1; + + // If the last label is empty and it is not the first label, then it is the root label. + // Increase the max size by 1, making it 254, to account for the root label's "." + // delimiter. This also means we don't need to check the last label's length for being too + // long. + if ($length > 1 && '' === $labels[$length - 1]) { + ++$maxDomainSize; + --$length; + } + + for ($i = 0; $i < $length; ++$i) { + $bytes = \strlen($labels[$i]); + $domainLength += $bytes; + + if ($bytes > self::MAX_LABEL_SIZE) { + $info->errors |= self::ERROR_LABEL_TOO_LONG; + } + } + + if ($domainLength > $maxDomainSize) { + $info->errors |= self::ERROR_DOMAIN_NAME_TOO_LONG; + } + } + + /** + * @see https://www.unicode.org/reports/tr46/#Validity_Criteria + * + * @param string $label + * @param array $options + * @param bool $canBeEmpty + */ + private static function validateLabel($label, Info $info, array $options, $canBeEmpty) + { + if ('' === $label) { + if (!$canBeEmpty) { + $info->errors |= self::ERROR_EMPTY_LABEL; + } + + return; + } + + // Step 1. The label must be in Unicode Normalization Form C. + if (!static::isNormalized($label)) { + $info->errors |= self::ERROR_INVALID_ACE_LABEL; + } + + $codePoints = self::utf8Decode($label); + + // Step 2. If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character + // in both the thrid and fourth positions. + if (isset($codePoints[2], $codePoints[3]) && 0x002D === $codePoints[2] && 0x002D === $codePoints[3]) { + $info->errors |= self::ERROR_HYPHEN_3_4; + } + + // Step 3. If CheckHyphens, the label must neither begin nor end with a U+002D + // HYPHEN-MINUS character. + if ('-' === substr($label, 0, 1)) { + $info->errors |= self::ERROR_LEADING_HYPHEN; + } + + if ('-' === substr($label, -1, 1)) { + $info->errors |= self::ERROR_TRAILING_HYPHEN; + } + + // Step 4. The label must not contain a U+002E (.) FULL STOP. + if (false !== strpos($label, '.')) { + $info->errors |= self::ERROR_LABEL_HAS_DOT; + } + + // Step 5. The label must not begin with a combining mark, that is: General_Category=Mark. + if (1 === preg_match(static::REGEX_COMBINING_MARK, $label)) { + $info->errors |= self::ERROR_LEADING_COMBINING_MARK; + } + + // Step 6. Each code point in the label must only have certain status values according to + // Section 5, IDNA Mapping Table: + $transitional = $options['Transitional_Processing']; + + foreach ($codePoints as $codePoint) { + $data = self::lookupCodePointStatus($codePoint); + $status = $data['status']; + + if ('valid' === $status || (!$transitional && 'deviation' === $status)) { + continue; + } + + $info->errors |= self::ERROR_DISALLOWED; + + break; + } + + // Step 7. The label must satisify the ContextJ rules from Appendix A, in + // The Unicode Code Points and Internationalized Domain Names for Applications (IDNA) + // [IDNA2008]. + if (!self::isValidContextJ($codePoints, $label)) { + $info->errors |= self::ERROR_CONTEXTJ; + } + + // Step 8. If CheckBidi, and if the domain name is a Bidi domain name, then the label must + // satisfy all six of the numbered conditions in [IDNA2008] RFC 5893, Section 2. + if ($options['CheckBidi'] && (!$info->bidiDomain || $info->validBidiDomain)) { + self::validateBidiLabel($label, $info); + } + } + + /** + * @see https://tools.ietf.org/html/rfc3492#section-6.2 + * + * @param string $input + * + * @return string + */ + private static function punycodeDecode($input) + { + $n = self::INITIAL_N; + $out = 0; + $i = 0; + $bias = self::INITIAL_BIAS; + $lastDelimIndex = strrpos($input, self::DELIMITER); + $b = false === $lastDelimIndex ? 0 : $lastDelimIndex; + $inputLength = \strlen($input); + $output = []; + $bytes = array_map('ord', str_split($input)); + + for ($j = 0; $j < $b; ++$j) { + if ($bytes[$j] > 0x7F) { + throw new \Exception('Invalid input'); + } + + $output[$out++] = $input[$j]; + } + + if ($b > 0) { + ++$b; + } + + for ($in = $b; $in < $inputLength; ++$out) { + $oldi = $i; + $w = 1; + + for ($k = self::BASE; /* no condition */; $k += self::BASE) { + if ($in >= $inputLength) { + throw new \Exception('Invalid input'); + } + + $digit = self::$basicToDigit[$bytes[$in++] & 0xFF] ?? -1; + + if ($digit < 0) { + throw new \Exception('Invalid input'); + } + + if ($digit > intdiv(self::MAX_INT - $i, $w)) { + throw new \Exception('Integer overflow'); + } + + $i += $digit * $w; + + if ($k <= $bias) { + $t = self::TMIN; + } elseif ($k >= $bias + self::TMAX) { + $t = self::TMAX; + } else { + $t = $k - $bias; + } + + if ($digit < $t) { + break; + } + + $baseMinusT = self::BASE - $t; + + if ($w > intdiv(self::MAX_INT, $baseMinusT)) { + throw new \Exception('Integer overflow'); + } + + $w *= $baseMinusT; + } + + $outPlusOne = $out + 1; + $bias = self::adaptBias($i - $oldi, $outPlusOne, 0 === $oldi); + + if (intdiv($i, $outPlusOne) > self::MAX_INT - $n) { + throw new \Exception('Integer overflow'); + } + + $n += intdiv($i, $outPlusOne); + $i %= $outPlusOne; + array_splice($output, $i++, 0, [mb_chr($n, 'utf-8')]); + } + + return implode('', $output); + } + + /** + * @see https://tools.ietf.org/html/rfc3492#section-6.3 + * + * @param string $input + * + * @return string + */ + private static function punycodeEncode($input) + { + $n = self::INITIAL_N; + $delta = 0; + $out = 0; + $bias = self::INITIAL_BIAS; + $inputLength = 0; + $output = ''; + $iter = self::utf8Decode($input); + + foreach ($iter as $codePoint) { + ++$inputLength; + + if ($codePoint < 0x80) { + $output .= \chr($codePoint); + ++$out; + } + } + + $h = $out; + $b = $out; + + if ($b > 0) { + $output .= self::DELIMITER; + ++$out; + } + + while ($h < $inputLength) { + $m = self::MAX_INT; + + foreach ($iter as $codePoint) { + if ($codePoint >= $n && $codePoint < $m) { + $m = $codePoint; + } + } + + if ($m - $n > intdiv(self::MAX_INT - $delta, $h + 1)) { + throw new \Exception('Integer overflow'); + } + + $delta += ($m - $n) * ($h + 1); + $n = $m; + + foreach ($iter as $codePoint) { + if ($codePoint < $n && 0 === ++$delta) { + throw new \Exception('Integer overflow'); + } + + if ($codePoint === $n) { + $q = $delta; + + for ($k = self::BASE; /* no condition */; $k += self::BASE) { + if ($k <= $bias) { + $t = self::TMIN; + } elseif ($k >= $bias + self::TMAX) { + $t = self::TMAX; + } else { + $t = $k - $bias; + } + + if ($q < $t) { + break; + } + + $qMinusT = $q - $t; + $baseMinusT = self::BASE - $t; + $output .= self::encodeDigit($t + ($qMinusT) % ($baseMinusT), false); + ++$out; + $q = intdiv($qMinusT, $baseMinusT); + } + + $output .= self::encodeDigit($q, false); + ++$out; + $bias = self::adaptBias($delta, $h + 1, $h === $b); + $delta = 0; + ++$h; + } + } + + ++$delta; + ++$n; + } + + return $output; + } + + /** + * @see https://tools.ietf.org/html/rfc3492#section-6.1 + * + * @param int $delta + * @param int $numPoints + * @param bool $firstTime + * + * @return int + */ + private static function adaptBias($delta, $numPoints, $firstTime) + { + // xxx >> 1 is a faster way of doing intdiv(xxx, 2) + $delta = $firstTime ? intdiv($delta, self::DAMP) : $delta >> 1; + $delta += intdiv($delta, $numPoints); + $k = 0; + + while ($delta > ((self::BASE - self::TMIN) * self::TMAX) >> 1) { + $delta = intdiv($delta, self::BASE - self::TMIN); + $k += self::BASE; + } + + return $k + intdiv((self::BASE - self::TMIN + 1) * $delta, $delta + self::SKEW); + } + + /** + * @param int $d + * @param bool $flag + * + * @return string + */ + private static function encodeDigit($d, $flag) + { + return \chr($d + 22 + 75 * ($d < 26 ? 1 : 0) - (($flag ? 1 : 0) << 5)); + } + + /** + * Takes a UTF-8 encoded string and converts it into a series of integer code points. Any + * invalid byte sequences will be replaced by a U+FFFD replacement code point. + * + * @see https://encoding.spec.whatwg.org/#utf-8-decoder + * + * @param string $input + * + * @return array + */ + private static function utf8Decode($input) + { + $bytesSeen = 0; + $bytesNeeded = 0; + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + $codePoint = 0; + $codePoints = []; + $length = \strlen($input); + + for ($i = 0; $i < $length; ++$i) { + $byte = \ord($input[$i]); + + if (0 === $bytesNeeded) { + if ($byte >= 0x00 && $byte <= 0x7F) { + $codePoints[] = $byte; + + continue; + } + + if ($byte >= 0xC2 && $byte <= 0xDF) { + $bytesNeeded = 1; + $codePoint = $byte & 0x1F; + } elseif ($byte >= 0xE0 && $byte <= 0xEF) { + if (0xE0 === $byte) { + $lowerBoundary = 0xA0; + } elseif (0xED === $byte) { + $upperBoundary = 0x9F; + } + + $bytesNeeded = 2; + $codePoint = $byte & 0xF; + } elseif ($byte >= 0xF0 && $byte <= 0xF4) { + if (0xF0 === $byte) { + $lowerBoundary = 0x90; + } elseif (0xF4 === $byte) { + $upperBoundary = 0x8F; + } + + $bytesNeeded = 3; + $codePoint = $byte & 0x7; + } else { + $codePoints[] = 0xFFFD; + } + + continue; + } + + if ($byte < $lowerBoundary || $byte > $upperBoundary) { + $codePoint = 0; + $bytesNeeded = 0; + $bytesSeen = 0; + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + --$i; + $codePoints[] = 0xFFFD; + + continue; + } + + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + $codePoint = ($codePoint << 6) | ($byte & 0x3F); + + if (++$bytesSeen !== $bytesNeeded) { + continue; + } + + $codePoints[] = $codePoint; + $codePoint = 0; + $bytesNeeded = 0; + $bytesSeen = 0; + } + + // String unexpectedly ended, so append a U+FFFD code point. + if (0 !== $bytesNeeded) { + $codePoints[] = 0xFFFD; + } + + return $codePoints; + } + + /** + * @param int $codePoint + * + * @return array{status: string, mapping?: string} + */ + private static function lookupCodePointStatus($codePoint) + { + if (!self::$mappingTableLoaded) { + self::$mappingTableLoaded = true; + self::$mapped = static::getData('mapped'); + self::$ignored = static::getData('ignored'); + self::$deviation = static::getData('deviation'); + self::$disallowed = static::getData('disallowed'); + self::$disallowed_STD3_mapped = static::getData('disallowed_STD3_mapped'); + self::$disallowed_STD3_valid = static::getData('disallowed_STD3_valid'); + } + + if (isset(self::$mapped[$codePoint])) { + return ['status' => 'mapped', 'mapping' => self::$mapped[$codePoint]]; + } + + if (isset(self::$ignored[$codePoint])) { + return ['status' => 'ignored']; + } + + if (isset(self::$deviation[$codePoint])) { + return ['status' => 'deviation', 'mapping' => self::$deviation[$codePoint]]; + } + + if (isset(self::$disallowed[$codePoint]) || static::inDisallowedRange($codePoint)) { + return ['status' => 'disallowed']; + } + + $isDisallowedMapped = isset(self::$disallowed_STD3_mapped[$codePoint]); + + if ($isDisallowedMapped || isset(self::$disallowed_STD3_valid[$codePoint])) { + $status = 'disallowed'; + + $status = $isDisallowedMapped ? 'mapped' : 'valid'; + + if ($isDisallowedMapped) { + return ['status' => $status, 'mapping' => self::$disallowed_STD3_mapped[$codePoint]]; + } + + return ['status' => $status]; + } + + return ['status' => 'valid']; + } + + private static function inDisallowedRange($codePoint) + { + return + ($codePoint >= 128 && $codePoint <= 159) + || ($codePoint >= 2155 && $codePoint <= 2207) + || ($codePoint >= 3676 && $codePoint <= 3712) + || ($codePoint >= 3808 && $codePoint <= 3839) + || ($codePoint >= 4059 && $codePoint <= 4095) + || ($codePoint >= 4256 && $codePoint <= 4293) + || ($codePoint >= 6849 && $codePoint <= 6911) + || ($codePoint >= 11859 && $codePoint <= 11903) + || ($codePoint >= 42955 && $codePoint <= 42996) + || ($codePoint >= 55296 && $codePoint <= 57343) + || ($codePoint >= 57344 && $codePoint <= 63743) + || ($codePoint >= 64218 && $codePoint <= 64255) + || ($codePoint >= 64976 && $codePoint <= 65007) + || ($codePoint >= 65630 && $codePoint <= 65663) + || ($codePoint >= 65953 && $codePoint <= 65999) + || ($codePoint >= 66046 && $codePoint <= 66175) + || ($codePoint >= 66518 && $codePoint <= 66559) + || ($codePoint >= 66928 && $codePoint <= 67071) + || ($codePoint >= 67432 && $codePoint <= 67583) + || ($codePoint >= 67760 && $codePoint <= 67807) + || ($codePoint >= 67904 && $codePoint <= 67967) + || ($codePoint >= 68256 && $codePoint <= 68287) + || ($codePoint >= 68528 && $codePoint <= 68607) + || ($codePoint >= 68681 && $codePoint <= 68735) + || ($codePoint >= 68922 && $codePoint <= 69215) + || ($codePoint >= 69298 && $codePoint <= 69375) + || ($codePoint >= 69466 && $codePoint <= 69551) + || ($codePoint >= 70207 && $codePoint <= 70271) + || ($codePoint >= 70517 && $codePoint <= 70655) + || ($codePoint >= 70874 && $codePoint <= 71039) + || ($codePoint >= 71134 && $codePoint <= 71167) + || ($codePoint >= 71370 && $codePoint <= 71423) + || ($codePoint >= 71488 && $codePoint <= 71679) + || ($codePoint >= 71740 && $codePoint <= 71839) + || ($codePoint >= 72026 && $codePoint <= 72095) + || ($codePoint >= 72441 && $codePoint <= 72703) + || ($codePoint >= 72887 && $codePoint <= 72959) + || ($codePoint >= 73130 && $codePoint <= 73439) + || ($codePoint >= 73465 && $codePoint <= 73647) + || ($codePoint >= 74650 && $codePoint <= 74751) + || ($codePoint >= 75076 && $codePoint <= 77823) + || ($codePoint >= 78905 && $codePoint <= 82943) + || ($codePoint >= 83527 && $codePoint <= 92159) + || ($codePoint >= 92784 && $codePoint <= 92879) + || ($codePoint >= 93072 && $codePoint <= 93759) + || ($codePoint >= 93851 && $codePoint <= 93951) + || ($codePoint >= 94112 && $codePoint <= 94175) + || ($codePoint >= 101590 && $codePoint <= 101631) + || ($codePoint >= 101641 && $codePoint <= 110591) + || ($codePoint >= 110879 && $codePoint <= 110927) + || ($codePoint >= 111356 && $codePoint <= 113663) + || ($codePoint >= 113828 && $codePoint <= 118783) + || ($codePoint >= 119366 && $codePoint <= 119519) + || ($codePoint >= 119673 && $codePoint <= 119807) + || ($codePoint >= 121520 && $codePoint <= 122879) + || ($codePoint >= 122923 && $codePoint <= 123135) + || ($codePoint >= 123216 && $codePoint <= 123583) + || ($codePoint >= 123648 && $codePoint <= 124927) + || ($codePoint >= 125143 && $codePoint <= 125183) + || ($codePoint >= 125280 && $codePoint <= 126064) + || ($codePoint >= 126133 && $codePoint <= 126208) + || ($codePoint >= 126270 && $codePoint <= 126463) + || ($codePoint >= 126652 && $codePoint <= 126703) + || ($codePoint >= 126706 && $codePoint <= 126975) + || ($codePoint >= 127406 && $codePoint <= 127461) + || ($codePoint >= 127590 && $codePoint <= 127743) + || ($codePoint >= 129202 && $codePoint <= 129279) + || ($codePoint >= 129751 && $codePoint <= 129791) + || ($codePoint >= 129995 && $codePoint <= 130031) + || ($codePoint >= 130042 && $codePoint <= 131069) + || ($codePoint >= 173790 && $codePoint <= 173823) + || ($codePoint >= 191457 && $codePoint <= 194559) + || ($codePoint >= 195102 && $codePoint <= 196605) + || ($codePoint >= 201547 && $codePoint <= 262141) + || ($codePoint >= 262144 && $codePoint <= 327677) + || ($codePoint >= 327680 && $codePoint <= 393213) + || ($codePoint >= 393216 && $codePoint <= 458749) + || ($codePoint >= 458752 && $codePoint <= 524285) + || ($codePoint >= 524288 && $codePoint <= 589821) + || ($codePoint >= 589824 && $codePoint <= 655357) + || ($codePoint >= 655360 && $codePoint <= 720893) + || ($codePoint >= 720896 && $codePoint <= 786429) + || ($codePoint >= 786432 && $codePoint <= 851965) + || ($codePoint >= 851968 && $codePoint <= 917501) + || ($codePoint >= 917536 && $codePoint <= 917631) + || ($codePoint >= 917632 && $codePoint <= 917759) + || ($codePoint >= 918000 && $codePoint <= 983037) + || ($codePoint >= 983040 && $codePoint <= 1048573) + || ($codePoint >= 1048576 && $codePoint <= 1114109); + } + + /** + * Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension. + * + * It has been validated with Unicode 6.3 Normalization Conformance Test. + * See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations. + * + * @author Nicolas Grekas + */ + private static + $C, + $D, + $cC; + + private const + ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4], + ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; + + public static function isNormalized(string $s) : bool + { + return self::normalize($s) === $s; + } + + public static function normalize(string $s) + { + if (!isset($s[\strspn($s, self::ASCII)])) { + return $s; + } + + if (!\preg_match('//u', $s)) { + return false; + } + + if (!\preg_match('/[^\x00-\x{2FF}]/u', $s)) { + return $s; + } + + if ('' === $s) { + return ''; + } + + if (null === self::$D) { + self::$D = self::getData('canonicalDecomposition'); + self::$cC = self::getData('combiningClass'); + } + + if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) ? \mb_internal_encoding() : null) { + \mb_internal_encoding('8bit'); + } + + $r = self::decompose($s); + + if (null === self::$C) { + self::$C = self::getData('canonicalComposition'); + } + + $r = self::recompose($r); + + if (null !== $mbEncoding) { + \mb_internal_encoding($mbEncoding); + } + + return $r; + } + + private static function recompose($s) + { + $result = $tail = ''; + + $i = $s[0] < "\x80" ? 1 : static::ulenMask[$s[0] & "\xF0"]; + $len = \strlen($s); + + $lastUchr = \substr($s, 0, $i); + $lastUcls = isset(static::$cC[$lastUchr]) ? 256 : 0; + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($tail) { + $lastUchr .= $tail; + $tail = ''; + } + + if ($j = \strspn($s, self::ASCII, $i + 1)) { + $lastUchr .= \substr($s, $i, $j); + $i += $j; + } + + $result .= $lastUchr; + $lastUchr = $s[$i]; + $lastUcls = 0; + ++$i; + continue; + } + + $ulen = static::ulenMask[$s[$i] & "\xF0"]; + $uchr = \substr($s, $i, $ulen); + + if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr + || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr + || $lastUcls) { + // Table lookup and combining chars composition + + $ucls = static::$cC[$uchr] ?? 0; + + if (isset(static::$C[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) { + $lastUchr = static::$C[$lastUchr.$uchr]; + } elseif ($lastUcls = $ucls) { + $tail .= $uchr; + } else { + if ($tail) { + $lastUchr .= $tail; + $tail = ''; + } + + $result .= $lastUchr; + $lastUchr = $uchr; + } + } else { + // Hangul chars + + $L = \ord($lastUchr[2]) - 0x80; + $V = \ord($uchr[2]) - 0xA1; + $T = 0; + + $uchr = \substr($s, $i + $ulen, 3); + + if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") { + $T = \ord($uchr[2]) - 0xA7; + 0 > $T && $T += 0x40; + $ulen += 3; + } + + $L = 0xAC00 + ($L * 21 + $V) * 28 + $T; + $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F); + } + + $i += $ulen; + } + + return $result.$lastUchr.$tail; + } + + private static function decompose($s) + { + $result = ''; + + $c = []; + $i = 0; + $len = \strlen($s); + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($c) { + \ksort($c); + $result .= \implode('', $c); + $c = []; + } + + $j = 1 + \strspn($s, self::ASCII, $i + 1); + $result .= \substr($s, $i, $j); + $i += $j; + continue; + } + + $ulen = static::ulenMask[$s[$i] & "\xF0"]; + $uchr = \substr($s, $i, $ulen); + $i += $ulen; + + if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) { + // Table lookup + + if ($uchr !== $j = (static::$D[$uchr] ?? $uchr)) { + $uchr = $j; + + $j = \strlen($uchr); + $ulen = $uchr[0] < "\x80" ? 1 : static::ulenMask[$uchr[0] & "\xF0"]; + + if ($ulen != $j) { + // Put trailing chars in $s + + $j -= $ulen; + $i -= $j; + + if (0 > $i) { + $s = \str_repeat(' ', -$i).$s; + $len -= $i; + $i = 0; + } + + while ($j--) { + $s[$i + $j] = $uchr[$ulen + $j]; + } + + $uchr = \substr($uchr, 0, $ulen); + } + } + if (isset(static::$cC[$uchr])) { + // Combining chars, for sorting + + if (!isset($c[static::$cC[$uchr]])) { + $c[static::$cC[$uchr]] = ''; + } + $c[static::$cC[$uchr]] .= $uchr; + continue; + } + } else { + // Hangul chars + + $uchr = \unpack('C*', $uchr); + $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80; + + $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588)) + ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); + + if ($j %= 28) { + $uchr .= $j < 25 + ? ("\xE1\x86".\chr(0xA7 + $j)) + : ("\xE1\x87".\chr(0x67 + $j)); + } + } + if ($c) { + \ksort($c); + $result .= \implode('', $c); + $c = []; + } + + $result .= $uchr; + } + + if ($c) { + \ksort($c); + $result .= \implode('', $c); + } + + return $result; + } + + private static function getData($file) + { + if (\file_exists($file = __DIR__ . '/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalComposition.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalComposition.php new file mode 100644 index 000000000..db4764419 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalComposition.php @@ -0,0 +1,945 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '΅' => '΅', + 'Ά' => 'Ά', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ὲ' => 'ὲ', + 'ὴ' => 'ὴ', + 'ὶ' => 'ὶ', + 'ὸ' => 'ὸ', + 'ὺ' => 'ὺ', + 'ὼ' => 'ὼ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'ᾼ' => 'ᾼ', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Ὴ' => 'Ὴ', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ὼ' => 'Ὼ', + 'ῼ' => 'ῼ', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalDecomposition.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalDecomposition.php new file mode 100644 index 000000000..5a3e8e096 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/canonicalDecomposition.php @@ -0,0 +1,2065 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '̀' => '̀', + '́' => '́', + '̓' => '̓', + '̈́' => '̈́', + 'ʹ' => 'ʹ', + ';' => ';', + '΅' => '΅', + 'Ά' => 'Ά', + '·' => '·', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'क़' => 'क़', + 'ख़' => 'ख़', + 'ग़' => 'ग़', + 'ज़' => 'ज़', + 'ड़' => 'ड़', + 'ढ़' => 'ढ़', + 'फ़' => 'फ़', + 'य़' => 'य़', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ড়' => 'ড়', + 'ঢ়' => 'ঢ়', + 'য়' => 'য়', + 'ਲ਼' => 'ਲ਼', + 'ਸ਼' => 'ਸ਼', + 'ਖ਼' => 'ਖ਼', + 'ਗ਼' => 'ਗ਼', + 'ਜ਼' => 'ਜ਼', + 'ਫ਼' => 'ਫ਼', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ଡ଼' => 'ଡ଼', + 'ଢ଼' => 'ଢ଼', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'གྷ' => 'གྷ', + 'ཌྷ' => 'ཌྷ', + 'དྷ' => 'དྷ', + 'བྷ' => 'བྷ', + 'ཛྷ' => 'ཛྷ', + 'ཀྵ' => 'ཀྵ', + 'ཱི' => 'ཱི', + 'ཱུ' => 'ཱུ', + 'ྲྀ' => 'ྲྀ', + 'ླྀ' => 'ླྀ', + 'ཱྀ' => 'ཱྀ', + 'ྒྷ' => 'ྒྷ', + 'ྜྷ' => 'ྜྷ', + 'ྡྷ' => 'ྡྷ', + 'ྦྷ' => 'ྦྷ', + 'ྫྷ' => 'ྫྷ', + 'ྐྵ' => 'ྐྵ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ά' => 'ά', + 'ὲ' => 'ὲ', + 'έ' => 'έ', + 'ὴ' => 'ὴ', + 'ή' => 'ή', + 'ὶ' => 'ὶ', + 'ί' => 'ί', + 'ὸ' => 'ὸ', + 'ό' => 'ό', + 'ὺ' => 'ὺ', + 'ύ' => 'ύ', + 'ὼ' => 'ὼ', + 'ώ' => 'ώ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'Ά' => 'Ά', + 'ᾼ' => 'ᾼ', + 'ι' => 'ι', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Έ' => 'Έ', + 'Ὴ' => 'Ὴ', + 'Ή' => 'Ή', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ΐ' => 'ΐ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + 'Ί' => 'Ί', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ΰ' => 'ΰ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ύ' => 'Ύ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + '΅' => '΅', + '`' => '`', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ό' => 'Ό', + 'Ὼ' => 'Ὼ', + 'Ώ' => 'Ώ', + 'ῼ' => 'ῼ', + '´' => '´', + ' ' => ' ', + ' ' => ' ', + 'Ω' => 'Ω', + 'K' => 'K', + 'Å' => 'Å', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + '〈' => '〈', + '〉' => '〉', + '⫝̸' => '⫝̸', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '豈' => '豈', + '更' => '更', + '車' => '車', + '賈' => '賈', + '滑' => '滑', + '串' => '串', + '句' => '句', + '龜' => '龜', + '龜' => '龜', + '契' => '契', + '金' => '金', + '喇' => '喇', + '奈' => '奈', + '懶' => '懶', + '癩' => '癩', + '羅' => '羅', + '蘿' => '蘿', + '螺' => '螺', + '裸' => '裸', + '邏' => '邏', + '樂' => '樂', + '洛' => '洛', + '烙' => '烙', + '珞' => '珞', + '落' => '落', + '酪' => '酪', + '駱' => '駱', + '亂' => '亂', + '卵' => '卵', + '欄' => '欄', + '爛' => '爛', + '蘭' => '蘭', + '鸞' => '鸞', + '嵐' => '嵐', + '濫' => '濫', + '藍' => '藍', + '襤' => '襤', + '拉' => '拉', + '臘' => '臘', + '蠟' => '蠟', + '廊' => '廊', + '朗' => '朗', + '浪' => '浪', + '狼' => '狼', + '郎' => '郎', + '來' => '來', + '冷' => '冷', + '勞' => '勞', + '擄' => '擄', + '櫓' => '櫓', + '爐' => '爐', + '盧' => '盧', + '老' => '老', + '蘆' => '蘆', + '虜' => '虜', + '路' => '路', + '露' => '露', + '魯' => '魯', + '鷺' => '鷺', + '碌' => '碌', + '祿' => '祿', + '綠' => '綠', + '菉' => '菉', + '錄' => '錄', + '鹿' => '鹿', + '論' => '論', + '壟' => '壟', + '弄' => '弄', + '籠' => '籠', + '聾' => '聾', + '牢' => '牢', + '磊' => '磊', + '賂' => '賂', + '雷' => '雷', + '壘' => '壘', + '屢' => '屢', + '樓' => '樓', + '淚' => '淚', + '漏' => '漏', + '累' => '累', + '縷' => '縷', + '陋' => '陋', + '勒' => '勒', + '肋' => '肋', + '凜' => '凜', + '凌' => '凌', + '稜' => '稜', + '綾' => '綾', + '菱' => '菱', + '陵' => '陵', + '讀' => '讀', + '拏' => '拏', + '樂' => '樂', + '諾' => '諾', + '丹' => '丹', + '寧' => '寧', + '怒' => '怒', + '率' => '率', + '異' => '異', + '北' => '北', + '磻' => '磻', + '便' => '便', + '復' => '復', + '不' => '不', + '泌' => '泌', + '數' => '數', + '索' => '索', + '參' => '參', + '塞' => '塞', + '省' => '省', + '葉' => '葉', + '說' => '說', + '殺' => '殺', + '辰' => '辰', + '沈' => '沈', + '拾' => '拾', + '若' => '若', + '掠' => '掠', + '略' => '略', + '亮' => '亮', + '兩' => '兩', + '凉' => '凉', + '梁' => '梁', + '糧' => '糧', + '良' => '良', + '諒' => '諒', + '量' => '量', + '勵' => '勵', + '呂' => '呂', + '女' => '女', + '廬' => '廬', + '旅' => '旅', + '濾' => '濾', + '礪' => '礪', + '閭' => '閭', + '驪' => '驪', + '麗' => '麗', + '黎' => '黎', + '力' => '力', + '曆' => '曆', + '歷' => '歷', + '轢' => '轢', + '年' => '年', + '憐' => '憐', + '戀' => '戀', + '撚' => '撚', + '漣' => '漣', + '煉' => '煉', + '璉' => '璉', + '秊' => '秊', + '練' => '練', + '聯' => '聯', + '輦' => '輦', + '蓮' => '蓮', + '連' => '連', + '鍊' => '鍊', + '列' => '列', + '劣' => '劣', + '咽' => '咽', + '烈' => '烈', + '裂' => '裂', + '說' => '說', + '廉' => '廉', + '念' => '念', + '捻' => '捻', + '殮' => '殮', + '簾' => '簾', + '獵' => '獵', + '令' => '令', + '囹' => '囹', + '寧' => '寧', + '嶺' => '嶺', + '怜' => '怜', + '玲' => '玲', + '瑩' => '瑩', + '羚' => '羚', + '聆' => '聆', + '鈴' => '鈴', + '零' => '零', + '靈' => '靈', + '領' => '領', + '例' => '例', + '禮' => '禮', + '醴' => '醴', + '隸' => '隸', + '惡' => '惡', + '了' => '了', + '僚' => '僚', + '寮' => '寮', + '尿' => '尿', + '料' => '料', + '樂' => '樂', + '燎' => '燎', + '療' => '療', + '蓼' => '蓼', + '遼' => '遼', + '龍' => '龍', + '暈' => '暈', + '阮' => '阮', + '劉' => '劉', + '杻' => '杻', + '柳' => '柳', + '流' => '流', + '溜' => '溜', + '琉' => '琉', + '留' => '留', + '硫' => '硫', + '紐' => '紐', + '類' => '類', + '六' => '六', + '戮' => '戮', + '陸' => '陸', + '倫' => '倫', + '崙' => '崙', + '淪' => '淪', + '輪' => '輪', + '律' => '律', + '慄' => '慄', + '栗' => '栗', + '率' => '率', + '隆' => '隆', + '利' => '利', + '吏' => '吏', + '履' => '履', + '易' => '易', + '李' => '李', + '梨' => '梨', + '泥' => '泥', + '理' => '理', + '痢' => '痢', + '罹' => '罹', + '裏' => '裏', + '裡' => '裡', + '里' => '里', + '離' => '離', + '匿' => '匿', + '溺' => '溺', + '吝' => '吝', + '燐' => '燐', + '璘' => '璘', + '藺' => '藺', + '隣' => '隣', + '鱗' => '鱗', + '麟' => '麟', + '林' => '林', + '淋' => '淋', + '臨' => '臨', + '立' => '立', + '笠' => '笠', + '粒' => '粒', + '狀' => '狀', + '炙' => '炙', + '識' => '識', + '什' => '什', + '茶' => '茶', + '刺' => '刺', + '切' => '切', + '度' => '度', + '拓' => '拓', + '糖' => '糖', + '宅' => '宅', + '洞' => '洞', + '暴' => '暴', + '輻' => '輻', + '行' => '行', + '降' => '降', + '見' => '見', + '廓' => '廓', + '兀' => '兀', + '嗀' => '嗀', + '塚' => '塚', + '晴' => '晴', + '凞' => '凞', + '猪' => '猪', + '益' => '益', + '礼' => '礼', + '神' => '神', + '祥' => '祥', + '福' => '福', + '靖' => '靖', + '精' => '精', + '羽' => '羽', + '蘒' => '蘒', + '諸' => '諸', + '逸' => '逸', + '都' => '都', + '飯' => '飯', + '飼' => '飼', + '館' => '館', + '鶴' => '鶴', + '郞' => '郞', + '隷' => '隷', + '侮' => '侮', + '僧' => '僧', + '免' => '免', + '勉' => '勉', + '勤' => '勤', + '卑' => '卑', + '喝' => '喝', + '嘆' => '嘆', + '器' => '器', + '塀' => '塀', + '墨' => '墨', + '層' => '層', + '屮' => '屮', + '悔' => '悔', + '慨' => '慨', + '憎' => '憎', + '懲' => '懲', + '敏' => '敏', + '既' => '既', + '暑' => '暑', + '梅' => '梅', + '海' => '海', + '渚' => '渚', + '漢' => '漢', + '煮' => '煮', + '爫' => '爫', + '琢' => '琢', + '碑' => '碑', + '社' => '社', + '祉' => '祉', + '祈' => '祈', + '祐' => '祐', + '祖' => '祖', + '祝' => '祝', + '禍' => '禍', + '禎' => '禎', + '穀' => '穀', + '突' => '突', + '節' => '節', + '練' => '練', + '縉' => '縉', + '繁' => '繁', + '署' => '署', + '者' => '者', + '臭' => '臭', + '艹' => '艹', + '艹' => '艹', + '著' => '著', + '褐' => '褐', + '視' => '視', + '謁' => '謁', + '謹' => '謹', + '賓' => '賓', + '贈' => '贈', + '辶' => '辶', + '逸' => '逸', + '難' => '難', + '響' => '響', + '頻' => '頻', + '恵' => '恵', + '𤋮' => '𤋮', + '舘' => '舘', + '並' => '並', + '况' => '况', + '全' => '全', + '侀' => '侀', + '充' => '充', + '冀' => '冀', + '勇' => '勇', + '勺' => '勺', + '喝' => '喝', + '啕' => '啕', + '喙' => '喙', + '嗢' => '嗢', + '塚' => '塚', + '墳' => '墳', + '奄' => '奄', + '奔' => '奔', + '婢' => '婢', + '嬨' => '嬨', + '廒' => '廒', + '廙' => '廙', + '彩' => '彩', + '徭' => '徭', + '惘' => '惘', + '慎' => '慎', + '愈' => '愈', + '憎' => '憎', + '慠' => '慠', + '懲' => '懲', + '戴' => '戴', + '揄' => '揄', + '搜' => '搜', + '摒' => '摒', + '敖' => '敖', + '晴' => '晴', + '朗' => '朗', + '望' => '望', + '杖' => '杖', + '歹' => '歹', + '殺' => '殺', + '流' => '流', + '滛' => '滛', + '滋' => '滋', + '漢' => '漢', + '瀞' => '瀞', + '煮' => '煮', + '瞧' => '瞧', + '爵' => '爵', + '犯' => '犯', + '猪' => '猪', + '瑱' => '瑱', + '甆' => '甆', + '画' => '画', + '瘝' => '瘝', + '瘟' => '瘟', + '益' => '益', + '盛' => '盛', + '直' => '直', + '睊' => '睊', + '着' => '着', + '磌' => '磌', + '窱' => '窱', + '節' => '節', + '类' => '类', + '絛' => '絛', + '練' => '練', + '缾' => '缾', + '者' => '者', + '荒' => '荒', + '華' => '華', + '蝹' => '蝹', + '襁' => '襁', + '覆' => '覆', + '視' => '視', + '調' => '調', + '諸' => '諸', + '請' => '請', + '謁' => '謁', + '諾' => '諾', + '諭' => '諭', + '謹' => '謹', + '變' => '變', + '贈' => '贈', + '輸' => '輸', + '遲' => '遲', + '醙' => '醙', + '鉶' => '鉶', + '陼' => '陼', + '難' => '難', + '靖' => '靖', + '韛' => '韛', + '響' => '響', + '頋' => '頋', + '頻' => '頻', + '鬒' => '鬒', + '龜' => '龜', + '𢡊' => '𢡊', + '𢡄' => '𢡄', + '𣏕' => '𣏕', + '㮝' => '㮝', + '䀘' => '䀘', + '䀹' => '䀹', + '𥉉' => '𥉉', + '𥳐' => '𥳐', + '𧻓' => '𧻓', + '齃' => '齃', + '龎' => '龎', + 'יִ' => 'יִ', + 'ײַ' => 'ײַ', + 'שׁ' => 'שׁ', + 'שׂ' => 'שׂ', + 'שּׁ' => 'שּׁ', + 'שּׂ' => 'שּׂ', + 'אַ' => 'אַ', + 'אָ' => 'אָ', + 'אּ' => 'אּ', + 'בּ' => 'בּ', + 'גּ' => 'גּ', + 'דּ' => 'דּ', + 'הּ' => 'הּ', + 'וּ' => 'וּ', + 'זּ' => 'זּ', + 'טּ' => 'טּ', + 'יּ' => 'יּ', + 'ךּ' => 'ךּ', + 'כּ' => 'כּ', + 'לּ' => 'לּ', + 'מּ' => 'מּ', + 'נּ' => 'נּ', + 'סּ' => 'סּ', + 'ףּ' => 'ףּ', + 'פּ' => 'פּ', + 'צּ' => 'צּ', + 'קּ' => 'קּ', + 'רּ' => 'רּ', + 'שּ' => 'שּ', + 'תּ' => 'תּ', + 'וֹ' => 'וֹ', + 'בֿ' => 'בֿ', + 'כֿ' => 'כֿ', + 'פֿ' => 'פֿ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', + '𝅗𝅥' => '𝅗𝅥', + '𝅘𝅥' => '𝅘𝅥', + '𝅘𝅥𝅮' => '𝅘𝅥𝅮', + '𝅘𝅥𝅯' => '𝅘𝅥𝅯', + '𝅘𝅥𝅰' => '𝅘𝅥𝅰', + '𝅘𝅥𝅱' => '𝅘𝅥𝅱', + '𝅘𝅥𝅲' => '𝅘𝅥𝅲', + '𝆹𝅥' => '𝆹𝅥', + '𝆺𝅥' => '𝆺𝅥', + '𝆹𝅥𝅮' => '𝆹𝅥𝅮', + '𝆺𝅥𝅮' => '𝆺𝅥𝅮', + '𝆹𝅥𝅯' => '𝆹𝅥𝅯', + '𝆺𝅥𝅯' => '𝆺𝅥𝅯', + '丽' => '丽', + '丸' => '丸', + '乁' => '乁', + '𠄢' => '𠄢', + '你' => '你', + '侮' => '侮', + '侻' => '侻', + '倂' => '倂', + '偺' => '偺', + '備' => '備', + '僧' => '僧', + '像' => '像', + '㒞' => '㒞', + '𠘺' => '𠘺', + '免' => '免', + '兔' => '兔', + '兤' => '兤', + '具' => '具', + '𠔜' => '𠔜', + '㒹' => '㒹', + '內' => '內', + '再' => '再', + '𠕋' => '𠕋', + '冗' => '冗', + '冤' => '冤', + '仌' => '仌', + '冬' => '冬', + '况' => '况', + '𩇟' => '𩇟', + '凵' => '凵', + '刃' => '刃', + '㓟' => '㓟', + '刻' => '刻', + '剆' => '剆', + '割' => '割', + '剷' => '剷', + '㔕' => '㔕', + '勇' => '勇', + '勉' => '勉', + '勤' => '勤', + '勺' => '勺', + '包' => '包', + '匆' => '匆', + '北' => '北', + '卉' => '卉', + '卑' => '卑', + '博' => '博', + '即' => '即', + '卽' => '卽', + '卿' => '卿', + '卿' => '卿', + '卿' => '卿', + '𠨬' => '𠨬', + '灰' => '灰', + '及' => '及', + '叟' => '叟', + '𠭣' => '𠭣', + '叫' => '叫', + '叱' => '叱', + '吆' => '吆', + '咞' => '咞', + '吸' => '吸', + '呈' => '呈', + '周' => '周', + '咢' => '咢', + '哶' => '哶', + '唐' => '唐', + '啓' => '啓', + '啣' => '啣', + '善' => '善', + '善' => '善', + '喙' => '喙', + '喫' => '喫', + '喳' => '喳', + '嗂' => '嗂', + '圖' => '圖', + '嘆' => '嘆', + '圗' => '圗', + '噑' => '噑', + '噴' => '噴', + '切' => '切', + '壮' => '壮', + '城' => '城', + '埴' => '埴', + '堍' => '堍', + '型' => '型', + '堲' => '堲', + '報' => '報', + '墬' => '墬', + '𡓤' => '𡓤', + '売' => '売', + '壷' => '壷', + '夆' => '夆', + '多' => '多', + '夢' => '夢', + '奢' => '奢', + '𡚨' => '𡚨', + '𡛪' => '𡛪', + '姬' => '姬', + '娛' => '娛', + '娧' => '娧', + '姘' => '姘', + '婦' => '婦', + '㛮' => '㛮', + '㛼' => '㛼', + '嬈' => '嬈', + '嬾' => '嬾', + '嬾' => '嬾', + '𡧈' => '𡧈', + '寃' => '寃', + '寘' => '寘', + '寧' => '寧', + '寳' => '寳', + '𡬘' => '𡬘', + '寿' => '寿', + '将' => '将', + '当' => '当', + '尢' => '尢', + '㞁' => '㞁', + '屠' => '屠', + '屮' => '屮', + '峀' => '峀', + '岍' => '岍', + '𡷤' => '𡷤', + '嵃' => '嵃', + '𡷦' => '𡷦', + '嵮' => '嵮', + '嵫' => '嵫', + '嵼' => '嵼', + '巡' => '巡', + '巢' => '巢', + '㠯' => '㠯', + '巽' => '巽', + '帨' => '帨', + '帽' => '帽', + '幩' => '幩', + '㡢' => '㡢', + '𢆃' => '𢆃', + '㡼' => '㡼', + '庰' => '庰', + '庳' => '庳', + '庶' => '庶', + '廊' => '廊', + '𪎒' => '𪎒', + '廾' => '廾', + '𢌱' => '𢌱', + '𢌱' => '𢌱', + '舁' => '舁', + '弢' => '弢', + '弢' => '弢', + '㣇' => '㣇', + '𣊸' => '𣊸', + '𦇚' => '𦇚', + '形' => '形', + '彫' => '彫', + '㣣' => '㣣', + '徚' => '徚', + '忍' => '忍', + '志' => '志', + '忹' => '忹', + '悁' => '悁', + '㤺' => '㤺', + '㤜' => '㤜', + '悔' => '悔', + '𢛔' => '𢛔', + '惇' => '惇', + '慈' => '慈', + '慌' => '慌', + '慎' => '慎', + '慌' => '慌', + '慺' => '慺', + '憎' => '憎', + '憲' => '憲', + '憤' => '憤', + '憯' => '憯', + '懞' => '懞', + '懲' => '懲', + '懶' => '懶', + '成' => '成', + '戛' => '戛', + '扝' => '扝', + '抱' => '抱', + '拔' => '拔', + '捐' => '捐', + '𢬌' => '𢬌', + '挽' => '挽', + '拼' => '拼', + '捨' => '捨', + '掃' => '掃', + '揤' => '揤', + '𢯱' => '𢯱', + '搢' => '搢', + '揅' => '揅', + '掩' => '掩', + '㨮' => '㨮', + '摩' => '摩', + '摾' => '摾', + '撝' => '撝', + '摷' => '摷', + '㩬' => '㩬', + '敏' => '敏', + '敬' => '敬', + '𣀊' => '𣀊', + '旣' => '旣', + '書' => '書', + '晉' => '晉', + '㬙' => '㬙', + '暑' => '暑', + '㬈' => '㬈', + '㫤' => '㫤', + '冒' => '冒', + '冕' => '冕', + '最' => '最', + '暜' => '暜', + '肭' => '肭', + '䏙' => '䏙', + '朗' => '朗', + '望' => '望', + '朡' => '朡', + '杞' => '杞', + '杓' => '杓', + '𣏃' => '𣏃', + '㭉' => '㭉', + '柺' => '柺', + '枅' => '枅', + '桒' => '桒', + '梅' => '梅', + '𣑭' => '𣑭', + '梎' => '梎', + '栟' => '栟', + '椔' => '椔', + '㮝' => '㮝', + '楂' => '楂', + '榣' => '榣', + '槪' => '槪', + '檨' => '檨', + '𣚣' => '𣚣', + '櫛' => '櫛', + '㰘' => '㰘', + '次' => '次', + '𣢧' => '𣢧', + '歔' => '歔', + '㱎' => '㱎', + '歲' => '歲', + '殟' => '殟', + '殺' => '殺', + '殻' => '殻', + '𣪍' => '𣪍', + '𡴋' => '𡴋', + '𣫺' => '𣫺', + '汎' => '汎', + '𣲼' => '𣲼', + '沿' => '沿', + '泍' => '泍', + '汧' => '汧', + '洖' => '洖', + '派' => '派', + '海' => '海', + '流' => '流', + '浩' => '浩', + '浸' => '浸', + '涅' => '涅', + '𣴞' => '𣴞', + '洴' => '洴', + '港' => '港', + '湮' => '湮', + '㴳' => '㴳', + '滋' => '滋', + '滇' => '滇', + '𣻑' => '𣻑', + '淹' => '淹', + '潮' => '潮', + '𣽞' => '𣽞', + '𣾎' => '𣾎', + '濆' => '濆', + '瀹' => '瀹', + '瀞' => '瀞', + '瀛' => '瀛', + '㶖' => '㶖', + '灊' => '灊', + '災' => '災', + '灷' => '灷', + '炭' => '炭', + '𠔥' => '𠔥', + '煅' => '煅', + '𤉣' => '𤉣', + '熜' => '熜', + '𤎫' => '𤎫', + '爨' => '爨', + '爵' => '爵', + '牐' => '牐', + '𤘈' => '𤘈', + '犀' => '犀', + '犕' => '犕', + '𤜵' => '𤜵', + '𤠔' => '𤠔', + '獺' => '獺', + '王' => '王', + '㺬' => '㺬', + '玥' => '玥', + '㺸' => '㺸', + '㺸' => '㺸', + '瑇' => '瑇', + '瑜' => '瑜', + '瑱' => '瑱', + '璅' => '璅', + '瓊' => '瓊', + '㼛' => '㼛', + '甤' => '甤', + '𤰶' => '𤰶', + '甾' => '甾', + '𤲒' => '𤲒', + '異' => '異', + '𢆟' => '𢆟', + '瘐' => '瘐', + '𤾡' => '𤾡', + '𤾸' => '𤾸', + '𥁄' => '𥁄', + '㿼' => '㿼', + '䀈' => '䀈', + '直' => '直', + '𥃳' => '𥃳', + '𥃲' => '𥃲', + '𥄙' => '𥄙', + '𥄳' => '𥄳', + '眞' => '眞', + '真' => '真', + '真' => '真', + '睊' => '睊', + '䀹' => '䀹', + '瞋' => '瞋', + '䁆' => '䁆', + '䂖' => '䂖', + '𥐝' => '𥐝', + '硎' => '硎', + '碌' => '碌', + '磌' => '磌', + '䃣' => '䃣', + '𥘦' => '𥘦', + '祖' => '祖', + '𥚚' => '𥚚', + '𥛅' => '𥛅', + '福' => '福', + '秫' => '秫', + '䄯' => '䄯', + '穀' => '穀', + '穊' => '穊', + '穏' => '穏', + '𥥼' => '𥥼', + '𥪧' => '𥪧', + '𥪧' => '𥪧', + '竮' => '竮', + '䈂' => '䈂', + '𥮫' => '𥮫', + '篆' => '篆', + '築' => '築', + '䈧' => '䈧', + '𥲀' => '𥲀', + '糒' => '糒', + '䊠' => '䊠', + '糨' => '糨', + '糣' => '糣', + '紀' => '紀', + '𥾆' => '𥾆', + '絣' => '絣', + '䌁' => '䌁', + '緇' => '緇', + '縂' => '縂', + '繅' => '繅', + '䌴' => '䌴', + '𦈨' => '𦈨', + '𦉇' => '𦉇', + '䍙' => '䍙', + '𦋙' => '𦋙', + '罺' => '罺', + '𦌾' => '𦌾', + '羕' => '羕', + '翺' => '翺', + '者' => '者', + '𦓚' => '𦓚', + '𦔣' => '𦔣', + '聠' => '聠', + '𦖨' => '𦖨', + '聰' => '聰', + '𣍟' => '𣍟', + '䏕' => '䏕', + '育' => '育', + '脃' => '脃', + '䐋' => '䐋', + '脾' => '脾', + '媵' => '媵', + '𦞧' => '𦞧', + '𦞵' => '𦞵', + '𣎓' => '𣎓', + '𣎜' => '𣎜', + '舁' => '舁', + '舄' => '舄', + '辞' => '辞', + '䑫' => '䑫', + '芑' => '芑', + '芋' => '芋', + '芝' => '芝', + '劳' => '劳', + '花' => '花', + '芳' => '芳', + '芽' => '芽', + '苦' => '苦', + '𦬼' => '𦬼', + '若' => '若', + '茝' => '茝', + '荣' => '荣', + '莭' => '莭', + '茣' => '茣', + '莽' => '莽', + '菧' => '菧', + '著' => '著', + '荓' => '荓', + '菊' => '菊', + '菌' => '菌', + '菜' => '菜', + '𦰶' => '𦰶', + '𦵫' => '𦵫', + '𦳕' => '𦳕', + '䔫' => '䔫', + '蓱' => '蓱', + '蓳' => '蓳', + '蔖' => '蔖', + '𧏊' => '𧏊', + '蕤' => '蕤', + '𦼬' => '𦼬', + '䕝' => '䕝', + '䕡' => '䕡', + '𦾱' => '𦾱', + '𧃒' => '𧃒', + '䕫' => '䕫', + '虐' => '虐', + '虜' => '虜', + '虧' => '虧', + '虩' => '虩', + '蚩' => '蚩', + '蚈' => '蚈', + '蜎' => '蜎', + '蛢' => '蛢', + '蝹' => '蝹', + '蜨' => '蜨', + '蝫' => '蝫', + '螆' => '螆', + '䗗' => '䗗', + '蟡' => '蟡', + '蠁' => '蠁', + '䗹' => '䗹', + '衠' => '衠', + '衣' => '衣', + '𧙧' => '𧙧', + '裗' => '裗', + '裞' => '裞', + '䘵' => '䘵', + '裺' => '裺', + '㒻' => '㒻', + '𧢮' => '𧢮', + '𧥦' => '𧥦', + '䚾' => '䚾', + '䛇' => '䛇', + '誠' => '誠', + '諭' => '諭', + '變' => '變', + '豕' => '豕', + '𧲨' => '𧲨', + '貫' => '貫', + '賁' => '賁', + '贛' => '贛', + '起' => '起', + '𧼯' => '𧼯', + '𠠄' => '𠠄', + '跋' => '跋', + '趼' => '趼', + '跰' => '跰', + '𠣞' => '𠣞', + '軔' => '軔', + '輸' => '輸', + '𨗒' => '𨗒', + '𨗭' => '𨗭', + '邔' => '邔', + '郱' => '郱', + '鄑' => '鄑', + '𨜮' => '𨜮', + '鄛' => '鄛', + '鈸' => '鈸', + '鋗' => '鋗', + '鋘' => '鋘', + '鉼' => '鉼', + '鏹' => '鏹', + '鐕' => '鐕', + '𨯺' => '𨯺', + '開' => '開', + '䦕' => '䦕', + '閷' => '閷', + '𨵷' => '𨵷', + '䧦' => '䧦', + '雃' => '雃', + '嶲' => '嶲', + '霣' => '霣', + '𩅅' => '𩅅', + '𩈚' => '𩈚', + '䩮' => '䩮', + '䩶' => '䩶', + '韠' => '韠', + '𩐊' => '𩐊', + '䪲' => '䪲', + '𩒖' => '𩒖', + '頋' => '頋', + '頋' => '頋', + '頩' => '頩', + '𩖶' => '𩖶', + '飢' => '飢', + '䬳' => '䬳', + '餩' => '餩', + '馧' => '馧', + '駂' => '駂', + '駾' => '駾', + '䯎' => '䯎', + '𩬰' => '𩬰', + '鬒' => '鬒', + '鱀' => '鱀', + '鳽' => '鳽', + '䳎' => '䳎', + '䳭' => '䳭', + '鵧' => '鵧', + '𪃎' => '𪃎', + '䳸' => '䳸', + '𪄅' => '𪄅', + '𪈎' => '𪈎', + '𪊑' => '𪊑', + '麻' => '麻', + '䵖' => '䵖', + '黹' => '黹', + '黾' => '黾', + '鼅' => '鼅', + '鼏' => '鼏', + '鼖' => '鼖', + '鼻' => '鼻', + '𪘀' => '𪘀', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/combiningClass.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/combiningClass.php new file mode 100644 index 000000000..ec90f36eb --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/combiningClass.php @@ -0,0 +1,876 @@ + 230, + '́' => 230, + '̂' => 230, + '̃' => 230, + '̄' => 230, + '̅' => 230, + '̆' => 230, + '̇' => 230, + '̈' => 230, + '̉' => 230, + '̊' => 230, + '̋' => 230, + '̌' => 230, + '̍' => 230, + '̎' => 230, + '̏' => 230, + '̐' => 230, + '̑' => 230, + '̒' => 230, + '̓' => 230, + '̔' => 230, + '̕' => 232, + '̖' => 220, + '̗' => 220, + '̘' => 220, + '̙' => 220, + '̚' => 232, + '̛' => 216, + '̜' => 220, + '̝' => 220, + '̞' => 220, + '̟' => 220, + '̠' => 220, + '̡' => 202, + '̢' => 202, + '̣' => 220, + '̤' => 220, + '̥' => 220, + '̦' => 220, + '̧' => 202, + '̨' => 202, + '̩' => 220, + '̪' => 220, + '̫' => 220, + '̬' => 220, + '̭' => 220, + '̮' => 220, + '̯' => 220, + '̰' => 220, + '̱' => 220, + '̲' => 220, + '̳' => 220, + '̴' => 1, + '̵' => 1, + '̶' => 1, + '̷' => 1, + '̸' => 1, + '̹' => 220, + '̺' => 220, + '̻' => 220, + '̼' => 220, + '̽' => 230, + '̾' => 230, + '̿' => 230, + '̀' => 230, + '́' => 230, + '͂' => 230, + '̓' => 230, + '̈́' => 230, + 'ͅ' => 240, + '͆' => 230, + '͇' => 220, + '͈' => 220, + '͉' => 220, + '͊' => 230, + '͋' => 230, + '͌' => 230, + '͍' => 220, + '͎' => 220, + '͐' => 230, + '͑' => 230, + '͒' => 230, + '͓' => 220, + '͔' => 220, + '͕' => 220, + '͖' => 220, + '͗' => 230, + '͘' => 232, + '͙' => 220, + '͚' => 220, + '͛' => 230, + '͜' => 233, + '͝' => 234, + '͞' => 234, + '͟' => 233, + '͠' => 234, + '͡' => 234, + '͢' => 233, + 'ͣ' => 230, + 'ͤ' => 230, + 'ͥ' => 230, + 'ͦ' => 230, + 'ͧ' => 230, + 'ͨ' => 230, + 'ͩ' => 230, + 'ͪ' => 230, + 'ͫ' => 230, + 'ͬ' => 230, + 'ͭ' => 230, + 'ͮ' => 230, + 'ͯ' => 230, + '҃' => 230, + '҄' => 230, + '҅' => 230, + '҆' => 230, + '҇' => 230, + '֑' => 220, + '֒' => 230, + '֓' => 230, + '֔' => 230, + '֕' => 230, + '֖' => 220, + '֗' => 230, + '֘' => 230, + '֙' => 230, + '֚' => 222, + '֛' => 220, + '֜' => 230, + '֝' => 230, + '֞' => 230, + '֟' => 230, + '֠' => 230, + '֡' => 230, + '֢' => 220, + '֣' => 220, + '֤' => 220, + '֥' => 220, + '֦' => 220, + '֧' => 220, + '֨' => 230, + '֩' => 230, + '֪' => 220, + '֫' => 230, + '֬' => 230, + '֭' => 222, + '֮' => 228, + '֯' => 230, + 'ְ' => 10, + 'ֱ' => 11, + 'ֲ' => 12, + 'ֳ' => 13, + 'ִ' => 14, + 'ֵ' => 15, + 'ֶ' => 16, + 'ַ' => 17, + 'ָ' => 18, + 'ֹ' => 19, + 'ֺ' => 19, + 'ֻ' => 20, + 'ּ' => 21, + 'ֽ' => 22, + 'ֿ' => 23, + 'ׁ' => 24, + 'ׂ' => 25, + 'ׄ' => 230, + 'ׅ' => 220, + 'ׇ' => 18, + 'ؐ' => 230, + 'ؑ' => 230, + 'ؒ' => 230, + 'ؓ' => 230, + 'ؔ' => 230, + 'ؕ' => 230, + 'ؖ' => 230, + 'ؗ' => 230, + 'ؘ' => 30, + 'ؙ' => 31, + 'ؚ' => 32, + 'ً' => 27, + 'ٌ' => 28, + 'ٍ' => 29, + 'َ' => 30, + 'ُ' => 31, + 'ِ' => 32, + 'ّ' => 33, + 'ْ' => 34, + 'ٓ' => 230, + 'ٔ' => 230, + 'ٕ' => 220, + 'ٖ' => 220, + 'ٗ' => 230, + '٘' => 230, + 'ٙ' => 230, + 'ٚ' => 230, + 'ٛ' => 230, + 'ٜ' => 220, + 'ٝ' => 230, + 'ٞ' => 230, + 'ٟ' => 220, + 'ٰ' => 35, + 'ۖ' => 230, + 'ۗ' => 230, + 'ۘ' => 230, + 'ۙ' => 230, + 'ۚ' => 230, + 'ۛ' => 230, + 'ۜ' => 230, + '۟' => 230, + '۠' => 230, + 'ۡ' => 230, + 'ۢ' => 230, + 'ۣ' => 220, + 'ۤ' => 230, + 'ۧ' => 230, + 'ۨ' => 230, + '۪' => 220, + '۫' => 230, + '۬' => 230, + 'ۭ' => 220, + 'ܑ' => 36, + 'ܰ' => 230, + 'ܱ' => 220, + 'ܲ' => 230, + 'ܳ' => 230, + 'ܴ' => 220, + 'ܵ' => 230, + 'ܶ' => 230, + 'ܷ' => 220, + 'ܸ' => 220, + 'ܹ' => 220, + 'ܺ' => 230, + 'ܻ' => 220, + 'ܼ' => 220, + 'ܽ' => 230, + 'ܾ' => 220, + 'ܿ' => 230, + '݀' => 230, + '݁' => 230, + '݂' => 220, + '݃' => 230, + '݄' => 220, + '݅' => 230, + '݆' => 220, + '݇' => 230, + '݈' => 220, + '݉' => 230, + '݊' => 230, + '߫' => 230, + '߬' => 230, + '߭' => 230, + '߮' => 230, + '߯' => 230, + '߰' => 230, + '߱' => 230, + '߲' => 220, + '߳' => 230, + '߽' => 220, + 'ࠖ' => 230, + 'ࠗ' => 230, + '࠘' => 230, + '࠙' => 230, + 'ࠛ' => 230, + 'ࠜ' => 230, + 'ࠝ' => 230, + 'ࠞ' => 230, + 'ࠟ' => 230, + 'ࠠ' => 230, + 'ࠡ' => 230, + 'ࠢ' => 230, + 'ࠣ' => 230, + 'ࠥ' => 230, + 'ࠦ' => 230, + 'ࠧ' => 230, + 'ࠩ' => 230, + 'ࠪ' => 230, + 'ࠫ' => 230, + 'ࠬ' => 230, + '࠭' => 230, + '࡙' => 220, + '࡚' => 220, + '࡛' => 220, + '࣓' => 220, + 'ࣔ' => 230, + 'ࣕ' => 230, + 'ࣖ' => 230, + 'ࣗ' => 230, + 'ࣘ' => 230, + 'ࣙ' => 230, + 'ࣚ' => 230, + 'ࣛ' => 230, + 'ࣜ' => 230, + 'ࣝ' => 230, + 'ࣞ' => 230, + 'ࣟ' => 230, + '࣠' => 230, + '࣡' => 230, + 'ࣣ' => 220, + 'ࣤ' => 230, + 'ࣥ' => 230, + 'ࣦ' => 220, + 'ࣧ' => 230, + 'ࣨ' => 230, + 'ࣩ' => 220, + '࣪' => 230, + '࣫' => 230, + '࣬' => 230, + '࣭' => 220, + '࣮' => 220, + '࣯' => 220, + 'ࣰ' => 27, + 'ࣱ' => 28, + 'ࣲ' => 29, + 'ࣳ' => 230, + 'ࣴ' => 230, + 'ࣵ' => 230, + 'ࣶ' => 220, + 'ࣷ' => 230, + 'ࣸ' => 230, + 'ࣹ' => 220, + 'ࣺ' => 220, + 'ࣻ' => 230, + 'ࣼ' => 230, + 'ࣽ' => 230, + 'ࣾ' => 230, + 'ࣿ' => 230, + '़' => 7, + '्' => 9, + '॑' => 230, + '॒' => 220, + '॓' => 230, + '॔' => 230, + '়' => 7, + '্' => 9, + '৾' => 230, + '਼' => 7, + '੍' => 9, + '઼' => 7, + '્' => 9, + '଼' => 7, + '୍' => 9, + '்' => 9, + '్' => 9, + 'ౕ' => 84, + 'ౖ' => 91, + '಼' => 7, + '್' => 9, + '഻' => 9, + '഼' => 9, + '്' => 9, + '්' => 9, + 'ุ' => 103, + 'ู' => 103, + 'ฺ' => 9, + '่' => 107, + '้' => 107, + '๊' => 107, + '๋' => 107, + 'ຸ' => 118, + 'ູ' => 118, + '຺' => 9, + '່' => 122, + '້' => 122, + '໊' => 122, + '໋' => 122, + '༘' => 220, + '༙' => 220, + '༵' => 220, + '༷' => 220, + '༹' => 216, + 'ཱ' => 129, + 'ི' => 130, + 'ུ' => 132, + 'ེ' => 130, + 'ཻ' => 130, + 'ོ' => 130, + 'ཽ' => 130, + 'ྀ' => 130, + 'ྂ' => 230, + 'ྃ' => 230, + '྄' => 9, + '྆' => 230, + '྇' => 230, + '࿆' => 220, + '့' => 7, + '္' => 9, + '်' => 9, + 'ႍ' => 220, + '፝' => 230, + '፞' => 230, + '፟' => 230, + '᜔' => 9, + '᜴' => 9, + '្' => 9, + '៝' => 230, + 'ᢩ' => 228, + '᤹' => 222, + '᤺' => 230, + '᤻' => 220, + 'ᨗ' => 230, + 'ᨘ' => 220, + '᩠' => 9, + '᩵' => 230, + '᩶' => 230, + '᩷' => 230, + '᩸' => 230, + '᩹' => 230, + '᩺' => 230, + '᩻' => 230, + '᩼' => 230, + '᩿' => 220, + '᪰' => 230, + '᪱' => 230, + '᪲' => 230, + '᪳' => 230, + '᪴' => 230, + '᪵' => 220, + '᪶' => 220, + '᪷' => 220, + '᪸' => 220, + '᪹' => 220, + '᪺' => 220, + '᪻' => 230, + '᪼' => 230, + '᪽' => 220, + 'ᪿ' => 220, + 'ᫀ' => 220, + '᬴' => 7, + '᭄' => 9, + '᭫' => 230, + '᭬' => 220, + '᭭' => 230, + '᭮' => 230, + '᭯' => 230, + '᭰' => 230, + '᭱' => 230, + '᭲' => 230, + '᭳' => 230, + '᮪' => 9, + '᮫' => 9, + '᯦' => 7, + '᯲' => 9, + '᯳' => 9, + '᰷' => 7, + '᳐' => 230, + '᳑' => 230, + '᳒' => 230, + '᳔' => 1, + '᳕' => 220, + '᳖' => 220, + '᳗' => 220, + '᳘' => 220, + '᳙' => 220, + '᳚' => 230, + '᳛' => 230, + '᳜' => 220, + '᳝' => 220, + '᳞' => 220, + '᳟' => 220, + '᳠' => 230, + '᳢' => 1, + '᳣' => 1, + '᳤' => 1, + '᳥' => 1, + '᳦' => 1, + '᳧' => 1, + '᳨' => 1, + '᳭' => 220, + '᳴' => 230, + '᳸' => 230, + '᳹' => 230, + '᷀' => 230, + '᷁' => 230, + '᷂' => 220, + '᷃' => 230, + '᷄' => 230, + '᷅' => 230, + '᷆' => 230, + '᷇' => 230, + '᷈' => 230, + '᷉' => 230, + '᷊' => 220, + '᷋' => 230, + '᷌' => 230, + '᷍' => 234, + '᷎' => 214, + '᷏' => 220, + '᷐' => 202, + '᷑' => 230, + '᷒' => 230, + 'ᷓ' => 230, + 'ᷔ' => 230, + 'ᷕ' => 230, + 'ᷖ' => 230, + 'ᷗ' => 230, + 'ᷘ' => 230, + 'ᷙ' => 230, + 'ᷚ' => 230, + 'ᷛ' => 230, + 'ᷜ' => 230, + 'ᷝ' => 230, + 'ᷞ' => 230, + 'ᷟ' => 230, + 'ᷠ' => 230, + 'ᷡ' => 230, + 'ᷢ' => 230, + 'ᷣ' => 230, + 'ᷤ' => 230, + 'ᷥ' => 230, + 'ᷦ' => 230, + 'ᷧ' => 230, + 'ᷨ' => 230, + 'ᷩ' => 230, + 'ᷪ' => 230, + 'ᷫ' => 230, + 'ᷬ' => 230, + 'ᷭ' => 230, + 'ᷮ' => 230, + 'ᷯ' => 230, + 'ᷰ' => 230, + 'ᷱ' => 230, + 'ᷲ' => 230, + 'ᷳ' => 230, + 'ᷴ' => 230, + '᷵' => 230, + '᷶' => 232, + '᷷' => 228, + '᷸' => 228, + '᷹' => 220, + '᷻' => 230, + '᷼' => 233, + '᷽' => 220, + '᷾' => 230, + '᷿' => 220, + '⃐' => 230, + '⃑' => 230, + '⃒' => 1, + '⃓' => 1, + '⃔' => 230, + '⃕' => 230, + '⃖' => 230, + '⃗' => 230, + '⃘' => 1, + '⃙' => 1, + '⃚' => 1, + '⃛' => 230, + '⃜' => 230, + '⃡' => 230, + '⃥' => 1, + '⃦' => 1, + '⃧' => 230, + '⃨' => 220, + '⃩' => 230, + '⃪' => 1, + '⃫' => 1, + '⃬' => 220, + '⃭' => 220, + '⃮' => 220, + '⃯' => 220, + '⃰' => 230, + '⳯' => 230, + '⳰' => 230, + '⳱' => 230, + '⵿' => 9, + 'ⷠ' => 230, + 'ⷡ' => 230, + 'ⷢ' => 230, + 'ⷣ' => 230, + 'ⷤ' => 230, + 'ⷥ' => 230, + 'ⷦ' => 230, + 'ⷧ' => 230, + 'ⷨ' => 230, + 'ⷩ' => 230, + 'ⷪ' => 230, + 'ⷫ' => 230, + 'ⷬ' => 230, + 'ⷭ' => 230, + 'ⷮ' => 230, + 'ⷯ' => 230, + 'ⷰ' => 230, + 'ⷱ' => 230, + 'ⷲ' => 230, + 'ⷳ' => 230, + 'ⷴ' => 230, + 'ⷵ' => 230, + 'ⷶ' => 230, + 'ⷷ' => 230, + 'ⷸ' => 230, + 'ⷹ' => 230, + 'ⷺ' => 230, + 'ⷻ' => 230, + 'ⷼ' => 230, + 'ⷽ' => 230, + 'ⷾ' => 230, + 'ⷿ' => 230, + '〪' => 218, + '〫' => 228, + '〬' => 232, + '〭' => 222, + '〮' => 224, + '〯' => 224, + '゙' => 8, + '゚' => 8, + '꙯' => 230, + 'ꙴ' => 230, + 'ꙵ' => 230, + 'ꙶ' => 230, + 'ꙷ' => 230, + 'ꙸ' => 230, + 'ꙹ' => 230, + 'ꙺ' => 230, + 'ꙻ' => 230, + '꙼' => 230, + '꙽' => 230, + 'ꚞ' => 230, + 'ꚟ' => 230, + '꛰' => 230, + '꛱' => 230, + '꠆' => 9, + '꠬' => 9, + '꣄' => 9, + '꣠' => 230, + '꣡' => 230, + '꣢' => 230, + '꣣' => 230, + '꣤' => 230, + '꣥' => 230, + '꣦' => 230, + '꣧' => 230, + '꣨' => 230, + '꣩' => 230, + '꣪' => 230, + '꣫' => 230, + '꣬' => 230, + '꣭' => 230, + '꣮' => 230, + '꣯' => 230, + '꣰' => 230, + '꣱' => 230, + '꤫' => 220, + '꤬' => 220, + '꤭' => 220, + '꥓' => 9, + '꦳' => 7, + '꧀' => 9, + 'ꪰ' => 230, + 'ꪲ' => 230, + 'ꪳ' => 230, + 'ꪴ' => 220, + 'ꪷ' => 230, + 'ꪸ' => 230, + 'ꪾ' => 230, + '꪿' => 230, + '꫁' => 230, + '꫶' => 9, + '꯭' => 9, + 'ﬞ' => 26, + '︠' => 230, + '︡' => 230, + '︢' => 230, + '︣' => 230, + '︤' => 230, + '︥' => 230, + '︦' => 230, + '︧' => 220, + '︨' => 220, + '︩' => 220, + '︪' => 220, + '︫' => 220, + '︬' => 220, + '︭' => 220, + '︮' => 230, + '︯' => 230, + '𐇽' => 220, + '𐋠' => 220, + '𐍶' => 230, + '𐍷' => 230, + '𐍸' => 230, + '𐍹' => 230, + '𐍺' => 230, + '𐨍' => 220, + '𐨏' => 230, + '𐨸' => 230, + '𐨹' => 1, + '𐨺' => 220, + '𐨿' => 9, + '𐫥' => 230, + '𐫦' => 220, + '𐴤' => 230, + '𐴥' => 230, + '𐴦' => 230, + '𐴧' => 230, + '𐺫' => 230, + '𐺬' => 230, + '𐽆' => 220, + '𐽇' => 220, + '𐽈' => 230, + '𐽉' => 230, + '𐽊' => 230, + '𐽋' => 220, + '𐽌' => 230, + '𐽍' => 220, + '𐽎' => 220, + '𐽏' => 220, + '𐽐' => 220, + '𑁆' => 9, + '𑁿' => 9, + '𑂹' => 9, + '𑂺' => 7, + '𑄀' => 230, + '𑄁' => 230, + '𑄂' => 230, + '𑄳' => 9, + '𑄴' => 9, + '𑅳' => 7, + '𑇀' => 9, + '𑇊' => 7, + '𑈵' => 9, + '𑈶' => 7, + '𑋩' => 7, + '𑋪' => 9, + '𑌻' => 7, + '𑌼' => 7, + '𑍍' => 9, + '𑍦' => 230, + '𑍧' => 230, + '𑍨' => 230, + '𑍩' => 230, + '𑍪' => 230, + '𑍫' => 230, + '𑍬' => 230, + '𑍰' => 230, + '𑍱' => 230, + '𑍲' => 230, + '𑍳' => 230, + '𑍴' => 230, + '𑑂' => 9, + '𑑆' => 7, + '𑑞' => 230, + '𑓂' => 9, + '𑓃' => 7, + '𑖿' => 9, + '𑗀' => 7, + '𑘿' => 9, + '𑚶' => 9, + '𑚷' => 7, + '𑜫' => 9, + '𑠹' => 9, + '𑠺' => 7, + '𑤽' => 9, + '𑤾' => 9, + '𑥃' => 7, + '𑧠' => 9, + '𑨴' => 9, + '𑩇' => 9, + '𑪙' => 9, + '𑰿' => 9, + '𑵂' => 7, + '𑵄' => 9, + '𑵅' => 9, + '𑶗' => 9, + '𖫰' => 1, + '𖫱' => 1, + '𖫲' => 1, + '𖫳' => 1, + '𖫴' => 1, + '𖬰' => 230, + '𖬱' => 230, + '𖬲' => 230, + '𖬳' => 230, + '𖬴' => 230, + '𖬵' => 230, + '𖬶' => 230, + '𖿰' => 6, + '𖿱' => 6, + '𛲞' => 1, + '𝅥' => 216, + '𝅦' => 216, + '𝅧' => 1, + '𝅨' => 1, + '𝅩' => 1, + '𝅭' => 226, + '𝅮' => 216, + '𝅯' => 216, + '𝅰' => 216, + '𝅱' => 216, + '𝅲' => 216, + '𝅻' => 220, + '𝅼' => 220, + '𝅽' => 220, + '𝅾' => 220, + '𝅿' => 220, + '𝆀' => 220, + '𝆁' => 220, + '𝆂' => 220, + '𝆅' => 230, + '𝆆' => 230, + '𝆇' => 230, + '𝆈' => 230, + '𝆉' => 230, + '𝆊' => 220, + '𝆋' => 220, + '𝆪' => 230, + '𝆫' => 230, + '𝆬' => 230, + '𝆭' => 230, + '𝉂' => 230, + '𝉃' => 230, + '𝉄' => 230, + '𞀀' => 230, + '𞀁' => 230, + '𞀂' => 230, + '𞀃' => 230, + '𞀄' => 230, + '𞀅' => 230, + '𞀆' => 230, + '𞀈' => 230, + '𞀉' => 230, + '𞀊' => 230, + '𞀋' => 230, + '𞀌' => 230, + '𞀍' => 230, + '𞀎' => 230, + '𞀏' => 230, + '𞀐' => 230, + '𞀑' => 230, + '𞀒' => 230, + '𞀓' => 230, + '𞀔' => 230, + '𞀕' => 230, + '𞀖' => 230, + '𞀗' => 230, + '𞀘' => 230, + '𞀛' => 230, + '𞀜' => 230, + '𞀝' => 230, + '𞀞' => 230, + '𞀟' => 230, + '𞀠' => 230, + '𞀡' => 230, + '𞀣' => 230, + '𞀤' => 230, + '𞀦' => 230, + '𞀧' => 230, + '𞀨' => 230, + '𞀩' => 230, + '𞀪' => 230, + '𞄰' => 230, + '𞄱' => 230, + '𞄲' => 230, + '𞄳' => 230, + '𞄴' => 230, + '𞄵' => 230, + '𞄶' => 230, + '𞋬' => 230, + '𞋭' => 230, + '𞋮' => 230, + '𞋯' => 230, + '𞣐' => 220, + '𞣑' => 220, + '𞣒' => 220, + '𞣓' => 220, + '𞣔' => 220, + '𞣕' => 220, + '𞣖' => 220, + '𞥄' => 230, + '𞥅' => 230, + '𞥆' => 230, + '𞥇' => 230, + '𞥈' => 230, + '𞥉' => 230, + '𞥊' => 7, +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/compatibilityDecomposition.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/compatibilityDecomposition.php new file mode 100644 index 000000000..157490289 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/compatibilityDecomposition.php @@ -0,0 +1,3695 @@ + ' ', + '¨' => ' ̈', + 'ª' => 'a', + '¯' => ' ̄', + '²' => '2', + '³' => '3', + '´' => ' ́', + 'µ' => 'μ', + '¸' => ' ̧', + '¹' => '1', + 'º' => 'o', + '¼' => '1⁄4', + '½' => '1⁄2', + '¾' => '3⁄4', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'Ŀ' => 'L·', + 'ŀ' => 'l·', + 'ʼn' => 'ʼn', + 'ſ' => 's', + 'DŽ' => 'DŽ', + 'Dž' => 'Dž', + 'dž' => 'dž', + 'LJ' => 'LJ', + 'Lj' => 'Lj', + 'lj' => 'lj', + 'NJ' => 'NJ', + 'Nj' => 'Nj', + 'nj' => 'nj', + 'DZ' => 'DZ', + 'Dz' => 'Dz', + 'dz' => 'dz', + 'ʰ' => 'h', + 'ʱ' => 'ɦ', + 'ʲ' => 'j', + 'ʳ' => 'r', + 'ʴ' => 'ɹ', + 'ʵ' => 'ɻ', + 'ʶ' => 'ʁ', + 'ʷ' => 'w', + 'ʸ' => 'y', + '˘' => ' ̆', + '˙' => ' ̇', + '˚' => ' ̊', + '˛' => ' ̨', + '˜' => ' ̃', + '˝' => ' ̋', + 'ˠ' => 'ɣ', + 'ˡ' => 'l', + 'ˢ' => 's', + 'ˣ' => 'x', + 'ˤ' => 'ʕ', + 'ͺ' => ' ͅ', + '΄' => ' ́', + '΅' => ' ̈́', + 'ϐ' => 'β', + 'ϑ' => 'θ', + 'ϒ' => 'Υ', + 'ϓ' => 'Ύ', + 'ϔ' => 'Ϋ', + 'ϕ' => 'φ', + 'ϖ' => 'π', + 'ϰ' => 'κ', + 'ϱ' => 'ρ', + 'ϲ' => 'ς', + 'ϴ' => 'Θ', + 'ϵ' => 'ε', + 'Ϲ' => 'Σ', + 'և' => 'եւ', + 'ٵ' => 'اٴ', + 'ٶ' => 'وٴ', + 'ٷ' => 'ۇٴ', + 'ٸ' => 'يٴ', + 'ำ' => 'ํา', + 'ຳ' => 'ໍາ', + 'ໜ' => 'ຫນ', + 'ໝ' => 'ຫມ', + '༌' => '་', + 'ཷ' => 'ྲཱྀ', + 'ཹ' => 'ླཱྀ', + 'ჼ' => 'ნ', + 'ᴬ' => 'A', + 'ᴭ' => 'Æ', + 'ᴮ' => 'B', + 'ᴰ' => 'D', + 'ᴱ' => 'E', + 'ᴲ' => 'Ǝ', + 'ᴳ' => 'G', + 'ᴴ' => 'H', + 'ᴵ' => 'I', + 'ᴶ' => 'J', + 'ᴷ' => 'K', + 'ᴸ' => 'L', + 'ᴹ' => 'M', + 'ᴺ' => 'N', + 'ᴼ' => 'O', + 'ᴽ' => 'Ȣ', + 'ᴾ' => 'P', + 'ᴿ' => 'R', + 'ᵀ' => 'T', + 'ᵁ' => 'U', + 'ᵂ' => 'W', + 'ᵃ' => 'a', + 'ᵄ' => 'ɐ', + 'ᵅ' => 'ɑ', + 'ᵆ' => 'ᴂ', + 'ᵇ' => 'b', + 'ᵈ' => 'd', + 'ᵉ' => 'e', + 'ᵊ' => 'ə', + 'ᵋ' => 'ɛ', + 'ᵌ' => 'ɜ', + 'ᵍ' => 'g', + 'ᵏ' => 'k', + 'ᵐ' => 'm', + 'ᵑ' => 'ŋ', + 'ᵒ' => 'o', + 'ᵓ' => 'ɔ', + 'ᵔ' => 'ᴖ', + 'ᵕ' => 'ᴗ', + 'ᵖ' => 'p', + 'ᵗ' => 't', + 'ᵘ' => 'u', + 'ᵙ' => 'ᴝ', + 'ᵚ' => 'ɯ', + 'ᵛ' => 'v', + 'ᵜ' => 'ᴥ', + 'ᵝ' => 'β', + 'ᵞ' => 'γ', + 'ᵟ' => 'δ', + 'ᵠ' => 'φ', + 'ᵡ' => 'χ', + 'ᵢ' => 'i', + 'ᵣ' => 'r', + 'ᵤ' => 'u', + 'ᵥ' => 'v', + 'ᵦ' => 'β', + 'ᵧ' => 'γ', + 'ᵨ' => 'ρ', + 'ᵩ' => 'φ', + 'ᵪ' => 'χ', + 'ᵸ' => 'н', + 'ᶛ' => 'ɒ', + 'ᶜ' => 'c', + 'ᶝ' => 'ɕ', + 'ᶞ' => 'ð', + 'ᶟ' => 'ɜ', + 'ᶠ' => 'f', + 'ᶡ' => 'ɟ', + 'ᶢ' => 'ɡ', + 'ᶣ' => 'ɥ', + 'ᶤ' => 'ɨ', + 'ᶥ' => 'ɩ', + 'ᶦ' => 'ɪ', + 'ᶧ' => 'ᵻ', + 'ᶨ' => 'ʝ', + 'ᶩ' => 'ɭ', + 'ᶪ' => 'ᶅ', + 'ᶫ' => 'ʟ', + 'ᶬ' => 'ɱ', + 'ᶭ' => 'ɰ', + 'ᶮ' => 'ɲ', + 'ᶯ' => 'ɳ', + 'ᶰ' => 'ɴ', + 'ᶱ' => 'ɵ', + 'ᶲ' => 'ɸ', + 'ᶳ' => 'ʂ', + 'ᶴ' => 'ʃ', + 'ᶵ' => 'ƫ', + 'ᶶ' => 'ʉ', + 'ᶷ' => 'ʊ', + 'ᶸ' => 'ᴜ', + 'ᶹ' => 'ʋ', + 'ᶺ' => 'ʌ', + 'ᶻ' => 'z', + 'ᶼ' => 'ʐ', + 'ᶽ' => 'ʑ', + 'ᶾ' => 'ʒ', + 'ᶿ' => 'θ', + 'ẚ' => 'aʾ', + 'ẛ' => 'ṡ', + '᾽' => ' ̓', + '᾿' => ' ̓', + '῀' => ' ͂', + '῁' => ' ̈͂', + '῍' => ' ̓̀', + '῎' => ' ̓́', + '῏' => ' ̓͂', + '῝' => ' ̔̀', + '῞' => ' ̔́', + '῟' => ' ̔͂', + '῭' => ' ̈̀', + '΅' => ' ̈́', + '´' => ' ́', + '῾' => ' ̔', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + '‑' => '‐', + '‗' => ' ̳', + '․' => '.', + '‥' => '..', + '…' => '...', + ' ' => ' ', + '″' => '′′', + '‴' => '′′′', + '‶' => '‵‵', + '‷' => '‵‵‵', + '‼' => '!!', + '‾' => ' ̅', + '⁇' => '??', + '⁈' => '?!', + '⁉' => '!?', + '⁗' => '′′′′', + ' ' => ' ', + '⁰' => '0', + 'ⁱ' => 'i', + '⁴' => '4', + '⁵' => '5', + '⁶' => '6', + '⁷' => '7', + '⁸' => '8', + '⁹' => '9', + '⁺' => '+', + '⁻' => '−', + '⁼' => '=', + '⁽' => '(', + '⁾' => ')', + 'ⁿ' => 'n', + '₀' => '0', + '₁' => '1', + '₂' => '2', + '₃' => '3', + '₄' => '4', + '₅' => '5', + '₆' => '6', + '₇' => '7', + '₈' => '8', + '₉' => '9', + '₊' => '+', + '₋' => '−', + '₌' => '=', + '₍' => '(', + '₎' => ')', + 'ₐ' => 'a', + 'ₑ' => 'e', + 'ₒ' => 'o', + 'ₓ' => 'x', + 'ₔ' => 'ə', + 'ₕ' => 'h', + 'ₖ' => 'k', + 'ₗ' => 'l', + 'ₘ' => 'm', + 'ₙ' => 'n', + 'ₚ' => 'p', + 'ₛ' => 's', + 'ₜ' => 't', + '₨' => 'Rs', + '℀' => 'a/c', + '℁' => 'a/s', + 'ℂ' => 'C', + '℃' => '°C', + '℅' => 'c/o', + '℆' => 'c/u', + 'ℇ' => 'Ɛ', + '℉' => '°F', + 'ℊ' => 'g', + 'ℋ' => 'H', + 'ℌ' => 'H', + 'ℍ' => 'H', + 'ℎ' => 'h', + 'ℏ' => 'ħ', + 'ℐ' => 'I', + 'ℑ' => 'I', + 'ℒ' => 'L', + 'ℓ' => 'l', + 'ℕ' => 'N', + '№' => 'No', + 'ℙ' => 'P', + 'ℚ' => 'Q', + 'ℛ' => 'R', + 'ℜ' => 'R', + 'ℝ' => 'R', + '℠' => 'SM', + '℡' => 'TEL', + '™' => 'TM', + 'ℤ' => 'Z', + 'ℨ' => 'Z', + 'ℬ' => 'B', + 'ℭ' => 'C', + 'ℯ' => 'e', + 'ℰ' => 'E', + 'ℱ' => 'F', + 'ℳ' => 'M', + 'ℴ' => 'o', + 'ℵ' => 'א', + 'ℶ' => 'ב', + 'ℷ' => 'ג', + 'ℸ' => 'ד', + 'ℹ' => 'i', + '℻' => 'FAX', + 'ℼ' => 'π', + 'ℽ' => 'γ', + 'ℾ' => 'Γ', + 'ℿ' => 'Π', + '⅀' => '∑', + 'ⅅ' => 'D', + 'ⅆ' => 'd', + 'ⅇ' => 'e', + 'ⅈ' => 'i', + 'ⅉ' => 'j', + '⅐' => '1⁄7', + '⅑' => '1⁄9', + '⅒' => '1⁄10', + '⅓' => '1⁄3', + '⅔' => '2⁄3', + '⅕' => '1⁄5', + '⅖' => '2⁄5', + '⅗' => '3⁄5', + '⅘' => '4⁄5', + '⅙' => '1⁄6', + '⅚' => '5⁄6', + '⅛' => '1⁄8', + '⅜' => '3⁄8', + '⅝' => '5⁄8', + '⅞' => '7⁄8', + '⅟' => '1⁄', + 'Ⅰ' => 'I', + 'Ⅱ' => 'II', + 'Ⅲ' => 'III', + 'Ⅳ' => 'IV', + 'Ⅴ' => 'V', + 'Ⅵ' => 'VI', + 'Ⅶ' => 'VII', + 'Ⅷ' => 'VIII', + 'Ⅸ' => 'IX', + 'Ⅹ' => 'X', + 'Ⅺ' => 'XI', + 'Ⅻ' => 'XII', + 'Ⅼ' => 'L', + 'Ⅽ' => 'C', + 'Ⅾ' => 'D', + 'Ⅿ' => 'M', + 'ⅰ' => 'i', + 'ⅱ' => 'ii', + 'ⅲ' => 'iii', + 'ⅳ' => 'iv', + 'ⅴ' => 'v', + 'ⅵ' => 'vi', + 'ⅶ' => 'vii', + 'ⅷ' => 'viii', + 'ⅸ' => 'ix', + 'ⅹ' => 'x', + 'ⅺ' => 'xi', + 'ⅻ' => 'xii', + 'ⅼ' => 'l', + 'ⅽ' => 'c', + 'ⅾ' => 'd', + 'ⅿ' => 'm', + '↉' => '0⁄3', + '∬' => '∫∫', + '∭' => '∫∫∫', + '∯' => '∮∮', + '∰' => '∮∮∮', + '①' => '1', + '②' => '2', + '③' => '3', + '④' => '4', + '⑤' => '5', + '⑥' => '6', + '⑦' => '7', + '⑧' => '8', + '⑨' => '9', + '⑩' => '10', + '⑪' => '11', + '⑫' => '12', + '⑬' => '13', + '⑭' => '14', + '⑮' => '15', + '⑯' => '16', + '⑰' => '17', + '⑱' => '18', + '⑲' => '19', + '⑳' => '20', + '⑴' => '(1)', + '⑵' => '(2)', + '⑶' => '(3)', + '⑷' => '(4)', + '⑸' => '(5)', + '⑹' => '(6)', + '⑺' => '(7)', + '⑻' => '(8)', + '⑼' => '(9)', + '⑽' => '(10)', + '⑾' => '(11)', + '⑿' => '(12)', + '⒀' => '(13)', + '⒁' => '(14)', + '⒂' => '(15)', + '⒃' => '(16)', + '⒄' => '(17)', + '⒅' => '(18)', + '⒆' => '(19)', + '⒇' => '(20)', + '⒈' => '1.', + '⒉' => '2.', + '⒊' => '3.', + '⒋' => '4.', + '⒌' => '5.', + '⒍' => '6.', + '⒎' => '7.', + '⒏' => '8.', + '⒐' => '9.', + '⒑' => '10.', + '⒒' => '11.', + '⒓' => '12.', + '⒔' => '13.', + '⒕' => '14.', + '⒖' => '15.', + '⒗' => '16.', + '⒘' => '17.', + '⒙' => '18.', + '⒚' => '19.', + '⒛' => '20.', + '⒜' => '(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)', + 'Ⓐ' => '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', + 'ⓐ' => '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', + '⨌' => '∫∫∫∫', + '⩴' => '::=', + '⩵' => '==', + '⩶' => '===', + 'ⱼ' => 'j', + 'ⱽ' => 'V', + 'ⵯ' => 'ⵡ', + '⺟' => '母', + '⻳' => '龟', + '⼀' => '一', + '⼁' => '丨', + '⼂' => '丶', + '⼃' => '丿', + '⼄' => '乙', + '⼅' => '亅', + '⼆' => '二', + '⼇' => '亠', + '⼈' => '人', + '⼉' => '儿', + '⼊' => '入', + '⼋' => '八', + '⼌' => '冂', + '⼍' => '冖', + '⼎' => '冫', + '⼏' => '几', + '⼐' => '凵', + '⼑' => '刀', + '⼒' => '力', + '⼓' => '勹', + '⼔' => '匕', + '⼕' => '匚', + '⼖' => '匸', + '⼗' => '十', + '⼘' => '卜', + '⼙' => '卩', + '⼚' => '厂', + '⼛' => '厶', + '⼜' => '又', + '⼝' => '口', + '⼞' => '囗', + '⼟' => '土', + '⼠' => '士', + '⼡' => '夂', + '⼢' => '夊', + '⼣' => '夕', + '⼤' => '大', + '⼥' => '女', + '⼦' => '子', + '⼧' => '宀', + '⼨' => '寸', + '⼩' => '小', + '⼪' => '尢', + '⼫' => '尸', + '⼬' => '屮', + '⼭' => '山', + '⼮' => '巛', + '⼯' => '工', + '⼰' => '己', + '⼱' => '巾', + '⼲' => '干', + '⼳' => '幺', + '⼴' => '广', + '⼵' => '廴', + '⼶' => '廾', + '⼷' => '弋', + '⼸' => '弓', + '⼹' => '彐', + '⼺' => '彡', + '⼻' => '彳', + '⼼' => '心', + '⼽' => '戈', + '⼾' => '戶', + '⼿' => '手', + '⽀' => '支', + '⽁' => '攴', + '⽂' => '文', + '⽃' => '斗', + '⽄' => '斤', + '⽅' => '方', + '⽆' => '无', + '⽇' => '日', + '⽈' => '曰', + '⽉' => '月', + '⽊' => '木', + '⽋' => '欠', + '⽌' => '止', + '⽍' => '歹', + '⽎' => '殳', + '⽏' => '毋', + '⽐' => '比', + '⽑' => '毛', + '⽒' => '氏', + '⽓' => '气', + '⽔' => '水', + '⽕' => '火', + '⽖' => '爪', + '⽗' => '父', + '⽘' => '爻', + '⽙' => '爿', + '⽚' => '片', + '⽛' => '牙', + '⽜' => '牛', + '⽝' => '犬', + '⽞' => '玄', + '⽟' => '玉', + '⽠' => '瓜', + '⽡' => '瓦', + '⽢' => '甘', + '⽣' => '生', + '⽤' => '用', + '⽥' => '田', + '⽦' => '疋', + '⽧' => '疒', + '⽨' => '癶', + '⽩' => '白', + '⽪' => '皮', + '⽫' => '皿', + '⽬' => '目', + '⽭' => '矛', + '⽮' => '矢', + '⽯' => '石', + '⽰' => '示', + '⽱' => '禸', + '⽲' => '禾', + '⽳' => '穴', + '⽴' => '立', + '⽵' => '竹', + '⽶' => '米', + '⽷' => '糸', + '⽸' => '缶', + '⽹' => '网', + '⽺' => '羊', + '⽻' => '羽', + '⽼' => '老', + '⽽' => '而', + '⽾' => '耒', + '⽿' => '耳', + '⾀' => '聿', + '⾁' => '肉', + '⾂' => '臣', + '⾃' => '自', + '⾄' => '至', + '⾅' => '臼', + '⾆' => '舌', + '⾇' => '舛', + '⾈' => '舟', + '⾉' => '艮', + '⾊' => '色', + '⾋' => '艸', + '⾌' => '虍', + '⾍' => '虫', + '⾎' => '血', + '⾏' => '行', + '⾐' => '衣', + '⾑' => '襾', + '⾒' => '見', + '⾓' => '角', + '⾔' => '言', + '⾕' => '谷', + '⾖' => '豆', + '⾗' => '豕', + '⾘' => '豸', + '⾙' => '貝', + '⾚' => '赤', + '⾛' => '走', + '⾜' => '足', + '⾝' => '身', + '⾞' => '車', + '⾟' => '辛', + '⾠' => '辰', + '⾡' => '辵', + '⾢' => '邑', + '⾣' => '酉', + '⾤' => '釆', + '⾥' => '里', + '⾦' => '金', + '⾧' => '長', + '⾨' => '門', + '⾩' => '阜', + '⾪' => '隶', + '⾫' => '隹', + '⾬' => '雨', + '⾭' => '靑', + '⾮' => '非', + '⾯' => '面', + '⾰' => '革', + '⾱' => '韋', + '⾲' => '韭', + '⾳' => '音', + '⾴' => '頁', + '⾵' => '風', + '⾶' => '飛', + '⾷' => '食', + '⾸' => '首', + '⾹' => '香', + '⾺' => '馬', + '⾻' => '骨', + '⾼' => '高', + '⾽' => '髟', + '⾾' => '鬥', + '⾿' => '鬯', + '⿀' => '鬲', + '⿁' => '鬼', + '⿂' => '魚', + '⿃' => '鳥', + '⿄' => '鹵', + '⿅' => '鹿', + '⿆' => '麥', + '⿇' => '麻', + '⿈' => '黃', + '⿉' => '黍', + '⿊' => '黑', + '⿋' => '黹', + '⿌' => '黽', + '⿍' => '鼎', + '⿎' => '鼓', + '⿏' => '鼠', + '⿐' => '鼻', + '⿑' => '齊', + '⿒' => '齒', + '⿓' => '龍', + '⿔' => '龜', + '⿕' => '龠', + ' ' => ' ', + '〶' => '〒', + '〸' => '十', + '〹' => '卄', + '〺' => '卅', + '゛' => ' ゙', + '゜' => ' ゚', + 'ゟ' => 'より', + 'ヿ' => 'コト', + 'ㄱ' => 'ᄀ', + 'ㄲ' => 'ᄁ', + 'ㄳ' => 'ᆪ', + 'ㄴ' => 'ᄂ', + 'ㄵ' => 'ᆬ', + 'ㄶ' => 'ᆭ', + 'ㄷ' => 'ᄃ', + 'ㄸ' => 'ᄄ', + 'ㄹ' => 'ᄅ', + 'ㄺ' => 'ᆰ', + 'ㄻ' => 'ᆱ', + 'ㄼ' => 'ᆲ', + 'ㄽ' => 'ᆳ', + 'ㄾ' => 'ᆴ', + 'ㄿ' => 'ᆵ', + 'ㅀ' => 'ᄚ', + 'ㅁ' => 'ᄆ', + 'ㅂ' => 'ᄇ', + 'ㅃ' => 'ᄈ', + 'ㅄ' => 'ᄡ', + 'ㅅ' => 'ᄉ', + 'ㅆ' => 'ᄊ', + 'ㅇ' => 'ᄋ', + 'ㅈ' => 'ᄌ', + 'ㅉ' => 'ᄍ', + 'ㅊ' => 'ᄎ', + 'ㅋ' => 'ᄏ', + 'ㅌ' => 'ᄐ', + 'ㅍ' => 'ᄑ', + 'ㅎ' => 'ᄒ', + 'ㅏ' => 'ᅡ', + 'ㅐ' => 'ᅢ', + 'ㅑ' => 'ᅣ', + 'ㅒ' => 'ᅤ', + 'ㅓ' => 'ᅥ', + 'ㅔ' => 'ᅦ', + 'ㅕ' => 'ᅧ', + 'ㅖ' => 'ᅨ', + 'ㅗ' => 'ᅩ', + 'ㅘ' => 'ᅪ', + 'ㅙ' => 'ᅫ', + 'ㅚ' => 'ᅬ', + 'ㅛ' => 'ᅭ', + 'ㅜ' => 'ᅮ', + 'ㅝ' => 'ᅯ', + 'ㅞ' => 'ᅰ', + 'ㅟ' => 'ᅱ', + 'ㅠ' => 'ᅲ', + 'ㅡ' => 'ᅳ', + 'ㅢ' => 'ᅴ', + 'ㅣ' => 'ᅵ', + 'ㅤ' => 'ᅠ', + 'ㅥ' => 'ᄔ', + 'ㅦ' => 'ᄕ', + 'ㅧ' => 'ᇇ', + 'ㅨ' => 'ᇈ', + 'ㅩ' => 'ᇌ', + 'ㅪ' => 'ᇎ', + 'ㅫ' => 'ᇓ', + 'ㅬ' => 'ᇗ', + 'ㅭ' => 'ᇙ', + 'ㅮ' => 'ᄜ', + 'ㅯ' => 'ᇝ', + 'ㅰ' => 'ᇟ', + 'ㅱ' => 'ᄝ', + 'ㅲ' => 'ᄞ', + 'ㅳ' => 'ᄠ', + 'ㅴ' => 'ᄢ', + 'ㅵ' => 'ᄣ', + 'ㅶ' => 'ᄧ', + 'ㅷ' => 'ᄩ', + 'ㅸ' => 'ᄫ', + 'ㅹ' => 'ᄬ', + 'ㅺ' => 'ᄭ', + 'ㅻ' => 'ᄮ', + 'ㅼ' => 'ᄯ', + 'ㅽ' => 'ᄲ', + 'ㅾ' => 'ᄶ', + 'ㅿ' => 'ᅀ', + 'ㆀ' => 'ᅇ', + 'ㆁ' => 'ᅌ', + 'ㆂ' => 'ᇱ', + 'ㆃ' => 'ᇲ', + 'ㆄ' => 'ᅗ', + 'ㆅ' => 'ᅘ', + 'ㆆ' => 'ᅙ', + 'ㆇ' => 'ᆄ', + 'ㆈ' => 'ᆅ', + 'ㆉ' => 'ᆈ', + 'ㆊ' => 'ᆑ', + 'ㆋ' => 'ᆒ', + 'ㆌ' => 'ᆔ', + 'ㆍ' => 'ᆞ', + 'ㆎ' => 'ᆡ', + '㆒' => '一', + '㆓' => '二', + '㆔' => '三', + '㆕' => '四', + '㆖' => '上', + '㆗' => '中', + '㆘' => '下', + '㆙' => '甲', + '㆚' => '乙', + '㆛' => '丙', + '㆜' => '丁', + '㆝' => '天', + '㆞' => '地', + '㆟' => '人', + '㈀' => '(ᄀ)', + '㈁' => '(ᄂ)', + '㈂' => '(ᄃ)', + '㈃' => '(ᄅ)', + '㈄' => '(ᄆ)', + '㈅' => '(ᄇ)', + '㈆' => '(ᄉ)', + '㈇' => '(ᄋ)', + '㈈' => '(ᄌ)', + '㈉' => '(ᄎ)', + '㈊' => '(ᄏ)', + '㈋' => '(ᄐ)', + '㈌' => '(ᄑ)', + '㈍' => '(ᄒ)', + '㈎' => '(가)', + '㈏' => '(나)', + '㈐' => '(다)', + '㈑' => '(라)', + '㈒' => '(마)', + '㈓' => '(바)', + '㈔' => '(사)', + '㈕' => '(아)', + '㈖' => '(자)', + '㈗' => '(차)', + '㈘' => '(카)', + '㈙' => '(타)', + '㈚' => '(파)', + '㈛' => '(하)', + '㈜' => '(주)', + '㈝' => '(오전)', + '㈞' => '(오후)', + '㈠' => '(一)', + '㈡' => '(二)', + '㈢' => '(三)', + '㈣' => '(四)', + '㈤' => '(五)', + '㈥' => '(六)', + '㈦' => '(七)', + '㈧' => '(八)', + '㈨' => '(九)', + '㈩' => '(十)', + '㈪' => '(月)', + '㈫' => '(火)', + '㈬' => '(水)', + '㈭' => '(木)', + '㈮' => '(金)', + '㈯' => '(土)', + '㈰' => '(日)', + '㈱' => '(株)', + '㈲' => '(有)', + '㈳' => '(社)', + '㈴' => '(名)', + '㈵' => '(特)', + '㈶' => '(財)', + '㈷' => '(祝)', + '㈸' => '(労)', + '㈹' => '(代)', + '㈺' => '(呼)', + '㈻' => '(学)', + '㈼' => '(監)', + '㈽' => '(企)', + '㈾' => '(資)', + '㈿' => '(協)', + '㉀' => '(祭)', + '㉁' => '(休)', + '㉂' => '(自)', + '㉃' => '(至)', + '㉄' => '問', + '㉅' => '幼', + '㉆' => '文', + '㉇' => '箏', + '㉐' => 'PTE', + '㉑' => '21', + '㉒' => '22', + '㉓' => '23', + '㉔' => '24', + '㉕' => '25', + '㉖' => '26', + '㉗' => '27', + '㉘' => '28', + '㉙' => '29', + '㉚' => '30', + '㉛' => '31', + '㉜' => '32', + '㉝' => '33', + '㉞' => '34', + '㉟' => '35', + '㉠' => 'ᄀ', + '㉡' => 'ᄂ', + '㉢' => 'ᄃ', + '㉣' => 'ᄅ', + '㉤' => 'ᄆ', + '㉥' => 'ᄇ', + '㉦' => 'ᄉ', + '㉧' => 'ᄋ', + '㉨' => 'ᄌ', + '㉩' => 'ᄎ', + '㉪' => 'ᄏ', + '㉫' => 'ᄐ', + '㉬' => 'ᄑ', + '㉭' => 'ᄒ', + '㉮' => '가', + '㉯' => '나', + '㉰' => '다', + '㉱' => '라', + '㉲' => '마', + '㉳' => '바', + '㉴' => '사', + '㉵' => '아', + '㉶' => '자', + '㉷' => '차', + '㉸' => '카', + '㉹' => '타', + '㉺' => '파', + '㉻' => '하', + '㉼' => '참고', + '㉽' => '주의', + '㉾' => '우', + '㊀' => '一', + '㊁' => '二', + '㊂' => '三', + '㊃' => '四', + '㊄' => '五', + '㊅' => '六', + '㊆' => '七', + '㊇' => '八', + '㊈' => '九', + '㊉' => '十', + '㊊' => '月', + '㊋' => '火', + '㊌' => '水', + '㊍' => '木', + '㊎' => '金', + '㊏' => '土', + '㊐' => '日', + '㊑' => '株', + '㊒' => '有', + '㊓' => '社', + '㊔' => '名', + '㊕' => '特', + '㊖' => '財', + '㊗' => '祝', + '㊘' => '労', + '㊙' => '秘', + '㊚' => '男', + '㊛' => '女', + '㊜' => '適', + '㊝' => '優', + '㊞' => '印', + '㊟' => '注', + '㊠' => '項', + '㊡' => '休', + '㊢' => '写', + '㊣' => '正', + '㊤' => '上', + '㊥' => '中', + '㊦' => '下', + '㊧' => '左', + '㊨' => '右', + '㊩' => '医', + '㊪' => '宗', + '㊫' => '学', + '㊬' => '監', + '㊭' => '企', + '㊮' => '資', + '㊯' => '協', + '㊰' => '夜', + '㊱' => '36', + '㊲' => '37', + '㊳' => '38', + '㊴' => '39', + '㊵' => '40', + '㊶' => '41', + '㊷' => '42', + '㊸' => '43', + '㊹' => '44', + '㊺' => '45', + '㊻' => '46', + '㊼' => '47', + '㊽' => '48', + '㊾' => '49', + '㊿' => '50', + '㋀' => '1月', + '㋁' => '2月', + '㋂' => '3月', + '㋃' => '4月', + '㋄' => '5月', + '㋅' => '6月', + '㋆' => '7月', + '㋇' => '8月', + '㋈' => '9月', + '㋉' => '10月', + '㋊' => '11月', + '㋋' => '12月', + '㋌' => 'Hg', + '㋍' => 'erg', + '㋎' => 'eV', + '㋏' => 'LTD', + '㋐' => 'ア', + '㋑' => 'イ', + '㋒' => 'ウ', + '㋓' => 'エ', + '㋔' => 'オ', + '㋕' => 'カ', + '㋖' => 'キ', + '㋗' => 'ク', + '㋘' => 'ケ', + '㋙' => 'コ', + '㋚' => 'サ', + '㋛' => 'シ', + '㋜' => 'ス', + '㋝' => 'セ', + '㋞' => 'ソ', + '㋟' => 'タ', + '㋠' => 'チ', + '㋡' => 'ツ', + '㋢' => 'テ', + '㋣' => 'ト', + '㋤' => 'ナ', + '㋥' => 'ニ', + '㋦' => 'ヌ', + '㋧' => 'ネ', + '㋨' => 'ノ', + '㋩' => 'ハ', + '㋪' => 'ヒ', + '㋫' => 'フ', + '㋬' => 'ヘ', + '㋭' => 'ホ', + '㋮' => 'マ', + '㋯' => 'ミ', + '㋰' => 'ム', + '㋱' => 'メ', + '㋲' => 'モ', + '㋳' => 'ヤ', + '㋴' => 'ユ', + '㋵' => 'ヨ', + '㋶' => 'ラ', + '㋷' => 'リ', + '㋸' => 'ル', + '㋹' => 'レ', + '㋺' => 'ロ', + '㋻' => 'ワ', + '㋼' => 'ヰ', + '㋽' => 'ヱ', + '㋾' => 'ヲ', + '㋿' => '令和', + '㌀' => 'アパート', + '㌁' => 'アルファ', + '㌂' => 'アンペア', + '㌃' => 'アール', + '㌄' => 'イニング', + '㌅' => 'インチ', + '㌆' => 'ウォン', + '㌇' => 'エスクード', + '㌈' => 'エーカー', + '㌉' => 'オンス', + '㌊' => 'オーム', + '㌋' => 'カイリ', + '㌌' => 'カラット', + '㌍' => 'カロリー', + '㌎' => 'ガロン', + '㌏' => 'ガンマ', + '㌐' => 'ギガ', + '㌑' => 'ギニー', + '㌒' => 'キュリー', + '㌓' => 'ギルダー', + '㌔' => 'キロ', + '㌕' => 'キログラム', + '㌖' => 'キロメートル', + '㌗' => 'キロワット', + '㌘' => 'グラム', + '㌙' => 'グラムトン', + '㌚' => 'クルゼイロ', + '㌛' => 'クローネ', + '㌜' => 'ケース', + '㌝' => 'コルナ', + '㌞' => 'コーポ', + '㌟' => 'サイクル', + '㌠' => 'サンチーム', + '㌡' => 'シリング', + '㌢' => 'センチ', + '㌣' => 'セント', + '㌤' => 'ダース', + '㌥' => 'デシ', + '㌦' => 'ドル', + '㌧' => 'トン', + '㌨' => 'ナノ', + '㌩' => 'ノット', + '㌪' => 'ハイツ', + '㌫' => 'パーセント', + '㌬' => 'パーツ', + '㌭' => 'バーレル', + '㌮' => 'ピアストル', + '㌯' => 'ピクル', + '㌰' => 'ピコ', + '㌱' => 'ビル', + '㌲' => 'ファラッド', + '㌳' => 'フィート', + '㌴' => 'ブッシェル', + '㌵' => 'フラン', + '㌶' => 'ヘクタール', + '㌷' => 'ペソ', + '㌸' => 'ペニヒ', + '㌹' => 'ヘルツ', + '㌺' => 'ペンス', + '㌻' => 'ページ', + '㌼' => 'ベータ', + '㌽' => 'ポイント', + '㌾' => 'ボルト', + '㌿' => 'ホン', + '㍀' => 'ポンド', + '㍁' => 'ホール', + '㍂' => 'ホーン', + '㍃' => 'マイクロ', + '㍄' => 'マイル', + '㍅' => 'マッハ', + '㍆' => 'マルク', + '㍇' => 'マンション', + '㍈' => 'ミクロン', + '㍉' => 'ミリ', + '㍊' => 'ミリバール', + '㍋' => 'メガ', + '㍌' => 'メガトン', + '㍍' => 'メートル', + '㍎' => 'ヤード', + '㍏' => 'ヤール', + '㍐' => 'ユアン', + '㍑' => 'リットル', + '㍒' => 'リラ', + '㍓' => 'ルピー', + '㍔' => 'ルーブル', + '㍕' => 'レム', + '㍖' => 'レントゲン', + '㍗' => 'ワット', + '㍘' => '0点', + '㍙' => '1点', + '㍚' => '2点', + '㍛' => '3点', + '㍜' => '4点', + '㍝' => '5点', + '㍞' => '6点', + '㍟' => '7点', + '㍠' => '8点', + '㍡' => '9点', + '㍢' => '10点', + '㍣' => '11点', + '㍤' => '12点', + '㍥' => '13点', + '㍦' => '14点', + '㍧' => '15点', + '㍨' => '16点', + '㍩' => '17点', + '㍪' => '18点', + '㍫' => '19点', + '㍬' => '20点', + '㍭' => '21点', + '㍮' => '22点', + '㍯' => '23点', + '㍰' => '24点', + '㍱' => 'hPa', + '㍲' => 'da', + '㍳' => 'AU', + '㍴' => 'bar', + '㍵' => 'oV', + '㍶' => 'pc', + '㍷' => 'dm', + '㍸' => 'dm2', + '㍹' => 'dm3', + '㍺' => 'IU', + '㍻' => '平成', + '㍼' => '昭和', + '㍽' => '大正', + '㍾' => '明治', + '㍿' => '株式会社', + '㎀' => 'pA', + '㎁' => 'nA', + '㎂' => 'μA', + '㎃' => 'mA', + '㎄' => 'kA', + '㎅' => 'KB', + '㎆' => 'MB', + '㎇' => 'GB', + '㎈' => 'cal', + '㎉' => 'kcal', + '㎊' => 'pF', + '㎋' => 'nF', + '㎌' => 'μF', + '㎍' => 'μg', + '㎎' => 'mg', + '㎏' => 'kg', + '㎐' => 'Hz', + '㎑' => 'kHz', + '㎒' => 'MHz', + '㎓' => 'GHz', + '㎔' => 'THz', + '㎕' => 'μl', + '㎖' => 'ml', + '㎗' => 'dl', + '㎘' => 'kl', + '㎙' => 'fm', + '㎚' => 'nm', + '㎛' => 'μm', + '㎜' => 'mm', + '㎝' => 'cm', + '㎞' => 'km', + '㎟' => 'mm2', + '㎠' => 'cm2', + '㎡' => 'm2', + '㎢' => 'km2', + '㎣' => 'mm3', + '㎤' => 'cm3', + '㎥' => 'm3', + '㎦' => 'km3', + '㎧' => 'm∕s', + '㎨' => 'm∕s2', + '㎩' => 'Pa', + '㎪' => 'kPa', + '㎫' => 'MPa', + '㎬' => 'GPa', + '㎭' => 'rad', + '㎮' => 'rad∕s', + '㎯' => 'rad∕s2', + '㎰' => 'ps', + '㎱' => 'ns', + '㎲' => 'μs', + '㎳' => 'ms', + '㎴' => 'pV', + '㎵' => 'nV', + '㎶' => 'μV', + '㎷' => 'mV', + '㎸' => 'kV', + '㎹' => 'MV', + '㎺' => 'pW', + '㎻' => 'nW', + '㎼' => 'μW', + '㎽' => 'mW', + '㎾' => 'kW', + '㎿' => 'MW', + '㏀' => 'kΩ', + '㏁' => 'MΩ', + '㏂' => 'a.m.', + '㏃' => 'Bq', + '㏄' => 'cc', + '㏅' => 'cd', + '㏆' => 'C∕kg', + '㏇' => 'Co.', + '㏈' => 'dB', + '㏉' => 'Gy', + '㏊' => 'ha', + '㏋' => 'HP', + '㏌' => 'in', + '㏍' => 'KK', + '㏎' => 'KM', + '㏏' => 'kt', + '㏐' => 'lm', + '㏑' => 'ln', + '㏒' => 'log', + '㏓' => 'lx', + '㏔' => 'mb', + '㏕' => 'mil', + '㏖' => 'mol', + '㏗' => 'PH', + '㏘' => 'p.m.', + '㏙' => 'PPM', + '㏚' => 'PR', + '㏛' => 'sr', + '㏜' => 'Sv', + '㏝' => 'Wb', + '㏞' => 'V∕m', + '㏟' => 'A∕m', + '㏠' => '1日', + '㏡' => '2日', + '㏢' => '3日', + '㏣' => '4日', + '㏤' => '5日', + '㏥' => '6日', + '㏦' => '7日', + '㏧' => '8日', + '㏨' => '9日', + '㏩' => '10日', + '㏪' => '11日', + '㏫' => '12日', + '㏬' => '13日', + '㏭' => '14日', + '㏮' => '15日', + '㏯' => '16日', + '㏰' => '17日', + '㏱' => '18日', + '㏲' => '19日', + '㏳' => '20日', + '㏴' => '21日', + '㏵' => '22日', + '㏶' => '23日', + '㏷' => '24日', + '㏸' => '25日', + '㏹' => '26日', + '㏺' => '27日', + '㏻' => '28日', + '㏼' => '29日', + '㏽' => '30日', + '㏾' => '31日', + '㏿' => 'gal', + 'ꚜ' => 'ъ', + 'ꚝ' => 'ь', + 'ꝰ' => 'ꝯ', + 'ꟸ' => 'Ħ', + 'ꟹ' => 'œ', + 'ꭜ' => 'ꜧ', + 'ꭝ' => 'ꬷ', + 'ꭞ' => 'ɫ', + 'ꭟ' => 'ꭒ', + 'ꭩ' => 'ʍ', + 'ff' => 'ff', + 'fi' => 'fi', + 'fl' => 'fl', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'ſt' => 'st', + 'st' => 'st', + 'ﬓ' => 'մն', + 'ﬔ' => 'մե', + 'ﬕ' => 'մի', + 'ﬖ' => 'վն', + 'ﬗ' => 'մխ', + 'ﬠ' => 'ע', + 'ﬡ' => 'א', + 'ﬢ' => 'ד', + 'ﬣ' => 'ה', + 'ﬤ' => 'כ', + 'ﬥ' => 'ל', + 'ﬦ' => 'ם', + 'ﬧ' => 'ר', + 'ﬨ' => 'ת', + '﬩' => '+', + 'ﭏ' => 'אל', + 'ﭐ' => 'ٱ', + 'ﭑ' => 'ٱ', + 'ﭒ' => 'ٻ', + 'ﭓ' => 'ٻ', + 'ﭔ' => 'ٻ', + 'ﭕ' => 'ٻ', + 'ﭖ' => 'پ', + 'ﭗ' => 'پ', + 'ﭘ' => 'پ', + 'ﭙ' => 'پ', + 'ﭚ' => 'ڀ', + 'ﭛ' => 'ڀ', + 'ﭜ' => 'ڀ', + 'ﭝ' => 'ڀ', + 'ﭞ' => 'ٺ', + 'ﭟ' => 'ٺ', + 'ﭠ' => 'ٺ', + 'ﭡ' => 'ٺ', + 'ﭢ' => 'ٿ', + 'ﭣ' => 'ٿ', + 'ﭤ' => 'ٿ', + 'ﭥ' => 'ٿ', + 'ﭦ' => 'ٹ', + 'ﭧ' => 'ٹ', + 'ﭨ' => 'ٹ', + 'ﭩ' => 'ٹ', + 'ﭪ' => 'ڤ', + 'ﭫ' => 'ڤ', + 'ﭬ' => 'ڤ', + 'ﭭ' => 'ڤ', + 'ﭮ' => 'ڦ', + 'ﭯ' => 'ڦ', + 'ﭰ' => 'ڦ', + 'ﭱ' => 'ڦ', + 'ﭲ' => 'ڄ', + 'ﭳ' => 'ڄ', + 'ﭴ' => 'ڄ', + 'ﭵ' => 'ڄ', + 'ﭶ' => 'ڃ', + 'ﭷ' => 'ڃ', + 'ﭸ' => 'ڃ', + 'ﭹ' => 'ڃ', + 'ﭺ' => 'چ', + 'ﭻ' => 'چ', + 'ﭼ' => 'چ', + 'ﭽ' => 'چ', + 'ﭾ' => 'ڇ', + 'ﭿ' => 'ڇ', + 'ﮀ' => 'ڇ', + 'ﮁ' => 'ڇ', + 'ﮂ' => 'ڍ', + 'ﮃ' => 'ڍ', + 'ﮄ' => 'ڌ', + 'ﮅ' => 'ڌ', + 'ﮆ' => 'ڎ', + 'ﮇ' => 'ڎ', + 'ﮈ' => 'ڈ', + 'ﮉ' => 'ڈ', + 'ﮊ' => 'ژ', + 'ﮋ' => 'ژ', + 'ﮌ' => 'ڑ', + 'ﮍ' => 'ڑ', + 'ﮎ' => 'ک', + 'ﮏ' => 'ک', + 'ﮐ' => 'ک', + 'ﮑ' => 'ک', + 'ﮒ' => 'گ', + 'ﮓ' => 'گ', + 'ﮔ' => 'گ', + 'ﮕ' => 'گ', + 'ﮖ' => 'ڳ', + 'ﮗ' => 'ڳ', + 'ﮘ' => 'ڳ', + 'ﮙ' => 'ڳ', + 'ﮚ' => 'ڱ', + 'ﮛ' => 'ڱ', + 'ﮜ' => 'ڱ', + 'ﮝ' => 'ڱ', + 'ﮞ' => 'ں', + 'ﮟ' => 'ں', + 'ﮠ' => 'ڻ', + 'ﮡ' => 'ڻ', + 'ﮢ' => 'ڻ', + 'ﮣ' => 'ڻ', + 'ﮤ' => 'ۀ', + 'ﮥ' => 'ۀ', + 'ﮦ' => 'ہ', + 'ﮧ' => 'ہ', + 'ﮨ' => 'ہ', + 'ﮩ' => 'ہ', + 'ﮪ' => 'ھ', + 'ﮫ' => 'ھ', + 'ﮬ' => 'ھ', + 'ﮭ' => 'ھ', + 'ﮮ' => 'ے', + 'ﮯ' => 'ے', + 'ﮰ' => 'ۓ', + 'ﮱ' => 'ۓ', + 'ﯓ' => 'ڭ', + 'ﯔ' => 'ڭ', + 'ﯕ' => 'ڭ', + 'ﯖ' => 'ڭ', + 'ﯗ' => 'ۇ', + 'ﯘ' => 'ۇ', + 'ﯙ' => 'ۆ', + 'ﯚ' => 'ۆ', + 'ﯛ' => 'ۈ', + 'ﯜ' => 'ۈ', + 'ﯝ' => 'ۇٴ', + 'ﯞ' => 'ۋ', + 'ﯟ' => 'ۋ', + 'ﯠ' => 'ۅ', + 'ﯡ' => 'ۅ', + 'ﯢ' => 'ۉ', + 'ﯣ' => 'ۉ', + 'ﯤ' => 'ې', + 'ﯥ' => 'ې', + 'ﯦ' => 'ې', + 'ﯧ' => 'ې', + 'ﯨ' => 'ى', + 'ﯩ' => 'ى', + 'ﯪ' => 'ئا', + 'ﯫ' => 'ئا', + 'ﯬ' => 'ئە', + 'ﯭ' => 'ئە', + 'ﯮ' => 'ئو', + 'ﯯ' => 'ئو', + 'ﯰ' => 'ئۇ', + 'ﯱ' => 'ئۇ', + 'ﯲ' => 'ئۆ', + 'ﯳ' => 'ئۆ', + 'ﯴ' => 'ئۈ', + 'ﯵ' => 'ئۈ', + 'ﯶ' => 'ئې', + 'ﯷ' => 'ئې', + 'ﯸ' => 'ئې', + 'ﯹ' => 'ئى', + 'ﯺ' => 'ئى', + 'ﯻ' => 'ئى', + 'ﯼ' => 'ی', + 'ﯽ' => 'ی', + 'ﯾ' => 'ی', + 'ﯿ' => 'ی', + 'ﰀ' => 'ئج', + 'ﰁ' => 'ئح', + 'ﰂ' => 'ئم', + 'ﰃ' => 'ئى', + 'ﰄ' => 'ئي', + 'ﰅ' => 'بج', + 'ﰆ' => 'بح', + 'ﰇ' => 'بخ', + 'ﰈ' => 'بم', + 'ﰉ' => 'بى', + 'ﰊ' => 'بي', + 'ﰋ' => 'تج', + 'ﰌ' => 'تح', + 'ﰍ' => 'تخ', + 'ﰎ' => 'تم', + 'ﰏ' => 'تى', + 'ﰐ' => 'تي', + 'ﰑ' => 'ثج', + 'ﰒ' => 'ثم', + 'ﰓ' => 'ثى', + 'ﰔ' => 'ثي', + 'ﰕ' => 'جح', + 'ﰖ' => 'جم', + 'ﰗ' => 'حج', + 'ﰘ' => 'حم', + 'ﰙ' => 'خج', + 'ﰚ' => 'خح', + 'ﰛ' => 'خم', + 'ﰜ' => 'سج', + 'ﰝ' => 'سح', + 'ﰞ' => 'سخ', + 'ﰟ' => 'سم', + 'ﰠ' => 'صح', + 'ﰡ' => 'صم', + 'ﰢ' => 'ضج', + 'ﰣ' => 'ضح', + 'ﰤ' => 'ضخ', + 'ﰥ' => 'ضم', + 'ﰦ' => 'طح', + 'ﰧ' => 'طم', + 'ﰨ' => 'ظم', + 'ﰩ' => 'عج', + 'ﰪ' => 'عم', + 'ﰫ' => 'غج', + 'ﰬ' => 'غم', + 'ﰭ' => 'فج', + 'ﰮ' => 'فح', + 'ﰯ' => 'فخ', + 'ﰰ' => 'فم', + 'ﰱ' => 'فى', + 'ﰲ' => 'في', + 'ﰳ' => 'قح', + 'ﰴ' => 'قم', + 'ﰵ' => 'قى', + 'ﰶ' => 'قي', + 'ﰷ' => 'كا', + 'ﰸ' => 'كج', + 'ﰹ' => 'كح', + 'ﰺ' => 'كخ', + 'ﰻ' => 'كل', + 'ﰼ' => 'كم', + 'ﰽ' => 'كى', + 'ﰾ' => 'كي', + 'ﰿ' => 'لج', + 'ﱀ' => 'لح', + 'ﱁ' => 'لخ', + 'ﱂ' => 'لم', + 'ﱃ' => 'لى', + 'ﱄ' => 'لي', + 'ﱅ' => 'مج', + 'ﱆ' => 'مح', + 'ﱇ' => 'مخ', + 'ﱈ' => 'مم', + 'ﱉ' => 'مى', + 'ﱊ' => 'مي', + 'ﱋ' => 'نج', + 'ﱌ' => 'نح', + 'ﱍ' => 'نخ', + 'ﱎ' => 'نم', + 'ﱏ' => 'نى', + 'ﱐ' => 'ني', + 'ﱑ' => 'هج', + 'ﱒ' => 'هم', + 'ﱓ' => 'هى', + 'ﱔ' => 'هي', + 'ﱕ' => 'يج', + 'ﱖ' => 'يح', + 'ﱗ' => 'يخ', + 'ﱘ' => 'يم', + 'ﱙ' => 'يى', + 'ﱚ' => 'يي', + 'ﱛ' => 'ذٰ', + 'ﱜ' => 'رٰ', + 'ﱝ' => 'ىٰ', + 'ﱞ' => ' ٌّ', + 'ﱟ' => ' ٍّ', + 'ﱠ' => ' َّ', + 'ﱡ' => ' ُّ', + 'ﱢ' => ' ِّ', + 'ﱣ' => ' ّٰ', + 'ﱤ' => 'ئر', + 'ﱥ' => 'ئز', + 'ﱦ' => 'ئم', + 'ﱧ' => 'ئن', + 'ﱨ' => 'ئى', + 'ﱩ' => 'ئي', + 'ﱪ' => 'بر', + 'ﱫ' => 'بز', + 'ﱬ' => 'بم', + 'ﱭ' => 'بن', + 'ﱮ' => 'بى', + 'ﱯ' => 'بي', + 'ﱰ' => 'تر', + 'ﱱ' => 'تز', + 'ﱲ' => 'تم', + 'ﱳ' => 'تن', + 'ﱴ' => 'تى', + 'ﱵ' => 'تي', + 'ﱶ' => 'ثر', + 'ﱷ' => 'ثز', + 'ﱸ' => 'ثم', + 'ﱹ' => 'ثن', + 'ﱺ' => 'ثى', + 'ﱻ' => 'ثي', + 'ﱼ' => 'فى', + 'ﱽ' => 'في', + 'ﱾ' => 'قى', + 'ﱿ' => 'قي', + 'ﲀ' => 'كا', + 'ﲁ' => 'كل', + 'ﲂ' => 'كم', + 'ﲃ' => 'كى', + 'ﲄ' => 'كي', + 'ﲅ' => 'لم', + 'ﲆ' => 'لى', + 'ﲇ' => 'لي', + 'ﲈ' => 'ما', + 'ﲉ' => 'مم', + 'ﲊ' => 'نر', + 'ﲋ' => 'نز', + 'ﲌ' => 'نم', + 'ﲍ' => 'نن', + 'ﲎ' => 'نى', + 'ﲏ' => 'ني', + 'ﲐ' => 'ىٰ', + 'ﲑ' => 'ير', + 'ﲒ' => 'يز', + 'ﲓ' => 'يم', + 'ﲔ' => 'ين', + 'ﲕ' => 'يى', + 'ﲖ' => 'يي', + 'ﲗ' => 'ئج', + 'ﲘ' => 'ئح', + 'ﲙ' => 'ئخ', + 'ﲚ' => 'ئم', + 'ﲛ' => 'ئه', + 'ﲜ' => 'بج', + 'ﲝ' => 'بح', + 'ﲞ' => 'بخ', + 'ﲟ' => 'بم', + 'ﲠ' => 'به', + 'ﲡ' => 'تج', + 'ﲢ' => 'تح', + 'ﲣ' => 'تخ', + 'ﲤ' => 'تم', + 'ﲥ' => 'ته', + 'ﲦ' => 'ثم', + 'ﲧ' => 'جح', + 'ﲨ' => 'جم', + 'ﲩ' => 'حج', + 'ﲪ' => 'حم', + 'ﲫ' => 'خج', + 'ﲬ' => 'خم', + 'ﲭ' => 'سج', + 'ﲮ' => 'سح', + 'ﲯ' => 'سخ', + 'ﲰ' => 'سم', + 'ﲱ' => 'صح', + 'ﲲ' => 'صخ', + 'ﲳ' => 'صم', + 'ﲴ' => 'ضج', + 'ﲵ' => 'ضح', + 'ﲶ' => 'ضخ', + 'ﲷ' => 'ضم', + 'ﲸ' => 'طح', + 'ﲹ' => 'ظم', + 'ﲺ' => 'عج', + 'ﲻ' => 'عم', + 'ﲼ' => 'غج', + 'ﲽ' => 'غم', + 'ﲾ' => 'فج', + 'ﲿ' => 'فح', + 'ﳀ' => 'فخ', + 'ﳁ' => 'فم', + 'ﳂ' => 'قح', + 'ﳃ' => 'قم', + 'ﳄ' => 'كج', + 'ﳅ' => 'كح', + 'ﳆ' => 'كخ', + 'ﳇ' => 'كل', + 'ﳈ' => 'كم', + 'ﳉ' => 'لج', + 'ﳊ' => 'لح', + 'ﳋ' => 'لخ', + 'ﳌ' => 'لم', + 'ﳍ' => 'له', + 'ﳎ' => 'مج', + 'ﳏ' => 'مح', + 'ﳐ' => 'مخ', + 'ﳑ' => 'مم', + 'ﳒ' => 'نج', + 'ﳓ' => 'نح', + 'ﳔ' => 'نخ', + 'ﳕ' => 'نم', + 'ﳖ' => 'نه', + 'ﳗ' => 'هج', + 'ﳘ' => 'هم', + 'ﳙ' => 'هٰ', + 'ﳚ' => 'يج', + 'ﳛ' => 'يح', + 'ﳜ' => 'يخ', + 'ﳝ' => 'يم', + 'ﳞ' => 'يه', + 'ﳟ' => 'ئم', + 'ﳠ' => 'ئه', + 'ﳡ' => 'بم', + 'ﳢ' => 'به', + 'ﳣ' => 'تم', + 'ﳤ' => 'ته', + 'ﳥ' => 'ثم', + 'ﳦ' => 'ثه', + 'ﳧ' => 'سم', + 'ﳨ' => 'سه', + 'ﳩ' => 'شم', + 'ﳪ' => 'شه', + 'ﳫ' => 'كل', + 'ﳬ' => 'كم', + 'ﳭ' => 'لم', + 'ﳮ' => 'نم', + 'ﳯ' => 'نه', + 'ﳰ' => 'يم', + 'ﳱ' => 'يه', + 'ﳲ' => 'ـَّ', + 'ﳳ' => 'ـُّ', + 'ﳴ' => 'ـِّ', + 'ﳵ' => 'طى', + 'ﳶ' => 'طي', + 'ﳷ' => 'عى', + 'ﳸ' => 'عي', + 'ﳹ' => 'غى', + 'ﳺ' => 'غي', + 'ﳻ' => 'سى', + 'ﳼ' => 'سي', + 'ﳽ' => 'شى', + 'ﳾ' => 'شي', + 'ﳿ' => 'حى', + 'ﴀ' => 'حي', + 'ﴁ' => 'جى', + 'ﴂ' => 'جي', + 'ﴃ' => 'خى', + 'ﴄ' => 'خي', + 'ﴅ' => 'صى', + 'ﴆ' => 'صي', + 'ﴇ' => 'ضى', + 'ﴈ' => 'ضي', + 'ﴉ' => 'شج', + 'ﴊ' => 'شح', + 'ﴋ' => 'شخ', + 'ﴌ' => 'شم', + 'ﴍ' => 'شر', + 'ﴎ' => 'سر', + 'ﴏ' => 'صر', + 'ﴐ' => 'ضر', + 'ﴑ' => 'طى', + 'ﴒ' => 'طي', + 'ﴓ' => 'عى', + 'ﴔ' => 'عي', + 'ﴕ' => 'غى', + 'ﴖ' => 'غي', + 'ﴗ' => 'سى', + 'ﴘ' => 'سي', + 'ﴙ' => 'شى', + 'ﴚ' => 'شي', + 'ﴛ' => 'حى', + 'ﴜ' => 'حي', + 'ﴝ' => 'جى', + 'ﴞ' => 'جي', + 'ﴟ' => 'خى', + 'ﴠ' => 'خي', + 'ﴡ' => 'صى', + 'ﴢ' => 'صي', + 'ﴣ' => 'ضى', + 'ﴤ' => 'ضي', + 'ﴥ' => 'شج', + 'ﴦ' => 'شح', + 'ﴧ' => 'شخ', + 'ﴨ' => 'شم', + 'ﴩ' => 'شر', + 'ﴪ' => 'سر', + 'ﴫ' => 'صر', + 'ﴬ' => 'ضر', + 'ﴭ' => 'شج', + 'ﴮ' => 'شح', + 'ﴯ' => 'شخ', + 'ﴰ' => 'شم', + 'ﴱ' => 'سه', + 'ﴲ' => 'شه', + 'ﴳ' => 'طم', + 'ﴴ' => 'سج', + 'ﴵ' => 'سح', + 'ﴶ' => 'سخ', + 'ﴷ' => 'شج', + 'ﴸ' => 'شح', + 'ﴹ' => 'شخ', + 'ﴺ' => 'طم', + 'ﴻ' => 'ظم', + 'ﴼ' => 'اً', + 'ﴽ' => 'اً', + 'ﵐ' => 'تجم', + 'ﵑ' => 'تحج', + 'ﵒ' => 'تحج', + 'ﵓ' => 'تحم', + 'ﵔ' => 'تخم', + 'ﵕ' => 'تمج', + 'ﵖ' => 'تمح', + 'ﵗ' => 'تمخ', + 'ﵘ' => 'جمح', + 'ﵙ' => 'جمح', + 'ﵚ' => 'حمي', + 'ﵛ' => 'حمى', + 'ﵜ' => 'سحج', + 'ﵝ' => 'سجح', + 'ﵞ' => 'سجى', + 'ﵟ' => 'سمح', + 'ﵠ' => 'سمح', + 'ﵡ' => 'سمج', + 'ﵢ' => 'سمم', + 'ﵣ' => 'سمم', + 'ﵤ' => 'صحح', + 'ﵥ' => 'صحح', + 'ﵦ' => 'صمم', + 'ﵧ' => 'شحم', + 'ﵨ' => 'شحم', + 'ﵩ' => 'شجي', + 'ﵪ' => 'شمخ', + 'ﵫ' => 'شمخ', + 'ﵬ' => 'شمم', + 'ﵭ' => 'شمم', + 'ﵮ' => 'ضحى', + 'ﵯ' => 'ضخم', + 'ﵰ' => 'ضخم', + 'ﵱ' => 'طمح', + 'ﵲ' => 'طمح', + 'ﵳ' => 'طمم', + 'ﵴ' => 'طمي', + 'ﵵ' => 'عجم', + 'ﵶ' => 'عمم', + 'ﵷ' => 'عمم', + 'ﵸ' => 'عمى', + 'ﵹ' => 'غمم', + 'ﵺ' => 'غمي', + 'ﵻ' => 'غمى', + 'ﵼ' => 'فخم', + 'ﵽ' => 'فخم', + 'ﵾ' => 'قمح', + 'ﵿ' => 'قمم', + 'ﶀ' => 'لحم', + 'ﶁ' => 'لحي', + 'ﶂ' => 'لحى', + 'ﶃ' => 'لجج', + 'ﶄ' => 'لجج', + 'ﶅ' => 'لخم', + 'ﶆ' => 'لخم', + 'ﶇ' => 'لمح', + 'ﶈ' => 'لمح', + 'ﶉ' => 'محج', + 'ﶊ' => 'محم', + 'ﶋ' => 'محي', + 'ﶌ' => 'مجح', + 'ﶍ' => 'مجم', + 'ﶎ' => 'مخج', + 'ﶏ' => 'مخم', + 'ﶒ' => 'مجخ', + 'ﶓ' => 'همج', + 'ﶔ' => 'همم', + 'ﶕ' => 'نحم', + 'ﶖ' => 'نحى', + 'ﶗ' => 'نجم', + 'ﶘ' => 'نجم', + 'ﶙ' => 'نجى', + 'ﶚ' => 'نمي', + 'ﶛ' => 'نمى', + 'ﶜ' => 'يمم', + 'ﶝ' => 'يمم', + 'ﶞ' => 'بخي', + 'ﶟ' => 'تجي', + 'ﶠ' => 'تجى', + 'ﶡ' => 'تخي', + 'ﶢ' => 'تخى', + 'ﶣ' => 'تمي', + 'ﶤ' => 'تمى', + 'ﶥ' => 'جمي', + 'ﶦ' => 'جحى', + 'ﶧ' => 'جمى', + 'ﶨ' => 'سخى', + 'ﶩ' => 'صحي', + 'ﶪ' => 'شحي', + 'ﶫ' => 'ضحي', + 'ﶬ' => 'لجي', + 'ﶭ' => 'لمي', + 'ﶮ' => 'يحي', + 'ﶯ' => 'يجي', + 'ﶰ' => 'يمي', + 'ﶱ' => 'ممي', + 'ﶲ' => 'قمي', + 'ﶳ' => 'نحي', + 'ﶴ' => 'قمح', + 'ﶵ' => 'لحم', + 'ﶶ' => 'عمي', + 'ﶷ' => 'كمي', + 'ﶸ' => 'نجح', + 'ﶹ' => 'مخي', + 'ﶺ' => 'لجم', + 'ﶻ' => 'كمم', + 'ﶼ' => 'لجم', + 'ﶽ' => 'نجح', + 'ﶾ' => 'جحي', + 'ﶿ' => 'حجي', + 'ﷀ' => 'مجي', + 'ﷁ' => 'فمي', + 'ﷂ' => 'بحي', + 'ﷃ' => 'كمم', + 'ﷄ' => 'عجم', + 'ﷅ' => 'صمم', + 'ﷆ' => 'سخي', + 'ﷇ' => 'نجي', + 'ﷰ' => 'صلے', + 'ﷱ' => 'قلے', + 'ﷲ' => 'الله', + 'ﷳ' => 'اكبر', + 'ﷴ' => 'محمد', + 'ﷵ' => 'صلعم', + 'ﷶ' => 'رسول', + 'ﷷ' => 'عليه', + 'ﷸ' => 'وسلم', + 'ﷹ' => 'صلى', + 'ﷺ' => 'صلى الله عليه وسلم', + 'ﷻ' => 'جل جلاله', + '﷼' => 'ریال', + '︐' => ',', + '︑' => '、', + '︒' => '。', + '︓' => ':', + '︔' => ';', + '︕' => '!', + '︖' => '?', + '︗' => '〖', + '︘' => '〗', + '︙' => '...', + '︰' => '..', + '︱' => '—', + '︲' => '–', + '︳' => '_', + '︴' => '_', + '︵' => '(', + '︶' => ')', + '︷' => '{', + '︸' => '}', + '︹' => '〔', + '︺' => '〕', + '︻' => '【', + '︼' => '】', + '︽' => '《', + '︾' => '》', + '︿' => '〈', + '﹀' => '〉', + '﹁' => '「', + '﹂' => '」', + '﹃' => '『', + '﹄' => '』', + '﹇' => '[', + '﹈' => ']', + '﹉' => ' ̅', + '﹊' => ' ̅', + '﹋' => ' ̅', + '﹌' => ' ̅', + '﹍' => '_', + '﹎' => '_', + '﹏' => '_', + '﹐' => ',', + '﹑' => '、', + '﹒' => '.', + '﹔' => ';', + '﹕' => ':', + '﹖' => '?', + '﹗' => '!', + '﹘' => '—', + '﹙' => '(', + '﹚' => ')', + '﹛' => '{', + '﹜' => '}', + '﹝' => '〔', + '﹞' => '〕', + '﹟' => '#', + '﹠' => '&', + '﹡' => '*', + '﹢' => '+', + '﹣' => '-', + '﹤' => '<', + '﹥' => '>', + '﹦' => '=', + '﹨' => '\\', + '﹩' => '$', + '﹪' => '%', + '﹫' => '@', + 'ﹰ' => ' ً', + 'ﹱ' => 'ـً', + 'ﹲ' => ' ٌ', + 'ﹴ' => ' ٍ', + 'ﹶ' => ' َ', + 'ﹷ' => 'ـَ', + 'ﹸ' => ' ُ', + 'ﹹ' => 'ـُ', + 'ﹺ' => ' ِ', + 'ﹻ' => 'ـِ', + 'ﹼ' => ' ّ', + 'ﹽ' => 'ـّ', + 'ﹾ' => ' ْ', + 'ﹿ' => 'ـْ', + 'ﺀ' => 'ء', + 'ﺁ' => 'آ', + 'ﺂ' => 'آ', + 'ﺃ' => 'أ', + 'ﺄ' => 'أ', + 'ﺅ' => 'ؤ', + 'ﺆ' => 'ؤ', + 'ﺇ' => 'إ', + 'ﺈ' => 'إ', + 'ﺉ' => 'ئ', + 'ﺊ' => 'ئ', + 'ﺋ' => 'ئ', + 'ﺌ' => 'ئ', + 'ﺍ' => 'ا', + 'ﺎ' => 'ا', + 'ﺏ' => 'ب', + 'ﺐ' => 'ب', + 'ﺑ' => 'ب', + 'ﺒ' => 'ب', + 'ﺓ' => 'ة', + 'ﺔ' => 'ة', + 'ﺕ' => 'ت', + 'ﺖ' => 'ت', + 'ﺗ' => 'ت', + 'ﺘ' => 'ت', + 'ﺙ' => 'ث', + 'ﺚ' => 'ث', + 'ﺛ' => 'ث', + 'ﺜ' => 'ث', + 'ﺝ' => 'ج', + 'ﺞ' => 'ج', + 'ﺟ' => 'ج', + 'ﺠ' => 'ج', + 'ﺡ' => 'ح', + 'ﺢ' => 'ح', + 'ﺣ' => 'ح', + 'ﺤ' => 'ح', + 'ﺥ' => 'خ', + 'ﺦ' => 'خ', + 'ﺧ' => 'خ', + 'ﺨ' => 'خ', + 'ﺩ' => 'د', + 'ﺪ' => 'د', + 'ﺫ' => 'ذ', + 'ﺬ' => 'ذ', + 'ﺭ' => 'ر', + 'ﺮ' => 'ر', + 'ﺯ' => 'ز', + 'ﺰ' => 'ز', + 'ﺱ' => 'س', + 'ﺲ' => 'س', + 'ﺳ' => 'س', + 'ﺴ' => 'س', + 'ﺵ' => 'ش', + 'ﺶ' => 'ش', + 'ﺷ' => 'ش', + 'ﺸ' => 'ش', + 'ﺹ' => 'ص', + 'ﺺ' => 'ص', + 'ﺻ' => 'ص', + 'ﺼ' => 'ص', + 'ﺽ' => 'ض', + 'ﺾ' => 'ض', + 'ﺿ' => 'ض', + 'ﻀ' => 'ض', + 'ﻁ' => 'ط', + 'ﻂ' => 'ط', + 'ﻃ' => 'ط', + 'ﻄ' => 'ط', + 'ﻅ' => 'ظ', + 'ﻆ' => 'ظ', + 'ﻇ' => 'ظ', + 'ﻈ' => 'ظ', + 'ﻉ' => 'ع', + 'ﻊ' => 'ع', + 'ﻋ' => 'ع', + 'ﻌ' => 'ع', + 'ﻍ' => 'غ', + 'ﻎ' => 'غ', + 'ﻏ' => 'غ', + 'ﻐ' => 'غ', + 'ﻑ' => 'ف', + 'ﻒ' => 'ف', + 'ﻓ' => 'ف', + 'ﻔ' => 'ف', + 'ﻕ' => 'ق', + 'ﻖ' => 'ق', + 'ﻗ' => 'ق', + 'ﻘ' => 'ق', + 'ﻙ' => 'ك', + 'ﻚ' => 'ك', + 'ﻛ' => 'ك', + 'ﻜ' => 'ك', + 'ﻝ' => 'ل', + 'ﻞ' => 'ل', + 'ﻟ' => 'ل', + 'ﻠ' => 'ل', + 'ﻡ' => 'م', + 'ﻢ' => 'م', + 'ﻣ' => 'م', + 'ﻤ' => 'م', + 'ﻥ' => 'ن', + 'ﻦ' => 'ن', + 'ﻧ' => 'ن', + 'ﻨ' => 'ن', + 'ﻩ' => 'ه', + 'ﻪ' => 'ه', + 'ﻫ' => 'ه', + 'ﻬ' => 'ه', + 'ﻭ' => 'و', + 'ﻮ' => 'و', + 'ﻯ' => 'ى', + 'ﻰ' => 'ى', + 'ﻱ' => 'ي', + 'ﻲ' => 'ي', + 'ﻳ' => 'ي', + 'ﻴ' => 'ي', + 'ﻵ' => 'لآ', + 'ﻶ' => 'لآ', + 'ﻷ' => 'لأ', + 'ﻸ' => 'لأ', + 'ﻹ' => 'لإ', + 'ﻺ' => 'لإ', + 'ﻻ' => 'لا', + 'ﻼ' => 'لا', + '!' => '!', + '"' => '"', + '#' => '#', + '$' => '$', + '%' => '%', + '&' => '&', + ''' => '\'', + '(' => '(', + ')' => ')', + '*' => '*', + '+' => '+', + ',' => ',', + '-' => '-', + '.' => '.', + '/' => '/', + '0' => '0', + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + '7' => '7', + '8' => '8', + '9' => '9', + ':' => ':', + ';' => ';', + '<' => '<', + '=' => '=', + '>' => '>', + '?' => '?', + '@' => '@', + 'A' => 'A', + 'B' => 'B', + 'C' => 'C', + 'D' => 'D', + 'E' => 'E', + 'F' => 'F', + 'G' => 'G', + 'H' => 'H', + 'I' => 'I', + 'J' => 'J', + 'K' => 'K', + 'L' => 'L', + 'M' => 'M', + 'N' => 'N', + 'O' => 'O', + 'P' => 'P', + 'Q' => 'Q', + 'R' => 'R', + 'S' => 'S', + 'T' => 'T', + 'U' => 'U', + 'V' => 'V', + 'W' => 'W', + 'X' => 'X', + 'Y' => 'Y', + 'Z' => 'Z', + '[' => '[', + '\' => '\\', + ']' => ']', + '^' => '^', + '_' => '_', + '`' => '`', + 'a' => 'a', + 'b' => 'b', + 'c' => 'c', + 'd' => 'd', + 'e' => 'e', + 'f' => 'f', + 'g' => 'g', + 'h' => 'h', + 'i' => 'i', + 'j' => 'j', + 'k' => 'k', + 'l' => 'l', + 'm' => 'm', + 'n' => 'n', + 'o' => 'o', + 'p' => 'p', + 'q' => 'q', + 'r' => 'r', + 's' => 's', + 't' => 't', + 'u' => 'u', + 'v' => 'v', + 'w' => 'w', + 'x' => 'x', + 'y' => 'y', + 'z' => 'z', + '{' => '{', + '|' => '|', + '}' => '}', + '~' => '~', + '⦅' => '⦅', + '⦆' => '⦆', + '。' => '。', + '「' => '「', + '」' => '」', + '、' => '、', + '・' => '・', + 'ヲ' => 'ヲ', + 'ァ' => 'ァ', + 'ィ' => 'ィ', + 'ゥ' => 'ゥ', + 'ェ' => 'ェ', + 'ォ' => 'ォ', + 'ャ' => 'ャ', + 'ュ' => 'ュ', + 'ョ' => 'ョ', + 'ッ' => 'ッ', + 'ー' => 'ー', + 'ア' => 'ア', + 'イ' => 'イ', + 'ウ' => 'ウ', + 'エ' => 'エ', + 'オ' => 'オ', + 'カ' => 'カ', + 'キ' => 'キ', + 'ク' => 'ク', + 'ケ' => 'ケ', + 'コ' => 'コ', + 'サ' => 'サ', + 'シ' => 'シ', + 'ス' => 'ス', + 'セ' => 'セ', + 'ソ' => 'ソ', + 'タ' => 'タ', + 'チ' => 'チ', + 'ツ' => 'ツ', + 'テ' => 'テ', + 'ト' => 'ト', + 'ナ' => 'ナ', + 'ニ' => 'ニ', + 'ヌ' => 'ヌ', + 'ネ' => 'ネ', + 'ノ' => 'ノ', + 'ハ' => 'ハ', + 'ヒ' => 'ヒ', + 'フ' => 'フ', + 'ヘ' => 'ヘ', + 'ホ' => 'ホ', + 'マ' => 'マ', + 'ミ' => 'ミ', + 'ム' => 'ム', + 'メ' => 'メ', + 'モ' => 'モ', + 'ヤ' => 'ヤ', + 'ユ' => 'ユ', + 'ヨ' => 'ヨ', + 'ラ' => 'ラ', + 'リ' => 'リ', + 'ル' => 'ル', + 'レ' => 'レ', + 'ロ' => 'ロ', + 'ワ' => 'ワ', + 'ン' => 'ン', + '゙' => '゙', + '゚' => '゚', + 'ᅠ' => 'ᅠ', + 'ᄀ' => 'ᄀ', + 'ᄁ' => 'ᄁ', + 'ᆪ' => 'ᆪ', + 'ᄂ' => 'ᄂ', + 'ᆬ' => 'ᆬ', + 'ᆭ' => 'ᆭ', + 'ᄃ' => 'ᄃ', + 'ᄄ' => 'ᄄ', + 'ᄅ' => 'ᄅ', + 'ᆰ' => 'ᆰ', + 'ᆱ' => 'ᆱ', + 'ᆲ' => 'ᆲ', + 'ᆳ' => 'ᆳ', + 'ᆴ' => 'ᆴ', + 'ᆵ' => 'ᆵ', + 'ᄚ' => 'ᄚ', + 'ᄆ' => 'ᄆ', + 'ᄇ' => 'ᄇ', + 'ᄈ' => 'ᄈ', + 'ᄡ' => 'ᄡ', + 'ᄉ' => 'ᄉ', + 'ᄊ' => 'ᄊ', + 'ᄋ' => 'ᄋ', + 'ᄌ' => 'ᄌ', + 'ᄍ' => 'ᄍ', + 'ᄎ' => 'ᄎ', + 'ᄏ' => 'ᄏ', + 'ᄐ' => 'ᄐ', + 'ᄑ' => 'ᄑ', + 'ᄒ' => 'ᄒ', + 'ᅡ' => 'ᅡ', + 'ᅢ' => 'ᅢ', + 'ᅣ' => 'ᅣ', + 'ᅤ' => 'ᅤ', + 'ᅥ' => 'ᅥ', + 'ᅦ' => 'ᅦ', + 'ᅧ' => 'ᅧ', + 'ᅨ' => 'ᅨ', + 'ᅩ' => 'ᅩ', + 'ᅪ' => 'ᅪ', + 'ᅫ' => 'ᅫ', + 'ᅬ' => 'ᅬ', + 'ᅭ' => 'ᅭ', + 'ᅮ' => 'ᅮ', + 'ᅯ' => 'ᅯ', + 'ᅰ' => 'ᅰ', + 'ᅱ' => 'ᅱ', + 'ᅲ' => 'ᅲ', + 'ᅳ' => 'ᅳ', + 'ᅴ' => 'ᅴ', + 'ᅵ' => 'ᅵ', + '¢' => '¢', + '£' => '£', + '¬' => '¬', + ' ̄' => ' ̄', + '¦' => '¦', + '¥' => '¥', + '₩' => '₩', + '│' => '│', + '←' => '←', + '↑' => '↑', + '→' => '→', + '↓' => '↓', + '■' => '■', + '○' => '○', + '𝐀' => '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', + '𝐚' => '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', + '𝐴' => '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', + '𝑎' => 'a', + '𝑏' => 'b', + '𝑐' => 'c', + '𝑑' => 'd', + '𝑒' => 'e', + '𝑓' => 'f', + '𝑔' => 'g', + '𝑖' => 'i', + '𝑗' => 'j', + '𝑘' => 'k', + '𝑙' => 'l', + '𝑚' => 'm', + '𝑛' => 'n', + '𝑜' => 'o', + '𝑝' => 'p', + '𝑞' => 'q', + '𝑟' => 'r', + '𝑠' => 's', + '𝑡' => 't', + '𝑢' => 'u', + '𝑣' => 'v', + '𝑤' => 'w', + '𝑥' => 'x', + '𝑦' => 'y', + '𝑧' => 'z', + '𝑨' => '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', + '𝒂' => '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', + '𝒜' => 'A', + '𝒞' => 'C', + '𝒟' => 'D', + '𝒢' => 'G', + '𝒥' => 'J', + '𝒦' => 'K', + '𝒩' => 'N', + '𝒪' => 'O', + '𝒫' => 'P', + '𝒬' => 'Q', + '𝒮' => 'S', + '𝒯' => 'T', + '𝒰' => 'U', + '𝒱' => 'V', + '𝒲' => 'W', + '𝒳' => 'X', + '𝒴' => 'Y', + '𝒵' => 'Z', + '𝒶' => 'a', + '𝒷' => 'b', + '𝒸' => 'c', + '𝒹' => 'd', + '𝒻' => 'f', + '𝒽' => 'h', + '𝒾' => 'i', + '𝒿' => 'j', + '𝓀' => 'k', + '𝓁' => 'l', + '𝓂' => 'm', + '𝓃' => 'n', + '𝓅' => 'p', + '𝓆' => 'q', + '𝓇' => 'r', + '𝓈' => 's', + '𝓉' => 't', + '𝓊' => 'u', + '𝓋' => 'v', + '𝓌' => 'w', + '𝓍' => 'x', + '𝓎' => 'y', + '𝓏' => 'z', + '𝓐' => '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', + '𝓪' => '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', + '𝔄' => 'A', + '𝔅' => 'B', + '𝔇' => 'D', + '𝔈' => 'E', + '𝔉' => 'F', + '𝔊' => 'G', + '𝔍' => 'J', + '𝔎' => 'K', + '𝔏' => 'L', + '𝔐' => 'M', + '𝔑' => 'N', + '𝔒' => 'O', + '𝔓' => 'P', + '𝔔' => 'Q', + '𝔖' => 'S', + '𝔗' => 'T', + '𝔘' => 'U', + '𝔙' => 'V', + '𝔚' => 'W', + '𝔛' => 'X', + '𝔜' => 'Y', + '𝔞' => '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', + '𝔸' => 'A', + '𝔹' => 'B', + '𝔻' => 'D', + '𝔼' => 'E', + '𝔽' => 'F', + '𝔾' => 'G', + '𝕀' => 'I', + '𝕁' => 'J', + '𝕂' => 'K', + '𝕃' => 'L', + '𝕄' => 'M', + '𝕆' => 'O', + '𝕊' => 'S', + '𝕋' => 'T', + '𝕌' => 'U', + '𝕍' => 'V', + '𝕎' => 'W', + '𝕏' => 'X', + '𝕐' => 'Y', + '𝕒' => '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', + '𝕬' => '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', + '𝖆' => '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', + '𝖠' => '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', + '𝖺' => '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', + '𝗔' => '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', + '𝗮' => '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', + '𝘈' => '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', + '𝘢' => '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', + '𝘼' => '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', + '𝙖' => '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', + '𝙰' => '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', + '𝚊' => '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', + '𝟘' => '0', + '𝟙' => '1', + '𝟚' => '2', + '𝟛' => '3', + '𝟜' => '4', + '𝟝' => '5', + '𝟞' => '6', + '𝟟' => '7', + '𝟠' => '8', + '𝟡' => '9', + '𝟢' => '0', + '𝟣' => '1', + '𝟤' => '2', + '𝟥' => '3', + '𝟦' => '4', + '𝟧' => '5', + '𝟨' => '6', + '𝟩' => '7', + '𝟪' => '8', + '𝟫' => '9', + '𝟬' => '0', + '𝟭' => '1', + '𝟮' => '2', + '𝟯' => '3', + '𝟰' => '4', + '𝟱' => '5', + '𝟲' => '6', + '𝟳' => '7', + '𝟴' => '8', + '𝟵' => '9', + '𝟶' => '0', + '𝟷' => '1', + '𝟸' => '2', + '𝟹' => '3', + '𝟺' => '4', + '𝟻' => '5', + '𝟼' => '6', + '𝟽' => '7', + '𝟾' => '8', + '𝟿' => '9', + '𞸀' => 'ا', + '𞸁' => 'ب', + '𞸂' => 'ج', + '𞸃' => 'د', + '𞸅' => 'و', + '𞸆' => 'ز', + '𞸇' => 'ح', + '𞸈' => 'ط', + '𞸉' => 'ي', + '𞸊' => 'ك', + '𞸋' => 'ل', + '𞸌' => 'م', + '𞸍' => 'ن', + '𞸎' => 'س', + '𞸏' => 'ع', + '𞸐' => 'ف', + '𞸑' => 'ص', + '𞸒' => 'ق', + '𞸓' => 'ر', + '𞸔' => 'ش', + '𞸕' => 'ت', + '𞸖' => 'ث', + '𞸗' => 'خ', + '𞸘' => 'ذ', + '𞸙' => 'ض', + '𞸚' => 'ظ', + '𞸛' => 'غ', + '𞸜' => 'ٮ', + '𞸝' => 'ں', + '𞸞' => 'ڡ', + '𞸟' => 'ٯ', + '𞸡' => 'ب', + '𞸢' => 'ج', + '𞸤' => 'ه', + '𞸧' => 'ح', + '𞸩' => 'ي', + '𞸪' => 'ك', + '𞸫' => 'ل', + '𞸬' => 'م', + '𞸭' => 'ن', + '𞸮' => 'س', + '𞸯' => 'ع', + '𞸰' => 'ف', + '𞸱' => 'ص', + '𞸲' => 'ق', + '𞸴' => 'ش', + '𞸵' => 'ت', + '𞸶' => 'ث', + '𞸷' => 'خ', + '𞸹' => 'ض', + '𞸻' => 'غ', + '𞹂' => 'ج', + '𞹇' => 'ح', + '𞹉' => 'ي', + '𞹋' => 'ل', + '𞹍' => 'ن', + '𞹎' => 'س', + '𞹏' => 'ع', + '𞹑' => 'ص', + '𞹒' => 'ق', + '𞹔' => 'ش', + '𞹗' => 'خ', + '𞹙' => 'ض', + '𞹛' => 'غ', + '𞹝' => 'ں', + '𞹟' => 'ٯ', + '𞹡' => 'ب', + '𞹢' => 'ج', + '𞹤' => 'ه', + '𞹧' => 'ح', + '𞹨' => 'ط', + '𞹩' => 'ي', + '𞹪' => 'ك', + '𞹬' => 'م', + '𞹭' => 'ن', + '𞹮' => 'س', + '𞹯' => 'ع', + '𞹰' => 'ف', + '𞹱' => 'ص', + '𞹲' => 'ق', + '𞹴' => 'ش', + '𞹵' => 'ت', + '𞹶' => 'ث', + '𞹷' => 'خ', + '𞹹' => 'ض', + '𞹺' => 'ظ', + '𞹻' => 'غ', + '𞹼' => 'ٮ', + '𞹾' => 'ڡ', + '𞺀' => 'ا', + '𞺁' => 'ب', + '𞺂' => 'ج', + '𞺃' => 'د', + '𞺄' => 'ه', + '𞺅' => 'و', + '𞺆' => 'ز', + '𞺇' => 'ح', + '𞺈' => 'ط', + '𞺉' => 'ي', + '𞺋' => 'ل', + '𞺌' => 'م', + '𞺍' => 'ن', + '𞺎' => 'س', + '𞺏' => 'ع', + '𞺐' => 'ف', + '𞺑' => 'ص', + '𞺒' => 'ق', + '𞺓' => 'ر', + '𞺔' => 'ش', + '𞺕' => 'ت', + '𞺖' => 'ث', + '𞺗' => 'خ', + '𞺘' => 'ذ', + '𞺙' => 'ض', + '𞺚' => 'ظ', + '𞺛' => 'غ', + '𞺡' => 'ب', + '𞺢' => 'ج', + '𞺣' => 'د', + '𞺥' => 'و', + '𞺦' => 'ز', + '𞺧' => 'ح', + '𞺨' => 'ط', + '𞺩' => 'ي', + '𞺫' => 'ل', + '𞺬' => 'م', + '𞺭' => 'ن', + '𞺮' => 'س', + '𞺯' => 'ع', + '𞺰' => 'ف', + '𞺱' => 'ص', + '𞺲' => 'ق', + '𞺳' => 'ر', + '𞺴' => 'ش', + '𞺵' => 'ت', + '𞺶' => 'ث', + '𞺷' => 'خ', + '𞺸' => 'ذ', + '𞺹' => 'ض', + '𞺺' => 'ظ', + '𞺻' => 'غ', + '🄀' => '0.', + '🄁' => '0,', + '🄂' => '1,', + '🄃' => '2,', + '🄄' => '3,', + '🄅' => '4,', + '🄆' => '5,', + '🄇' => '6,', + '🄈' => '7,', + '🄉' => '8,', + '🄊' => '9,', + '🄐' => '(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)', + '🄪' => '〔S〕', + '🄫' => 'C', + '🄬' => 'R', + '🄭' => 'CD', + '🄮' => 'WZ', + '🄰' => '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', + '🅊' => 'HV', + '🅋' => 'MV', + '🅌' => 'SD', + '🅍' => 'SS', + '🅎' => 'PPV', + '🅏' => 'WC', + '🅪' => 'MC', + '🅫' => 'MD', + '🅬' => 'MR', + '🆐' => 'DJ', + '🈀' => 'ほか', + '🈁' => 'ココ', + '🈂' => 'サ', + '🈐' => '手', + '🈑' => '字', + '🈒' => '双', + '🈓' => 'デ', + '🈔' => '二', + '🈕' => '多', + '🈖' => '解', + '🈗' => '天', + '🈘' => '交', + '🈙' => '映', + '🈚' => '無', + '🈛' => '料', + '🈜' => '前', + '🈝' => '後', + '🈞' => '再', + '🈟' => '新', + '🈠' => '初', + '🈡' => '終', + '🈢' => '生', + '🈣' => '販', + '🈤' => '声', + '🈥' => '吹', + '🈦' => '演', + '🈧' => '投', + '🈨' => '捕', + '🈩' => '一', + '🈪' => '三', + '🈫' => '遊', + '🈬' => '左', + '🈭' => '中', + '🈮' => '右', + '🈯' => '指', + '🈰' => '走', + '🈱' => '打', + '🈲' => '禁', + '🈳' => '空', + '🈴' => '合', + '🈵' => '満', + '🈶' => '有', + '🈷' => '月', + '🈸' => '申', + '🈹' => '割', + '🈺' => '営', + '🈻' => '配', + '🉀' => '〔本〕', + '🉁' => '〔三〕', + '🉂' => '〔二〕', + '🉃' => '〔安〕', + '🉄' => '〔点〕', + '🉅' => '〔打〕', + '🉆' => '〔盗〕', + '🉇' => '〔勝〕', + '🉈' => '〔敗〕', + '🉐' => '得', + '🉑' => '可', + '🯰' => '0', + '🯱' => '1', + '🯲' => '2', + '🯳' => '3', + '🯴' => '4', + '🯵' => '5', + '🯶' => '6', + '🯷' => '7', + '🯸' => '8', + '🯹' => '9', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/deviation.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/deviation.php new file mode 100644 index 000000000..0bbd33567 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/deviation.php @@ -0,0 +1,8 @@ + 'ss', + 962 => 'σ', + 8204 => '', + 8205 => '', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed.php new file mode 100644 index 000000000..25a5f564d --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed.php @@ -0,0 +1,2638 @@ + true, + 889 => true, + 896 => true, + 897 => true, + 898 => true, + 899 => true, + 907 => true, + 909 => true, + 930 => true, + 1216 => true, + 1328 => true, + 1367 => true, + 1368 => true, + 1419 => true, + 1420 => true, + 1424 => true, + 1480 => true, + 1481 => true, + 1482 => true, + 1483 => true, + 1484 => true, + 1485 => true, + 1486 => true, + 1487 => true, + 1515 => true, + 1516 => true, + 1517 => true, + 1518 => true, + 1525 => true, + 1526 => true, + 1527 => true, + 1528 => true, + 1529 => true, + 1530 => true, + 1531 => true, + 1532 => true, + 1533 => true, + 1534 => true, + 1535 => true, + 1536 => true, + 1537 => true, + 1538 => true, + 1539 => true, + 1540 => true, + 1541 => true, + 1564 => true, + 1565 => true, + 1757 => true, + 1806 => true, + 1807 => true, + 1867 => true, + 1868 => true, + 1970 => true, + 1971 => true, + 1972 => true, + 1973 => true, + 1974 => true, + 1975 => true, + 1976 => true, + 1977 => true, + 1978 => true, + 1979 => true, + 1980 => true, + 1981 => true, + 1982 => true, + 1983 => true, + 2043 => true, + 2044 => true, + 2094 => true, + 2095 => true, + 2111 => true, + 2140 => true, + 2141 => true, + 2143 => true, + 2229 => true, + 2248 => true, + 2249 => true, + 2250 => true, + 2251 => true, + 2252 => true, + 2253 => true, + 2254 => true, + 2255 => true, + 2256 => true, + 2257 => true, + 2258 => true, + 2274 => true, + 2436 => true, + 2445 => true, + 2446 => true, + 2449 => true, + 2450 => true, + 2473 => true, + 2481 => true, + 2483 => true, + 2484 => true, + 2485 => true, + 2490 => true, + 2491 => true, + 2501 => true, + 2502 => true, + 2505 => true, + 2506 => true, + 2511 => true, + 2512 => true, + 2513 => true, + 2514 => true, + 2515 => true, + 2516 => true, + 2517 => true, + 2518 => true, + 2520 => true, + 2521 => true, + 2522 => true, + 2523 => true, + 2526 => true, + 2532 => true, + 2533 => true, + 2559 => true, + 2560 => true, + 2564 => true, + 2571 => true, + 2572 => true, + 2573 => true, + 2574 => true, + 2577 => true, + 2578 => true, + 2601 => true, + 2609 => true, + 2612 => true, + 2615 => true, + 2618 => true, + 2619 => true, + 2621 => true, + 2627 => true, + 2628 => true, + 2629 => true, + 2630 => true, + 2633 => true, + 2634 => true, + 2638 => true, + 2639 => true, + 2640 => true, + 2642 => true, + 2643 => true, + 2644 => true, + 2645 => true, + 2646 => true, + 2647 => true, + 2648 => true, + 2653 => true, + 2655 => true, + 2656 => true, + 2657 => true, + 2658 => true, + 2659 => true, + 2660 => true, + 2661 => true, + 2679 => true, + 2680 => true, + 2681 => true, + 2682 => true, + 2683 => true, + 2684 => true, + 2685 => true, + 2686 => true, + 2687 => true, + 2688 => true, + 2692 => true, + 2702 => true, + 2706 => true, + 2729 => true, + 2737 => true, + 2740 => true, + 2746 => true, + 2747 => true, + 2758 => true, + 2762 => true, + 2766 => true, + 2767 => true, + 2769 => true, + 2770 => true, + 2771 => true, + 2772 => true, + 2773 => true, + 2774 => true, + 2775 => true, + 2776 => true, + 2777 => true, + 2778 => true, + 2779 => true, + 2780 => true, + 2781 => true, + 2782 => true, + 2783 => true, + 2788 => true, + 2789 => true, + 2802 => true, + 2803 => true, + 2804 => true, + 2805 => true, + 2806 => true, + 2807 => true, + 2808 => true, + 2816 => true, + 2820 => true, + 2829 => true, + 2830 => true, + 2833 => true, + 2834 => true, + 2857 => true, + 2865 => true, + 2868 => true, + 2874 => true, + 2875 => true, + 2885 => true, + 2886 => true, + 2889 => true, + 2890 => true, + 2894 => true, + 2895 => true, + 2896 => true, + 2897 => true, + 2898 => true, + 2899 => true, + 2900 => true, + 2904 => true, + 2905 => true, + 2906 => true, + 2907 => true, + 2910 => true, + 2916 => true, + 2917 => true, + 2936 => true, + 2937 => true, + 2938 => true, + 2939 => true, + 2940 => true, + 2941 => true, + 2942 => true, + 2943 => true, + 2944 => true, + 2945 => true, + 2948 => true, + 2955 => true, + 2956 => true, + 2957 => true, + 2961 => true, + 2966 => true, + 2967 => true, + 2968 => true, + 2971 => true, + 2973 => true, + 2976 => true, + 2977 => true, + 2978 => true, + 2981 => true, + 2982 => true, + 2983 => true, + 2987 => true, + 2988 => true, + 2989 => true, + 3002 => true, + 3003 => true, + 3004 => true, + 3005 => true, + 3011 => true, + 3012 => true, + 3013 => true, + 3017 => true, + 3022 => true, + 3023 => true, + 3025 => true, + 3026 => true, + 3027 => true, + 3028 => true, + 3029 => true, + 3030 => true, + 3032 => true, + 3033 => true, + 3034 => true, + 3035 => true, + 3036 => true, + 3037 => true, + 3038 => true, + 3039 => true, + 3040 => true, + 3041 => true, + 3042 => true, + 3043 => true, + 3044 => true, + 3045 => true, + 3067 => true, + 3068 => true, + 3069 => true, + 3070 => true, + 3071 => true, + 3085 => true, + 3089 => true, + 3113 => true, + 3130 => true, + 3131 => true, + 3132 => true, + 3141 => true, + 3145 => true, + 3150 => true, + 3151 => true, + 3152 => true, + 3153 => true, + 3154 => true, + 3155 => true, + 3156 => true, + 3159 => true, + 3163 => true, + 3164 => true, + 3165 => true, + 3166 => true, + 3167 => true, + 3172 => true, + 3173 => true, + 3184 => true, + 3185 => true, + 3186 => true, + 3187 => true, + 3188 => true, + 3189 => true, + 3190 => true, + 3213 => true, + 3217 => true, + 3241 => true, + 3252 => true, + 3258 => true, + 3259 => true, + 3269 => true, + 3273 => true, + 3278 => true, + 3279 => true, + 3280 => true, + 3281 => true, + 3282 => true, + 3283 => true, + 3284 => true, + 3287 => true, + 3288 => true, + 3289 => true, + 3290 => true, + 3291 => true, + 3292 => true, + 3293 => true, + 3295 => true, + 3300 => true, + 3301 => true, + 3312 => true, + 3315 => true, + 3316 => true, + 3317 => true, + 3318 => true, + 3319 => true, + 3320 => true, + 3321 => true, + 3322 => true, + 3323 => true, + 3324 => true, + 3325 => true, + 3326 => true, + 3327 => true, + 3341 => true, + 3345 => true, + 3397 => true, + 3401 => true, + 3408 => true, + 3409 => true, + 3410 => true, + 3411 => true, + 3428 => true, + 3429 => true, + 3456 => true, + 3460 => true, + 3479 => true, + 3480 => true, + 3481 => true, + 3506 => true, + 3516 => true, + 3518 => true, + 3519 => true, + 3527 => true, + 3528 => true, + 3529 => true, + 3531 => true, + 3532 => true, + 3533 => true, + 3534 => true, + 3541 => true, + 3543 => true, + 3552 => true, + 3553 => true, + 3554 => true, + 3555 => true, + 3556 => true, + 3557 => true, + 3568 => true, + 3569 => true, + 3573 => true, + 3574 => true, + 3575 => true, + 3576 => true, + 3577 => true, + 3578 => true, + 3579 => true, + 3580 => true, + 3581 => true, + 3582 => true, + 3583 => true, + 3584 => true, + 3643 => true, + 3644 => true, + 3645 => true, + 3646 => true, + 3715 => true, + 3717 => true, + 3723 => true, + 3748 => true, + 3750 => true, + 3774 => true, + 3775 => true, + 3781 => true, + 3783 => true, + 3790 => true, + 3791 => true, + 3802 => true, + 3803 => true, + 3912 => true, + 3949 => true, + 3950 => true, + 3951 => true, + 3952 => true, + 3992 => true, + 4029 => true, + 4045 => true, + 4294 => true, + 4296 => true, + 4297 => true, + 4298 => true, + 4299 => true, + 4300 => true, + 4302 => true, + 4303 => true, + 4447 => true, + 4448 => true, + 4681 => true, + 4686 => true, + 4687 => true, + 4695 => true, + 4697 => true, + 4702 => true, + 4703 => true, + 4745 => true, + 4750 => true, + 4751 => true, + 4785 => true, + 4790 => true, + 4791 => true, + 4799 => true, + 4801 => true, + 4806 => true, + 4807 => true, + 4823 => true, + 4881 => true, + 4886 => true, + 4887 => true, + 4955 => true, + 4956 => true, + 4989 => true, + 4990 => true, + 4991 => true, + 5018 => true, + 5019 => true, + 5020 => true, + 5021 => true, + 5022 => true, + 5023 => true, + 5110 => true, + 5111 => true, + 5118 => true, + 5119 => true, + 5760 => true, + 5789 => true, + 5790 => true, + 5791 => true, + 5881 => true, + 5882 => true, + 5883 => true, + 5884 => true, + 5885 => true, + 5886 => true, + 5887 => true, + 5901 => true, + 5909 => true, + 5910 => true, + 5911 => true, + 5912 => true, + 5913 => true, + 5914 => true, + 5915 => true, + 5916 => true, + 5917 => true, + 5918 => true, + 5919 => true, + 5943 => true, + 5944 => true, + 5945 => true, + 5946 => true, + 5947 => true, + 5948 => true, + 5949 => true, + 5950 => true, + 5951 => true, + 5972 => true, + 5973 => true, + 5974 => true, + 5975 => true, + 5976 => true, + 5977 => true, + 5978 => true, + 5979 => true, + 5980 => true, + 5981 => true, + 5982 => true, + 5983 => true, + 5997 => true, + 6001 => true, + 6004 => true, + 6005 => true, + 6006 => true, + 6007 => true, + 6008 => true, + 6009 => true, + 6010 => true, + 6011 => true, + 6012 => true, + 6013 => true, + 6014 => true, + 6015 => true, + 6068 => true, + 6069 => true, + 6110 => true, + 6111 => true, + 6122 => true, + 6123 => true, + 6124 => true, + 6125 => true, + 6126 => true, + 6127 => true, + 6138 => true, + 6139 => true, + 6140 => true, + 6141 => true, + 6142 => true, + 6143 => true, + 6150 => true, + 6158 => true, + 6159 => true, + 6170 => true, + 6171 => true, + 6172 => true, + 6173 => true, + 6174 => true, + 6175 => true, + 6265 => true, + 6266 => true, + 6267 => true, + 6268 => true, + 6269 => true, + 6270 => true, + 6271 => true, + 6315 => true, + 6316 => true, + 6317 => true, + 6318 => true, + 6319 => true, + 6390 => true, + 6391 => true, + 6392 => true, + 6393 => true, + 6394 => true, + 6395 => true, + 6396 => true, + 6397 => true, + 6398 => true, + 6399 => true, + 6431 => true, + 6444 => true, + 6445 => true, + 6446 => true, + 6447 => true, + 6460 => true, + 6461 => true, + 6462 => true, + 6463 => true, + 6465 => true, + 6466 => true, + 6467 => true, + 6510 => true, + 6511 => true, + 6517 => true, + 6518 => true, + 6519 => true, + 6520 => true, + 6521 => true, + 6522 => true, + 6523 => true, + 6524 => true, + 6525 => true, + 6526 => true, + 6527 => true, + 6572 => true, + 6573 => true, + 6574 => true, + 6575 => true, + 6602 => true, + 6603 => true, + 6604 => true, + 6605 => true, + 6606 => true, + 6607 => true, + 6619 => true, + 6620 => true, + 6621 => true, + 6684 => true, + 6685 => true, + 6751 => true, + 6781 => true, + 6782 => true, + 6794 => true, + 6795 => true, + 6796 => true, + 6797 => true, + 6798 => true, + 6799 => true, + 6810 => true, + 6811 => true, + 6812 => true, + 6813 => true, + 6814 => true, + 6815 => true, + 6830 => true, + 6831 => true, + 6988 => true, + 6989 => true, + 6990 => true, + 6991 => true, + 7037 => true, + 7038 => true, + 7039 => true, + 7156 => true, + 7157 => true, + 7158 => true, + 7159 => true, + 7160 => true, + 7161 => true, + 7162 => true, + 7163 => true, + 7224 => true, + 7225 => true, + 7226 => true, + 7242 => true, + 7243 => true, + 7244 => true, + 7305 => true, + 7306 => true, + 7307 => true, + 7308 => true, + 7309 => true, + 7310 => true, + 7311 => true, + 7355 => true, + 7356 => true, + 7368 => true, + 7369 => true, + 7370 => true, + 7371 => true, + 7372 => true, + 7373 => true, + 7374 => true, + 7375 => true, + 7419 => true, + 7420 => true, + 7421 => true, + 7422 => true, + 7423 => true, + 7674 => true, + 7958 => true, + 7959 => true, + 7966 => true, + 7967 => true, + 8006 => true, + 8007 => true, + 8014 => true, + 8015 => true, + 8024 => true, + 8026 => true, + 8028 => true, + 8030 => true, + 8062 => true, + 8063 => true, + 8117 => true, + 8133 => true, + 8148 => true, + 8149 => true, + 8156 => true, + 8176 => true, + 8177 => true, + 8181 => true, + 8191 => true, + 8206 => true, + 8207 => true, + 8228 => true, + 8229 => true, + 8230 => true, + 8232 => true, + 8233 => true, + 8234 => true, + 8235 => true, + 8236 => true, + 8237 => true, + 8238 => true, + 8289 => true, + 8290 => true, + 8291 => true, + 8293 => true, + 8294 => true, + 8295 => true, + 8296 => true, + 8297 => true, + 8298 => true, + 8299 => true, + 8300 => true, + 8301 => true, + 8302 => true, + 8303 => true, + 8306 => true, + 8307 => true, + 8335 => true, + 8349 => true, + 8350 => true, + 8351 => true, + 8384 => true, + 8385 => true, + 8386 => true, + 8387 => true, + 8388 => true, + 8389 => true, + 8390 => true, + 8391 => true, + 8392 => true, + 8393 => true, + 8394 => true, + 8395 => true, + 8396 => true, + 8397 => true, + 8398 => true, + 8399 => true, + 8433 => true, + 8434 => true, + 8435 => true, + 8436 => true, + 8437 => true, + 8438 => true, + 8439 => true, + 8440 => true, + 8441 => true, + 8442 => true, + 8443 => true, + 8444 => true, + 8445 => true, + 8446 => true, + 8447 => true, + 8498 => true, + 8579 => true, + 8588 => true, + 8589 => true, + 8590 => true, + 8591 => true, + 9255 => true, + 9256 => true, + 9257 => true, + 9258 => true, + 9259 => true, + 9260 => true, + 9261 => true, + 9262 => true, + 9263 => true, + 9264 => true, + 9265 => true, + 9266 => true, + 9267 => true, + 9268 => true, + 9269 => true, + 9270 => true, + 9271 => true, + 9272 => true, + 9273 => true, + 9274 => true, + 9275 => true, + 9276 => true, + 9277 => true, + 9278 => true, + 9279 => true, + 9291 => true, + 9292 => true, + 9293 => true, + 9294 => true, + 9295 => true, + 9296 => true, + 9297 => true, + 9298 => true, + 9299 => true, + 9300 => true, + 9301 => true, + 9302 => true, + 9303 => true, + 9304 => true, + 9305 => true, + 9306 => true, + 9307 => true, + 9308 => true, + 9309 => true, + 9310 => true, + 9311 => true, + 9352 => true, + 9353 => true, + 9354 => true, + 9355 => true, + 9356 => true, + 9357 => true, + 9358 => true, + 9359 => true, + 9360 => true, + 9361 => true, + 9362 => true, + 9363 => true, + 9364 => true, + 9365 => true, + 9366 => true, + 9367 => true, + 9368 => true, + 9369 => true, + 9370 => true, + 9371 => true, + 11124 => true, + 11125 => true, + 11158 => true, + 11311 => true, + 11359 => true, + 11508 => true, + 11509 => true, + 11510 => true, + 11511 => true, + 11512 => true, + 11558 => true, + 11560 => true, + 11561 => true, + 11562 => true, + 11563 => true, + 11564 => true, + 11566 => true, + 11567 => true, + 11624 => true, + 11625 => true, + 11626 => true, + 11627 => true, + 11628 => true, + 11629 => true, + 11630 => true, + 11633 => true, + 11634 => true, + 11635 => true, + 11636 => true, + 11637 => true, + 11638 => true, + 11639 => true, + 11640 => true, + 11641 => true, + 11642 => true, + 11643 => true, + 11644 => true, + 11645 => true, + 11646 => true, + 11671 => true, + 11672 => true, + 11673 => true, + 11674 => true, + 11675 => true, + 11676 => true, + 11677 => true, + 11678 => true, + 11679 => true, + 11687 => true, + 11695 => true, + 11703 => true, + 11711 => true, + 11719 => true, + 11727 => true, + 11735 => true, + 11743 => true, + 11930 => true, + 12020 => true, + 12021 => true, + 12022 => true, + 12023 => true, + 12024 => true, + 12025 => true, + 12026 => true, + 12027 => true, + 12028 => true, + 12029 => true, + 12030 => true, + 12031 => true, + 12246 => true, + 12247 => true, + 12248 => true, + 12249 => true, + 12250 => true, + 12251 => true, + 12252 => true, + 12253 => true, + 12254 => true, + 12255 => true, + 12256 => true, + 12257 => true, + 12258 => true, + 12259 => true, + 12260 => true, + 12261 => true, + 12262 => true, + 12263 => true, + 12264 => true, + 12265 => true, + 12266 => true, + 12267 => true, + 12268 => true, + 12269 => true, + 12270 => true, + 12271 => true, + 12272 => true, + 12273 => true, + 12274 => true, + 12275 => true, + 12276 => true, + 12277 => true, + 12278 => true, + 12279 => true, + 12280 => true, + 12281 => true, + 12282 => true, + 12283 => true, + 12284 => true, + 12285 => true, + 12286 => true, + 12287 => true, + 12352 => true, + 12439 => true, + 12440 => true, + 12544 => true, + 12545 => true, + 12546 => true, + 12547 => true, + 12548 => true, + 12592 => true, + 12644 => true, + 12687 => true, + 12772 => true, + 12773 => true, + 12774 => true, + 12775 => true, + 12776 => true, + 12777 => true, + 12778 => true, + 12779 => true, + 12780 => true, + 12781 => true, + 12782 => true, + 12783 => true, + 12831 => true, + 13250 => true, + 13255 => true, + 13272 => true, + 40957 => true, + 40958 => true, + 40959 => true, + 42125 => true, + 42126 => true, + 42127 => true, + 42183 => true, + 42184 => true, + 42185 => true, + 42186 => true, + 42187 => true, + 42188 => true, + 42189 => true, + 42190 => true, + 42191 => true, + 42540 => true, + 42541 => true, + 42542 => true, + 42543 => true, + 42544 => true, + 42545 => true, + 42546 => true, + 42547 => true, + 42548 => true, + 42549 => true, + 42550 => true, + 42551 => true, + 42552 => true, + 42553 => true, + 42554 => true, + 42555 => true, + 42556 => true, + 42557 => true, + 42558 => true, + 42559 => true, + 42744 => true, + 42745 => true, + 42746 => true, + 42747 => true, + 42748 => true, + 42749 => true, + 42750 => true, + 42751 => true, + 42944 => true, + 42945 => true, + 43053 => true, + 43054 => true, + 43055 => true, + 43066 => true, + 43067 => true, + 43068 => true, + 43069 => true, + 43070 => true, + 43071 => true, + 43128 => true, + 43129 => true, + 43130 => true, + 43131 => true, + 43132 => true, + 43133 => true, + 43134 => true, + 43135 => true, + 43206 => true, + 43207 => true, + 43208 => true, + 43209 => true, + 43210 => true, + 43211 => true, + 43212 => true, + 43213 => true, + 43226 => true, + 43227 => true, + 43228 => true, + 43229 => true, + 43230 => true, + 43231 => true, + 43348 => true, + 43349 => true, + 43350 => true, + 43351 => true, + 43352 => true, + 43353 => true, + 43354 => true, + 43355 => true, + 43356 => true, + 43357 => true, + 43358 => true, + 43389 => true, + 43390 => true, + 43391 => true, + 43470 => true, + 43482 => true, + 43483 => true, + 43484 => true, + 43485 => true, + 43519 => true, + 43575 => true, + 43576 => true, + 43577 => true, + 43578 => true, + 43579 => true, + 43580 => true, + 43581 => true, + 43582 => true, + 43583 => true, + 43598 => true, + 43599 => true, + 43610 => true, + 43611 => true, + 43715 => true, + 43716 => true, + 43717 => true, + 43718 => true, + 43719 => true, + 43720 => true, + 43721 => true, + 43722 => true, + 43723 => true, + 43724 => true, + 43725 => true, + 43726 => true, + 43727 => true, + 43728 => true, + 43729 => true, + 43730 => true, + 43731 => true, + 43732 => true, + 43733 => true, + 43734 => true, + 43735 => true, + 43736 => true, + 43737 => true, + 43738 => true, + 43767 => true, + 43768 => true, + 43769 => true, + 43770 => true, + 43771 => true, + 43772 => true, + 43773 => true, + 43774 => true, + 43775 => true, + 43776 => true, + 43783 => true, + 43784 => true, + 43791 => true, + 43792 => true, + 43799 => true, + 43800 => true, + 43801 => true, + 43802 => true, + 43803 => true, + 43804 => true, + 43805 => true, + 43806 => true, + 43807 => true, + 43815 => true, + 43823 => true, + 43884 => true, + 43885 => true, + 43886 => true, + 43887 => true, + 44014 => true, + 44015 => true, + 44026 => true, + 44027 => true, + 44028 => true, + 44029 => true, + 44030 => true, + 44031 => true, + 55204 => true, + 55205 => true, + 55206 => true, + 55207 => true, + 55208 => true, + 55209 => true, + 55210 => true, + 55211 => true, + 55212 => true, + 55213 => true, + 55214 => true, + 55215 => true, + 55239 => true, + 55240 => true, + 55241 => true, + 55242 => true, + 55292 => true, + 55293 => true, + 55294 => true, + 55295 => true, + 64110 => true, + 64111 => true, + 64263 => true, + 64264 => true, + 64265 => true, + 64266 => true, + 64267 => true, + 64268 => true, + 64269 => true, + 64270 => true, + 64271 => true, + 64272 => true, + 64273 => true, + 64274 => true, + 64280 => true, + 64281 => true, + 64282 => true, + 64283 => true, + 64284 => true, + 64311 => true, + 64317 => true, + 64319 => true, + 64322 => true, + 64325 => true, + 64450 => true, + 64451 => true, + 64452 => true, + 64453 => true, + 64454 => true, + 64455 => true, + 64456 => true, + 64457 => true, + 64458 => true, + 64459 => true, + 64460 => true, + 64461 => true, + 64462 => true, + 64463 => true, + 64464 => true, + 64465 => true, + 64466 => true, + 64832 => true, + 64833 => true, + 64834 => true, + 64835 => true, + 64836 => true, + 64837 => true, + 64838 => true, + 64839 => true, + 64840 => true, + 64841 => true, + 64842 => true, + 64843 => true, + 64844 => true, + 64845 => true, + 64846 => true, + 64847 => true, + 64912 => true, + 64913 => true, + 64968 => true, + 64969 => true, + 64970 => true, + 64971 => true, + 64972 => true, + 64973 => true, + 64974 => true, + 64975 => true, + 65022 => true, + 65023 => true, + 65042 => true, + 65049 => true, + 65050 => true, + 65051 => true, + 65052 => true, + 65053 => true, + 65054 => true, + 65055 => true, + 65072 => true, + 65106 => true, + 65107 => true, + 65127 => true, + 65132 => true, + 65133 => true, + 65134 => true, + 65135 => true, + 65141 => true, + 65277 => true, + 65278 => true, + 65280 => true, + 65440 => true, + 65471 => true, + 65472 => true, + 65473 => true, + 65480 => true, + 65481 => true, + 65488 => true, + 65489 => true, + 65496 => true, + 65497 => true, + 65501 => true, + 65502 => true, + 65503 => true, + 65511 => true, + 65519 => true, + 65520 => true, + 65521 => true, + 65522 => true, + 65523 => true, + 65524 => true, + 65525 => true, + 65526 => true, + 65527 => true, + 65528 => true, + 65529 => true, + 65530 => true, + 65531 => true, + 65532 => true, + 65533 => true, + 65534 => true, + 65535 => true, + 65548 => true, + 65575 => true, + 65595 => true, + 65598 => true, + 65614 => true, + 65615 => true, + 65787 => true, + 65788 => true, + 65789 => true, + 65790 => true, + 65791 => true, + 65795 => true, + 65796 => true, + 65797 => true, + 65798 => true, + 65844 => true, + 65845 => true, + 65846 => true, + 65935 => true, + 65949 => true, + 65950 => true, + 65951 => true, + 66205 => true, + 66206 => true, + 66207 => true, + 66257 => true, + 66258 => true, + 66259 => true, + 66260 => true, + 66261 => true, + 66262 => true, + 66263 => true, + 66264 => true, + 66265 => true, + 66266 => true, + 66267 => true, + 66268 => true, + 66269 => true, + 66270 => true, + 66271 => true, + 66300 => true, + 66301 => true, + 66302 => true, + 66303 => true, + 66340 => true, + 66341 => true, + 66342 => true, + 66343 => true, + 66344 => true, + 66345 => true, + 66346 => true, + 66347 => true, + 66348 => true, + 66379 => true, + 66380 => true, + 66381 => true, + 66382 => true, + 66383 => true, + 66427 => true, + 66428 => true, + 66429 => true, + 66430 => true, + 66431 => true, + 66462 => true, + 66500 => true, + 66501 => true, + 66502 => true, + 66503 => true, + 66718 => true, + 66719 => true, + 66730 => true, + 66731 => true, + 66732 => true, + 66733 => true, + 66734 => true, + 66735 => true, + 66772 => true, + 66773 => true, + 66774 => true, + 66775 => true, + 66812 => true, + 66813 => true, + 66814 => true, + 66815 => true, + 66856 => true, + 66857 => true, + 66858 => true, + 66859 => true, + 66860 => true, + 66861 => true, + 66862 => true, + 66863 => true, + 66916 => true, + 66917 => true, + 66918 => true, + 66919 => true, + 66920 => true, + 66921 => true, + 66922 => true, + 66923 => true, + 66924 => true, + 66925 => true, + 66926 => true, + 67383 => true, + 67384 => true, + 67385 => true, + 67386 => true, + 67387 => true, + 67388 => true, + 67389 => true, + 67390 => true, + 67391 => true, + 67414 => true, + 67415 => true, + 67416 => true, + 67417 => true, + 67418 => true, + 67419 => true, + 67420 => true, + 67421 => true, + 67422 => true, + 67423 => true, + 67590 => true, + 67591 => true, + 67593 => true, + 67638 => true, + 67641 => true, + 67642 => true, + 67643 => true, + 67645 => true, + 67646 => true, + 67670 => true, + 67743 => true, + 67744 => true, + 67745 => true, + 67746 => true, + 67747 => true, + 67748 => true, + 67749 => true, + 67750 => true, + 67827 => true, + 67830 => true, + 67831 => true, + 67832 => true, + 67833 => true, + 67834 => true, + 67868 => true, + 67869 => true, + 67870 => true, + 67898 => true, + 67899 => true, + 67900 => true, + 67901 => true, + 67902 => true, + 68024 => true, + 68025 => true, + 68026 => true, + 68027 => true, + 68048 => true, + 68049 => true, + 68100 => true, + 68103 => true, + 68104 => true, + 68105 => true, + 68106 => true, + 68107 => true, + 68116 => true, + 68120 => true, + 68150 => true, + 68151 => true, + 68155 => true, + 68156 => true, + 68157 => true, + 68158 => true, + 68169 => true, + 68170 => true, + 68171 => true, + 68172 => true, + 68173 => true, + 68174 => true, + 68175 => true, + 68185 => true, + 68186 => true, + 68187 => true, + 68188 => true, + 68189 => true, + 68190 => true, + 68191 => true, + 68327 => true, + 68328 => true, + 68329 => true, + 68330 => true, + 68343 => true, + 68344 => true, + 68345 => true, + 68346 => true, + 68347 => true, + 68348 => true, + 68349 => true, + 68350 => true, + 68351 => true, + 68406 => true, + 68407 => true, + 68408 => true, + 68438 => true, + 68439 => true, + 68467 => true, + 68468 => true, + 68469 => true, + 68470 => true, + 68471 => true, + 68498 => true, + 68499 => true, + 68500 => true, + 68501 => true, + 68502 => true, + 68503 => true, + 68504 => true, + 68509 => true, + 68510 => true, + 68511 => true, + 68512 => true, + 68513 => true, + 68514 => true, + 68515 => true, + 68516 => true, + 68517 => true, + 68518 => true, + 68519 => true, + 68520 => true, + 68787 => true, + 68788 => true, + 68789 => true, + 68790 => true, + 68791 => true, + 68792 => true, + 68793 => true, + 68794 => true, + 68795 => true, + 68796 => true, + 68797 => true, + 68798 => true, + 68799 => true, + 68851 => true, + 68852 => true, + 68853 => true, + 68854 => true, + 68855 => true, + 68856 => true, + 68857 => true, + 68904 => true, + 68905 => true, + 68906 => true, + 68907 => true, + 68908 => true, + 68909 => true, + 68910 => true, + 68911 => true, + 69247 => true, + 69290 => true, + 69294 => true, + 69295 => true, + 69416 => true, + 69417 => true, + 69418 => true, + 69419 => true, + 69420 => true, + 69421 => true, + 69422 => true, + 69423 => true, + 69580 => true, + 69581 => true, + 69582 => true, + 69583 => true, + 69584 => true, + 69585 => true, + 69586 => true, + 69587 => true, + 69588 => true, + 69589 => true, + 69590 => true, + 69591 => true, + 69592 => true, + 69593 => true, + 69594 => true, + 69595 => true, + 69596 => true, + 69597 => true, + 69598 => true, + 69599 => true, + 69623 => true, + 69624 => true, + 69625 => true, + 69626 => true, + 69627 => true, + 69628 => true, + 69629 => true, + 69630 => true, + 69631 => true, + 69710 => true, + 69711 => true, + 69712 => true, + 69713 => true, + 69744 => true, + 69745 => true, + 69746 => true, + 69747 => true, + 69748 => true, + 69749 => true, + 69750 => true, + 69751 => true, + 69752 => true, + 69753 => true, + 69754 => true, + 69755 => true, + 69756 => true, + 69757 => true, + 69758 => true, + 69821 => true, + 69826 => true, + 69827 => true, + 69828 => true, + 69829 => true, + 69830 => true, + 69831 => true, + 69832 => true, + 69833 => true, + 69834 => true, + 69835 => true, + 69836 => true, + 69837 => true, + 69838 => true, + 69839 => true, + 69865 => true, + 69866 => true, + 69867 => true, + 69868 => true, + 69869 => true, + 69870 => true, + 69871 => true, + 69882 => true, + 69883 => true, + 69884 => true, + 69885 => true, + 69886 => true, + 69887 => true, + 69941 => true, + 69960 => true, + 69961 => true, + 69962 => true, + 69963 => true, + 69964 => true, + 69965 => true, + 69966 => true, + 69967 => true, + 70007 => true, + 70008 => true, + 70009 => true, + 70010 => true, + 70011 => true, + 70012 => true, + 70013 => true, + 70014 => true, + 70015 => true, + 70112 => true, + 70133 => true, + 70134 => true, + 70135 => true, + 70136 => true, + 70137 => true, + 70138 => true, + 70139 => true, + 70140 => true, + 70141 => true, + 70142 => true, + 70143 => true, + 70162 => true, + 70279 => true, + 70281 => true, + 70286 => true, + 70302 => true, + 70314 => true, + 70315 => true, + 70316 => true, + 70317 => true, + 70318 => true, + 70319 => true, + 70379 => true, + 70380 => true, + 70381 => true, + 70382 => true, + 70383 => true, + 70394 => true, + 70395 => true, + 70396 => true, + 70397 => true, + 70398 => true, + 70399 => true, + 70404 => true, + 70413 => true, + 70414 => true, + 70417 => true, + 70418 => true, + 70441 => true, + 70449 => true, + 70452 => true, + 70458 => true, + 70469 => true, + 70470 => true, + 70473 => true, + 70474 => true, + 70478 => true, + 70479 => true, + 70481 => true, + 70482 => true, + 70483 => true, + 70484 => true, + 70485 => true, + 70486 => true, + 70488 => true, + 70489 => true, + 70490 => true, + 70491 => true, + 70492 => true, + 70500 => true, + 70501 => true, + 70509 => true, + 70510 => true, + 70511 => true, + 70748 => true, + 70754 => true, + 70755 => true, + 70756 => true, + 70757 => true, + 70758 => true, + 70759 => true, + 70760 => true, + 70761 => true, + 70762 => true, + 70763 => true, + 70764 => true, + 70765 => true, + 70766 => true, + 70767 => true, + 70768 => true, + 70769 => true, + 70770 => true, + 70771 => true, + 70772 => true, + 70773 => true, + 70774 => true, + 70775 => true, + 70776 => true, + 70777 => true, + 70778 => true, + 70779 => true, + 70780 => true, + 70781 => true, + 70782 => true, + 70783 => true, + 70856 => true, + 70857 => true, + 70858 => true, + 70859 => true, + 70860 => true, + 70861 => true, + 70862 => true, + 70863 => true, + 71094 => true, + 71095 => true, + 71237 => true, + 71238 => true, + 71239 => true, + 71240 => true, + 71241 => true, + 71242 => true, + 71243 => true, + 71244 => true, + 71245 => true, + 71246 => true, + 71247 => true, + 71258 => true, + 71259 => true, + 71260 => true, + 71261 => true, + 71262 => true, + 71263 => true, + 71277 => true, + 71278 => true, + 71279 => true, + 71280 => true, + 71281 => true, + 71282 => true, + 71283 => true, + 71284 => true, + 71285 => true, + 71286 => true, + 71287 => true, + 71288 => true, + 71289 => true, + 71290 => true, + 71291 => true, + 71292 => true, + 71293 => true, + 71294 => true, + 71295 => true, + 71353 => true, + 71354 => true, + 71355 => true, + 71356 => true, + 71357 => true, + 71358 => true, + 71359 => true, + 71451 => true, + 71452 => true, + 71468 => true, + 71469 => true, + 71470 => true, + 71471 => true, + 71923 => true, + 71924 => true, + 71925 => true, + 71926 => true, + 71927 => true, + 71928 => true, + 71929 => true, + 71930 => true, + 71931 => true, + 71932 => true, + 71933 => true, + 71934 => true, + 71943 => true, + 71944 => true, + 71946 => true, + 71947 => true, + 71956 => true, + 71959 => true, + 71990 => true, + 71993 => true, + 71994 => true, + 72007 => true, + 72008 => true, + 72009 => true, + 72010 => true, + 72011 => true, + 72012 => true, + 72013 => true, + 72014 => true, + 72015 => true, + 72104 => true, + 72105 => true, + 72152 => true, + 72153 => true, + 72165 => true, + 72166 => true, + 72167 => true, + 72168 => true, + 72169 => true, + 72170 => true, + 72171 => true, + 72172 => true, + 72173 => true, + 72174 => true, + 72175 => true, + 72176 => true, + 72177 => true, + 72178 => true, + 72179 => true, + 72180 => true, + 72181 => true, + 72182 => true, + 72183 => true, + 72184 => true, + 72185 => true, + 72186 => true, + 72187 => true, + 72188 => true, + 72189 => true, + 72190 => true, + 72191 => true, + 72264 => true, + 72265 => true, + 72266 => true, + 72267 => true, + 72268 => true, + 72269 => true, + 72270 => true, + 72271 => true, + 72355 => true, + 72356 => true, + 72357 => true, + 72358 => true, + 72359 => true, + 72360 => true, + 72361 => true, + 72362 => true, + 72363 => true, + 72364 => true, + 72365 => true, + 72366 => true, + 72367 => true, + 72368 => true, + 72369 => true, + 72370 => true, + 72371 => true, + 72372 => true, + 72373 => true, + 72374 => true, + 72375 => true, + 72376 => true, + 72377 => true, + 72378 => true, + 72379 => true, + 72380 => true, + 72381 => true, + 72382 => true, + 72383 => true, + 72713 => true, + 72759 => true, + 72774 => true, + 72775 => true, + 72776 => true, + 72777 => true, + 72778 => true, + 72779 => true, + 72780 => true, + 72781 => true, + 72782 => true, + 72783 => true, + 72813 => true, + 72814 => true, + 72815 => true, + 72848 => true, + 72849 => true, + 72872 => true, + 72967 => true, + 72970 => true, + 73015 => true, + 73016 => true, + 73017 => true, + 73019 => true, + 73022 => true, + 73032 => true, + 73033 => true, + 73034 => true, + 73035 => true, + 73036 => true, + 73037 => true, + 73038 => true, + 73039 => true, + 73050 => true, + 73051 => true, + 73052 => true, + 73053 => true, + 73054 => true, + 73055 => true, + 73062 => true, + 73065 => true, + 73103 => true, + 73106 => true, + 73113 => true, + 73114 => true, + 73115 => true, + 73116 => true, + 73117 => true, + 73118 => true, + 73119 => true, + 73649 => true, + 73650 => true, + 73651 => true, + 73652 => true, + 73653 => true, + 73654 => true, + 73655 => true, + 73656 => true, + 73657 => true, + 73658 => true, + 73659 => true, + 73660 => true, + 73661 => true, + 73662 => true, + 73663 => true, + 73714 => true, + 73715 => true, + 73716 => true, + 73717 => true, + 73718 => true, + 73719 => true, + 73720 => true, + 73721 => true, + 73722 => true, + 73723 => true, + 73724 => true, + 73725 => true, + 73726 => true, + 74863 => true, + 74869 => true, + 74870 => true, + 74871 => true, + 74872 => true, + 74873 => true, + 74874 => true, + 74875 => true, + 74876 => true, + 74877 => true, + 74878 => true, + 74879 => true, + 78895 => true, + 78896 => true, + 78897 => true, + 78898 => true, + 78899 => true, + 78900 => true, + 78901 => true, + 78902 => true, + 78903 => true, + 78904 => true, + 92729 => true, + 92730 => true, + 92731 => true, + 92732 => true, + 92733 => true, + 92734 => true, + 92735 => true, + 92767 => true, + 92778 => true, + 92779 => true, + 92780 => true, + 92781 => true, + 92910 => true, + 92911 => true, + 92918 => true, + 92919 => true, + 92920 => true, + 92921 => true, + 92922 => true, + 92923 => true, + 92924 => true, + 92925 => true, + 92926 => true, + 92927 => true, + 92998 => true, + 92999 => true, + 93000 => true, + 93001 => true, + 93002 => true, + 93003 => true, + 93004 => true, + 93005 => true, + 93006 => true, + 93007 => true, + 93018 => true, + 93026 => true, + 93048 => true, + 93049 => true, + 93050 => true, + 93051 => true, + 93052 => true, + 94027 => true, + 94028 => true, + 94029 => true, + 94030 => true, + 94088 => true, + 94089 => true, + 94090 => true, + 94091 => true, + 94092 => true, + 94093 => true, + 94094 => true, + 94181 => true, + 94182 => true, + 94183 => true, + 94184 => true, + 94185 => true, + 94186 => true, + 94187 => true, + 94188 => true, + 94189 => true, + 94190 => true, + 94191 => true, + 94194 => true, + 94195 => true, + 94196 => true, + 94197 => true, + 94198 => true, + 94199 => true, + 94200 => true, + 94201 => true, + 94202 => true, + 94203 => true, + 94204 => true, + 94205 => true, + 94206 => true, + 94207 => true, + 100344 => true, + 100345 => true, + 100346 => true, + 100347 => true, + 100348 => true, + 100349 => true, + 100350 => true, + 100351 => true, + 110931 => true, + 110932 => true, + 110933 => true, + 110934 => true, + 110935 => true, + 110936 => true, + 110937 => true, + 110938 => true, + 110939 => true, + 110940 => true, + 110941 => true, + 110942 => true, + 110943 => true, + 110944 => true, + 110945 => true, + 110946 => true, + 110947 => true, + 110952 => true, + 110953 => true, + 110954 => true, + 110955 => true, + 110956 => true, + 110957 => true, + 110958 => true, + 110959 => true, + 113771 => true, + 113772 => true, + 113773 => true, + 113774 => true, + 113775 => true, + 113789 => true, + 113790 => true, + 113791 => true, + 113801 => true, + 113802 => true, + 113803 => true, + 113804 => true, + 113805 => true, + 113806 => true, + 113807 => true, + 113818 => true, + 113819 => true, + 119030 => true, + 119031 => true, + 119032 => true, + 119033 => true, + 119034 => true, + 119035 => true, + 119036 => true, + 119037 => true, + 119038 => true, + 119039 => true, + 119079 => true, + 119080 => true, + 119155 => true, + 119156 => true, + 119157 => true, + 119158 => true, + 119159 => true, + 119160 => true, + 119161 => true, + 119162 => true, + 119273 => true, + 119274 => true, + 119275 => true, + 119276 => true, + 119277 => true, + 119278 => true, + 119279 => true, + 119280 => true, + 119281 => true, + 119282 => true, + 119283 => true, + 119284 => true, + 119285 => true, + 119286 => true, + 119287 => true, + 119288 => true, + 119289 => true, + 119290 => true, + 119291 => true, + 119292 => true, + 119293 => true, + 119294 => true, + 119295 => true, + 119540 => true, + 119541 => true, + 119542 => true, + 119543 => true, + 119544 => true, + 119545 => true, + 119546 => true, + 119547 => true, + 119548 => true, + 119549 => true, + 119550 => true, + 119551 => true, + 119639 => true, + 119640 => true, + 119641 => true, + 119642 => true, + 119643 => true, + 119644 => true, + 119645 => true, + 119646 => true, + 119647 => true, + 119893 => true, + 119965 => true, + 119968 => true, + 119969 => true, + 119971 => true, + 119972 => true, + 119975 => true, + 119976 => true, + 119981 => true, + 119994 => true, + 119996 => true, + 120004 => true, + 120070 => true, + 120075 => true, + 120076 => true, + 120085 => true, + 120093 => true, + 120122 => true, + 120127 => true, + 120133 => true, + 120135 => true, + 120136 => true, + 120137 => true, + 120145 => true, + 120486 => true, + 120487 => true, + 120780 => true, + 120781 => true, + 121484 => true, + 121485 => true, + 121486 => true, + 121487 => true, + 121488 => true, + 121489 => true, + 121490 => true, + 121491 => true, + 121492 => true, + 121493 => true, + 121494 => true, + 121495 => true, + 121496 => true, + 121497 => true, + 121498 => true, + 121504 => true, + 122887 => true, + 122905 => true, + 122906 => true, + 122914 => true, + 122917 => true, + 123181 => true, + 123182 => true, + 123183 => true, + 123198 => true, + 123199 => true, + 123210 => true, + 123211 => true, + 123212 => true, + 123213 => true, + 123642 => true, + 123643 => true, + 123644 => true, + 123645 => true, + 123646 => true, + 125125 => true, + 125126 => true, + 125260 => true, + 125261 => true, + 125262 => true, + 125263 => true, + 125274 => true, + 125275 => true, + 125276 => true, + 125277 => true, + 126468 => true, + 126496 => true, + 126499 => true, + 126501 => true, + 126502 => true, + 126504 => true, + 126515 => true, + 126520 => true, + 126522 => true, + 126524 => true, + 126525 => true, + 126526 => true, + 126527 => true, + 126528 => true, + 126529 => true, + 126531 => true, + 126532 => true, + 126533 => true, + 126534 => true, + 126536 => true, + 126538 => true, + 126540 => true, + 126544 => true, + 126547 => true, + 126549 => true, + 126550 => true, + 126552 => true, + 126554 => true, + 126556 => true, + 126558 => true, + 126560 => true, + 126563 => true, + 126565 => true, + 126566 => true, + 126571 => true, + 126579 => true, + 126584 => true, + 126589 => true, + 126591 => true, + 126602 => true, + 126620 => true, + 126621 => true, + 126622 => true, + 126623 => true, + 126624 => true, + 126628 => true, + 126634 => true, + 127020 => true, + 127021 => true, + 127022 => true, + 127023 => true, + 127124 => true, + 127125 => true, + 127126 => true, + 127127 => true, + 127128 => true, + 127129 => true, + 127130 => true, + 127131 => true, + 127132 => true, + 127133 => true, + 127134 => true, + 127135 => true, + 127151 => true, + 127152 => true, + 127168 => true, + 127184 => true, + 127222 => true, + 127223 => true, + 127224 => true, + 127225 => true, + 127226 => true, + 127227 => true, + 127228 => true, + 127229 => true, + 127230 => true, + 127231 => true, + 127232 => true, + 127491 => true, + 127492 => true, + 127493 => true, + 127494 => true, + 127495 => true, + 127496 => true, + 127497 => true, + 127498 => true, + 127499 => true, + 127500 => true, + 127501 => true, + 127502 => true, + 127503 => true, + 127548 => true, + 127549 => true, + 127550 => true, + 127551 => true, + 127561 => true, + 127562 => true, + 127563 => true, + 127564 => true, + 127565 => true, + 127566 => true, + 127567 => true, + 127570 => true, + 127571 => true, + 127572 => true, + 127573 => true, + 127574 => true, + 127575 => true, + 127576 => true, + 127577 => true, + 127578 => true, + 127579 => true, + 127580 => true, + 127581 => true, + 127582 => true, + 127583 => true, + 128728 => true, + 128729 => true, + 128730 => true, + 128731 => true, + 128732 => true, + 128733 => true, + 128734 => true, + 128735 => true, + 128749 => true, + 128750 => true, + 128751 => true, + 128765 => true, + 128766 => true, + 128767 => true, + 128884 => true, + 128885 => true, + 128886 => true, + 128887 => true, + 128888 => true, + 128889 => true, + 128890 => true, + 128891 => true, + 128892 => true, + 128893 => true, + 128894 => true, + 128895 => true, + 128985 => true, + 128986 => true, + 128987 => true, + 128988 => true, + 128989 => true, + 128990 => true, + 128991 => true, + 129004 => true, + 129005 => true, + 129006 => true, + 129007 => true, + 129008 => true, + 129009 => true, + 129010 => true, + 129011 => true, + 129012 => true, + 129013 => true, + 129014 => true, + 129015 => true, + 129016 => true, + 129017 => true, + 129018 => true, + 129019 => true, + 129020 => true, + 129021 => true, + 129022 => true, + 129023 => true, + 129036 => true, + 129037 => true, + 129038 => true, + 129039 => true, + 129096 => true, + 129097 => true, + 129098 => true, + 129099 => true, + 129100 => true, + 129101 => true, + 129102 => true, + 129103 => true, + 129114 => true, + 129115 => true, + 129116 => true, + 129117 => true, + 129118 => true, + 129119 => true, + 129160 => true, + 129161 => true, + 129162 => true, + 129163 => true, + 129164 => true, + 129165 => true, + 129166 => true, + 129167 => true, + 129198 => true, + 129199 => true, + 129401 => true, + 129484 => true, + 129620 => true, + 129621 => true, + 129622 => true, + 129623 => true, + 129624 => true, + 129625 => true, + 129626 => true, + 129627 => true, + 129628 => true, + 129629 => true, + 129630 => true, + 129631 => true, + 129646 => true, + 129647 => true, + 129653 => true, + 129654 => true, + 129655 => true, + 129659 => true, + 129660 => true, + 129661 => true, + 129662 => true, + 129663 => true, + 129671 => true, + 129672 => true, + 129673 => true, + 129674 => true, + 129675 => true, + 129676 => true, + 129677 => true, + 129678 => true, + 129679 => true, + 129705 => true, + 129706 => true, + 129707 => true, + 129708 => true, + 129709 => true, + 129710 => true, + 129711 => true, + 129719 => true, + 129720 => true, + 129721 => true, + 129722 => true, + 129723 => true, + 129724 => true, + 129725 => true, + 129726 => true, + 129727 => true, + 129731 => true, + 129732 => true, + 129733 => true, + 129734 => true, + 129735 => true, + 129736 => true, + 129737 => true, + 129738 => true, + 129739 => true, + 129740 => true, + 129741 => true, + 129742 => true, + 129743 => true, + 129939 => true, + 131070 => true, + 131071 => true, + 177973 => true, + 177974 => true, + 177975 => true, + 177976 => true, + 177977 => true, + 177978 => true, + 177979 => true, + 177980 => true, + 177981 => true, + 177982 => true, + 177983 => true, + 178206 => true, + 178207 => true, + 183970 => true, + 183971 => true, + 183972 => true, + 183973 => true, + 183974 => true, + 183975 => true, + 183976 => true, + 183977 => true, + 183978 => true, + 183979 => true, + 183980 => true, + 183981 => true, + 183982 => true, + 183983 => true, + 194664 => true, + 194676 => true, + 194847 => true, + 194911 => true, + 195007 => true, + 196606 => true, + 196607 => true, + 262142 => true, + 262143 => true, + 327678 => true, + 327679 => true, + 393214 => true, + 393215 => true, + 458750 => true, + 458751 => true, + 524286 => true, + 524287 => true, + 589822 => true, + 589823 => true, + 655358 => true, + 655359 => true, + 720894 => true, + 720895 => true, + 786430 => true, + 786431 => true, + 851966 => true, + 851967 => true, + 917502 => true, + 917503 => true, + 917504 => true, + 917505 => true, + 917506 => true, + 917507 => true, + 917508 => true, + 917509 => true, + 917510 => true, + 917511 => true, + 917512 => true, + 917513 => true, + 917514 => true, + 917515 => true, + 917516 => true, + 917517 => true, + 917518 => true, + 917519 => true, + 917520 => true, + 917521 => true, + 917522 => true, + 917523 => true, + 917524 => true, + 917525 => true, + 917526 => true, + 917527 => true, + 917528 => true, + 917529 => true, + 917530 => true, + 917531 => true, + 917532 => true, + 917533 => true, + 917534 => true, + 917535 => true, + 983038 => true, + 983039 => true, + 1048574 => true, + 1048575 => true, + 1114110 => true, + 1114111 => true, +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_mapped.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_mapped.php new file mode 100644 index 000000000..54f21cc0c --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_mapped.php @@ -0,0 +1,308 @@ + ' ', + 168 => ' ̈', + 175 => ' ̄', + 180 => ' ́', + 184 => ' ̧', + 728 => ' ̆', + 729 => ' ̇', + 730 => ' ̊', + 731 => ' ̨', + 732 => ' ̃', + 733 => ' ̋', + 890 => ' ι', + 894 => ';', + 900 => ' ́', + 901 => ' ̈́', + 8125 => ' ̓', + 8127 => ' ̓', + 8128 => ' ͂', + 8129 => ' ̈͂', + 8141 => ' ̓̀', + 8142 => ' ̓́', + 8143 => ' ̓͂', + 8157 => ' ̔̀', + 8158 => ' ̔́', + 8159 => ' ̔͂', + 8173 => ' ̈̀', + 8174 => ' ̈́', + 8175 => '`', + 8189 => ' ́', + 8190 => ' ̔', + 8192 => ' ', + 8193 => ' ', + 8194 => ' ', + 8195 => ' ', + 8196 => ' ', + 8197 => ' ', + 8198 => ' ', + 8199 => ' ', + 8200 => ' ', + 8201 => ' ', + 8202 => ' ', + 8215 => ' ̳', + 8239 => ' ', + 8252 => '!!', + 8254 => ' ̅', + 8263 => '??', + 8264 => '?!', + 8265 => '!?', + 8287 => ' ', + 8314 => '+', + 8316 => '=', + 8317 => '(', + 8318 => ')', + 8330 => '+', + 8332 => '=', + 8333 => '(', + 8334 => ')', + 8448 => 'a/c', + 8449 => 'a/s', + 8453 => 'c/o', + 8454 => 'c/u', + 9332 => '(1)', + 9333 => '(2)', + 9334 => '(3)', + 9335 => '(4)', + 9336 => '(5)', + 9337 => '(6)', + 9338 => '(7)', + 9339 => '(8)', + 9340 => '(9)', + 9341 => '(10)', + 9342 => '(11)', + 9343 => '(12)', + 9344 => '(13)', + 9345 => '(14)', + 9346 => '(15)', + 9347 => '(16)', + 9348 => '(17)', + 9349 => '(18)', + 9350 => '(19)', + 9351 => '(20)', + 9372 => '(a)', + 9373 => '(b)', + 9374 => '(c)', + 9375 => '(d)', + 9376 => '(e)', + 9377 => '(f)', + 9378 => '(g)', + 9379 => '(h)', + 9380 => '(i)', + 9381 => '(j)', + 9382 => '(k)', + 9383 => '(l)', + 9384 => '(m)', + 9385 => '(n)', + 9386 => '(o)', + 9387 => '(p)', + 9388 => '(q)', + 9389 => '(r)', + 9390 => '(s)', + 9391 => '(t)', + 9392 => '(u)', + 9393 => '(v)', + 9394 => '(w)', + 9395 => '(x)', + 9396 => '(y)', + 9397 => '(z)', + 10868 => '::=', + 10869 => '==', + 10870 => '===', + 12288 => ' ', + 12443 => ' ゙', + 12444 => ' ゚', + 12800 => '(ᄀ)', + 12801 => '(ᄂ)', + 12802 => '(ᄃ)', + 12803 => '(ᄅ)', + 12804 => '(ᄆ)', + 12805 => '(ᄇ)', + 12806 => '(ᄉ)', + 12807 => '(ᄋ)', + 12808 => '(ᄌ)', + 12809 => '(ᄎ)', + 12810 => '(ᄏ)', + 12811 => '(ᄐ)', + 12812 => '(ᄑ)', + 12813 => '(ᄒ)', + 12814 => '(가)', + 12815 => '(나)', + 12816 => '(다)', + 12817 => '(라)', + 12818 => '(마)', + 12819 => '(바)', + 12820 => '(사)', + 12821 => '(아)', + 12822 => '(자)', + 12823 => '(차)', + 12824 => '(카)', + 12825 => '(타)', + 12826 => '(파)', + 12827 => '(하)', + 12828 => '(주)', + 12829 => '(오전)', + 12830 => '(오후)', + 12832 => '(一)', + 12833 => '(二)', + 12834 => '(三)', + 12835 => '(四)', + 12836 => '(五)', + 12837 => '(六)', + 12838 => '(七)', + 12839 => '(八)', + 12840 => '(九)', + 12841 => '(十)', + 12842 => '(月)', + 12843 => '(火)', + 12844 => '(水)', + 12845 => '(木)', + 12846 => '(金)', + 12847 => '(土)', + 12848 => '(日)', + 12849 => '(株)', + 12850 => '(有)', + 12851 => '(社)', + 12852 => '(名)', + 12853 => '(特)', + 12854 => '(財)', + 12855 => '(祝)', + 12856 => '(労)', + 12857 => '(代)', + 12858 => '(呼)', + 12859 => '(学)', + 12860 => '(監)', + 12861 => '(企)', + 12862 => '(資)', + 12863 => '(協)', + 12864 => '(祭)', + 12865 => '(休)', + 12866 => '(自)', + 12867 => '(至)', + 64297 => '+', + 64606 => ' ٌّ', + 64607 => ' ٍّ', + 64608 => ' َّ', + 64609 => ' ُّ', + 64610 => ' ِّ', + 64611 => ' ّٰ', + 65018 => 'صلى الله عليه وسلم', + 65019 => 'جل جلاله', + 65040 => ',', + 65043 => ':', + 65044 => ';', + 65045 => '!', + 65046 => '?', + 65075 => '_', + 65076 => '_', + 65077 => '(', + 65078 => ')', + 65079 => '{', + 65080 => '}', + 65095 => '[', + 65096 => ']', + 65097 => ' ̅', + 65098 => ' ̅', + 65099 => ' ̅', + 65100 => ' ̅', + 65101 => '_', + 65102 => '_', + 65103 => '_', + 65104 => ',', + 65108 => ';', + 65109 => ':', + 65110 => '?', + 65111 => '!', + 65113 => '(', + 65114 => ')', + 65115 => '{', + 65116 => '}', + 65119 => '#', + 65120 => '&', + 65121 => '*', + 65122 => '+', + 65124 => '<', + 65125 => '>', + 65126 => '=', + 65128 => '\\', + 65129 => '$', + 65130 => '%', + 65131 => '@', + 65136 => ' ً', + 65138 => ' ٌ', + 65140 => ' ٍ', + 65142 => ' َ', + 65144 => ' ُ', + 65146 => ' ِ', + 65148 => ' ّ', + 65150 => ' ْ', + 65281 => '!', + 65282 => '"', + 65283 => '#', + 65284 => '$', + 65285 => '%', + 65286 => '&', + 65287 => '\'', + 65288 => '(', + 65289 => ')', + 65290 => '*', + 65291 => '+', + 65292 => ',', + 65295 => '/', + 65306 => ':', + 65307 => ';', + 65308 => '<', + 65309 => '=', + 65310 => '>', + 65311 => '?', + 65312 => '@', + 65339 => '[', + 65340 => '\\', + 65341 => ']', + 65342 => '^', + 65343 => '_', + 65344 => '`', + 65371 => '{', + 65372 => '|', + 65373 => '}', + 65374 => '~', + 65507 => ' ̄', + 127233 => '0,', + 127234 => '1,', + 127235 => '2,', + 127236 => '3,', + 127237 => '4,', + 127238 => '5,', + 127239 => '6,', + 127240 => '7,', + 127241 => '8,', + 127242 => '9,', + 127248 => '(a)', + 127249 => '(b)', + 127250 => '(c)', + 127251 => '(d)', + 127252 => '(e)', + 127253 => '(f)', + 127254 => '(g)', + 127255 => '(h)', + 127256 => '(i)', + 127257 => '(j)', + 127258 => '(k)', + 127259 => '(l)', + 127260 => '(m)', + 127261 => '(n)', + 127262 => '(o)', + 127263 => '(p)', + 127264 => '(q)', + 127265 => '(r)', + 127266 => '(s)', + 127267 => '(t)', + 127268 => '(u)', + 127269 => '(v)', + 127270 => '(w)', + 127271 => '(x)', + 127272 => '(y)', + 127273 => '(z)', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_valid.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_valid.php new file mode 100644 index 000000000..223396ec4 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/disallowed_STD3_valid.php @@ -0,0 +1,71 @@ + true, + 1 => true, + 2 => true, + 3 => true, + 4 => true, + 5 => true, + 6 => true, + 7 => true, + 8 => true, + 9 => true, + 10 => true, + 11 => true, + 12 => true, + 13 => true, + 14 => true, + 15 => true, + 16 => true, + 17 => true, + 18 => true, + 19 => true, + 20 => true, + 21 => true, + 22 => true, + 23 => true, + 24 => true, + 25 => true, + 26 => true, + 27 => true, + 28 => true, + 29 => true, + 30 => true, + 31 => true, + 32 => true, + 33 => true, + 34 => true, + 35 => true, + 36 => true, + 37 => true, + 38 => true, + 39 => true, + 40 => true, + 41 => true, + 42 => true, + 43 => true, + 44 => true, + 47 => true, + 58 => true, + 59 => true, + 60 => true, + 61 => true, + 62 => true, + 63 => true, + 64 => true, + 91 => true, + 92 => true, + 93 => true, + 94 => true, + 95 => true, + 96 => true, + 123 => true, + 124 => true, + 125 => true, + 126 => true, + 127 => true, + 8800 => true, + 8814 => true, + 8815 => true, +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/ignored.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/ignored.php new file mode 100644 index 000000000..b37784413 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/ignored.php @@ -0,0 +1,273 @@ + true, + 847 => true, + 6155 => true, + 6156 => true, + 6157 => true, + 8203 => true, + 8288 => true, + 8292 => true, + 65024 => true, + 65025 => true, + 65026 => true, + 65027 => true, + 65028 => true, + 65029 => true, + 65030 => true, + 65031 => true, + 65032 => true, + 65033 => true, + 65034 => true, + 65035 => true, + 65036 => true, + 65037 => true, + 65038 => true, + 65039 => true, + 65279 => true, + 113824 => true, + 113825 => true, + 113826 => true, + 113827 => true, + 917760 => true, + 917761 => true, + 917762 => true, + 917763 => true, + 917764 => true, + 917765 => true, + 917766 => true, + 917767 => true, + 917768 => true, + 917769 => true, + 917770 => true, + 917771 => true, + 917772 => true, + 917773 => true, + 917774 => true, + 917775 => true, + 917776 => true, + 917777 => true, + 917778 => true, + 917779 => true, + 917780 => true, + 917781 => true, + 917782 => true, + 917783 => true, + 917784 => true, + 917785 => true, + 917786 => true, + 917787 => true, + 917788 => true, + 917789 => true, + 917790 => true, + 917791 => true, + 917792 => true, + 917793 => true, + 917794 => true, + 917795 => true, + 917796 => true, + 917797 => true, + 917798 => true, + 917799 => true, + 917800 => true, + 917801 => true, + 917802 => true, + 917803 => true, + 917804 => true, + 917805 => true, + 917806 => true, + 917807 => true, + 917808 => true, + 917809 => true, + 917810 => true, + 917811 => true, + 917812 => true, + 917813 => true, + 917814 => true, + 917815 => true, + 917816 => true, + 917817 => true, + 917818 => true, + 917819 => true, + 917820 => true, + 917821 => true, + 917822 => true, + 917823 => true, + 917824 => true, + 917825 => true, + 917826 => true, + 917827 => true, + 917828 => true, + 917829 => true, + 917830 => true, + 917831 => true, + 917832 => true, + 917833 => true, + 917834 => true, + 917835 => true, + 917836 => true, + 917837 => true, + 917838 => true, + 917839 => true, + 917840 => true, + 917841 => true, + 917842 => true, + 917843 => true, + 917844 => true, + 917845 => true, + 917846 => true, + 917847 => true, + 917848 => true, + 917849 => true, + 917850 => true, + 917851 => true, + 917852 => true, + 917853 => true, + 917854 => true, + 917855 => true, + 917856 => true, + 917857 => true, + 917858 => true, + 917859 => true, + 917860 => true, + 917861 => true, + 917862 => true, + 917863 => true, + 917864 => true, + 917865 => true, + 917866 => true, + 917867 => true, + 917868 => true, + 917869 => true, + 917870 => true, + 917871 => true, + 917872 => true, + 917873 => true, + 917874 => true, + 917875 => true, + 917876 => true, + 917877 => true, + 917878 => true, + 917879 => true, + 917880 => true, + 917881 => true, + 917882 => true, + 917883 => true, + 917884 => true, + 917885 => true, + 917886 => true, + 917887 => true, + 917888 => true, + 917889 => true, + 917890 => true, + 917891 => true, + 917892 => true, + 917893 => true, + 917894 => true, + 917895 => true, + 917896 => true, + 917897 => true, + 917898 => true, + 917899 => true, + 917900 => true, + 917901 => true, + 917902 => true, + 917903 => true, + 917904 => true, + 917905 => true, + 917906 => true, + 917907 => true, + 917908 => true, + 917909 => true, + 917910 => true, + 917911 => true, + 917912 => true, + 917913 => true, + 917914 => true, + 917915 => true, + 917916 => true, + 917917 => true, + 917918 => true, + 917919 => true, + 917920 => true, + 917921 => true, + 917922 => true, + 917923 => true, + 917924 => true, + 917925 => true, + 917926 => true, + 917927 => true, + 917928 => true, + 917929 => true, + 917930 => true, + 917931 => true, + 917932 => true, + 917933 => true, + 917934 => true, + 917935 => true, + 917936 => true, + 917937 => true, + 917938 => true, + 917939 => true, + 917940 => true, + 917941 => true, + 917942 => true, + 917943 => true, + 917944 => true, + 917945 => true, + 917946 => true, + 917947 => true, + 917948 => true, + 917949 => true, + 917950 => true, + 917951 => true, + 917952 => true, + 917953 => true, + 917954 => true, + 917955 => true, + 917956 => true, + 917957 => true, + 917958 => true, + 917959 => true, + 917960 => true, + 917961 => true, + 917962 => true, + 917963 => true, + 917964 => true, + 917965 => true, + 917966 => true, + 917967 => true, + 917968 => true, + 917969 => true, + 917970 => true, + 917971 => true, + 917972 => true, + 917973 => true, + 917974 => true, + 917975 => true, + 917976 => true, + 917977 => true, + 917978 => true, + 917979 => true, + 917980 => true, + 917981 => true, + 917982 => true, + 917983 => true, + 917984 => true, + 917985 => true, + 917986 => true, + 917987 => true, + 917988 => true, + 917989 => true, + 917990 => true, + 917991 => true, + 917992 => true, + 917993 => true, + 917994 => true, + 917995 => true, + 917996 => true, + 917997 => true, + 917998 => true, + 917999 => true, +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/mapped.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/mapped.php new file mode 100644 index 000000000..9b85fe9d3 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/mapped.php @@ -0,0 +1,5778 @@ + 'a', + 66 => 'b', + 67 => 'c', + 68 => 'd', + 69 => 'e', + 70 => 'f', + 71 => 'g', + 72 => 'h', + 73 => 'i', + 74 => 'j', + 75 => 'k', + 76 => 'l', + 77 => 'm', + 78 => 'n', + 79 => 'o', + 80 => 'p', + 81 => 'q', + 82 => 'r', + 83 => 's', + 84 => 't', + 85 => 'u', + 86 => 'v', + 87 => 'w', + 88 => 'x', + 89 => 'y', + 90 => 'z', + 170 => 'a', + 178 => '2', + 179 => '3', + 181 => 'μ', + 185 => '1', + 186 => 'o', + 188 => '1⁄4', + 189 => '1⁄2', + 190 => '3⁄4', + 192 => 'à', + 193 => 'á', + 194 => 'â', + 195 => 'ã', + 196 => 'ä', + 197 => 'å', + 198 => 'æ', + 199 => 'ç', + 200 => 'è', + 201 => 'é', + 202 => 'ê', + 203 => 'ë', + 204 => 'ì', + 205 => 'í', + 206 => 'î', + 207 => 'ï', + 208 => 'ð', + 209 => 'ñ', + 210 => 'ò', + 211 => 'ó', + 212 => 'ô', + 213 => 'õ', + 214 => 'ö', + 216 => 'ø', + 217 => 'ù', + 218 => 'ú', + 219 => 'û', + 220 => 'ü', + 221 => 'ý', + 222 => 'þ', + 256 => 'ā', + 258 => 'ă', + 260 => 'ą', + 262 => 'ć', + 264 => 'ĉ', + 266 => 'ċ', + 268 => 'č', + 270 => 'ď', + 272 => 'đ', + 274 => 'ē', + 276 => 'ĕ', + 278 => 'ė', + 280 => 'ę', + 282 => 'ě', + 284 => 'ĝ', + 286 => 'ğ', + 288 => 'ġ', + 290 => 'ģ', + 292 => 'ĥ', + 294 => 'ħ', + 296 => 'ĩ', + 298 => 'ī', + 300 => 'ĭ', + 302 => 'į', + 304 => 'i̇', + 306 => 'ij', + 307 => 'ij', + 308 => 'ĵ', + 310 => 'ķ', + 313 => 'ĺ', + 315 => 'ļ', + 317 => 'ľ', + 319 => 'l·', + 320 => 'l·', + 321 => 'ł', + 323 => 'ń', + 325 => 'ņ', + 327 => 'ň', + 329 => 'ʼn', + 330 => 'ŋ', + 332 => 'ō', + 334 => 'ŏ', + 336 => 'ő', + 338 => 'œ', + 340 => 'ŕ', + 342 => 'ŗ', + 344 => 'ř', + 346 => 'ś', + 348 => 'ŝ', + 350 => 'ş', + 352 => 'š', + 354 => 'ţ', + 356 => 'ť', + 358 => 'ŧ', + 360 => 'ũ', + 362 => 'ū', + 364 => 'ŭ', + 366 => 'ů', + 368 => 'ű', + 370 => 'ų', + 372 => 'ŵ', + 374 => 'ŷ', + 376 => 'ÿ', + 377 => 'ź', + 379 => 'ż', + 381 => 'ž', + 383 => 's', + 385 => 'ɓ', + 386 => 'ƃ', + 388 => 'ƅ', + 390 => 'ɔ', + 391 => 'ƈ', + 393 => 'ɖ', + 394 => 'ɗ', + 395 => 'ƌ', + 398 => 'ǝ', + 399 => 'ə', + 400 => 'ɛ', + 401 => 'ƒ', + 403 => 'ɠ', + 404 => 'ɣ', + 406 => 'ɩ', + 407 => 'ɨ', + 408 => 'ƙ', + 412 => 'ɯ', + 413 => 'ɲ', + 415 => 'ɵ', + 416 => 'ơ', + 418 => 'ƣ', + 420 => 'ƥ', + 422 => 'ʀ', + 423 => 'ƨ', + 425 => 'ʃ', + 428 => 'ƭ', + 430 => 'ʈ', + 431 => 'ư', + 433 => 'ʊ', + 434 => 'ʋ', + 435 => 'ƴ', + 437 => 'ƶ', + 439 => 'ʒ', + 440 => 'ƹ', + 444 => 'ƽ', + 452 => 'dž', + 453 => 'dž', + 454 => 'dž', + 455 => 'lj', + 456 => 'lj', + 457 => 'lj', + 458 => 'nj', + 459 => 'nj', + 460 => 'nj', + 461 => 'ǎ', + 463 => 'ǐ', + 465 => 'ǒ', + 467 => 'ǔ', + 469 => 'ǖ', + 471 => 'ǘ', + 473 => 'ǚ', + 475 => 'ǜ', + 478 => 'ǟ', + 480 => 'ǡ', + 482 => 'ǣ', + 484 => 'ǥ', + 486 => 'ǧ', + 488 => 'ǩ', + 490 => 'ǫ', + 492 => 'ǭ', + 494 => 'ǯ', + 497 => 'dz', + 498 => 'dz', + 499 => 'dz', + 500 => 'ǵ', + 502 => 'ƕ', + 503 => 'ƿ', + 504 => 'ǹ', + 506 => 'ǻ', + 508 => 'ǽ', + 510 => 'ǿ', + 512 => 'ȁ', + 514 => 'ȃ', + 516 => 'ȅ', + 518 => 'ȇ', + 520 => 'ȉ', + 522 => 'ȋ', + 524 => 'ȍ', + 526 => 'ȏ', + 528 => 'ȑ', + 530 => 'ȓ', + 532 => 'ȕ', + 534 => 'ȗ', + 536 => 'ș', + 538 => 'ț', + 540 => 'ȝ', + 542 => 'ȟ', + 544 => 'ƞ', + 546 => 'ȣ', + 548 => 'ȥ', + 550 => 'ȧ', + 552 => 'ȩ', + 554 => 'ȫ', + 556 => 'ȭ', + 558 => 'ȯ', + 560 => 'ȱ', + 562 => 'ȳ', + 570 => 'ⱥ', + 571 => 'ȼ', + 573 => 'ƚ', + 574 => 'ⱦ', + 577 => 'ɂ', + 579 => 'ƀ', + 580 => 'ʉ', + 581 => 'ʌ', + 582 => 'ɇ', + 584 => 'ɉ', + 586 => 'ɋ', + 588 => 'ɍ', + 590 => 'ɏ', + 688 => 'h', + 689 => 'ɦ', + 690 => 'j', + 691 => 'r', + 692 => 'ɹ', + 693 => 'ɻ', + 694 => 'ʁ', + 695 => 'w', + 696 => 'y', + 736 => 'ɣ', + 737 => 'l', + 738 => 's', + 739 => 'x', + 740 => 'ʕ', + 832 => '̀', + 833 => '́', + 835 => '̓', + 836 => '̈́', + 837 => 'ι', + 880 => 'ͱ', + 882 => 'ͳ', + 884 => 'ʹ', + 886 => 'ͷ', + 895 => 'ϳ', + 902 => 'ά', + 903 => '·', + 904 => 'έ', + 905 => 'ή', + 906 => 'ί', + 908 => 'ό', + 910 => 'ύ', + 911 => 'ώ', + 913 => 'α', + 914 => 'β', + 915 => 'γ', + 916 => 'δ', + 917 => 'ε', + 918 => 'ζ', + 919 => 'η', + 920 => 'θ', + 921 => 'ι', + 922 => 'κ', + 923 => 'λ', + 924 => 'μ', + 925 => 'ν', + 926 => 'ξ', + 927 => 'ο', + 928 => 'π', + 929 => 'ρ', + 931 => 'σ', + 932 => 'τ', + 933 => 'υ', + 934 => 'φ', + 935 => 'χ', + 936 => 'ψ', + 937 => 'ω', + 938 => 'ϊ', + 939 => 'ϋ', + 975 => 'ϗ', + 976 => 'β', + 977 => 'θ', + 978 => 'υ', + 979 => 'ύ', + 980 => 'ϋ', + 981 => 'φ', + 982 => 'π', + 984 => 'ϙ', + 986 => 'ϛ', + 988 => 'ϝ', + 990 => 'ϟ', + 992 => 'ϡ', + 994 => 'ϣ', + 996 => 'ϥ', + 998 => 'ϧ', + 1000 => 'ϩ', + 1002 => 'ϫ', + 1004 => 'ϭ', + 1006 => 'ϯ', + 1008 => 'κ', + 1009 => 'ρ', + 1010 => 'σ', + 1012 => 'θ', + 1013 => 'ε', + 1015 => 'ϸ', + 1017 => 'σ', + 1018 => 'ϻ', + 1021 => 'ͻ', + 1022 => 'ͼ', + 1023 => 'ͽ', + 1024 => 'ѐ', + 1025 => 'ё', + 1026 => 'ђ', + 1027 => 'ѓ', + 1028 => 'є', + 1029 => 'ѕ', + 1030 => 'і', + 1031 => 'ї', + 1032 => 'ј', + 1033 => 'љ', + 1034 => 'њ', + 1035 => 'ћ', + 1036 => 'ќ', + 1037 => 'ѝ', + 1038 => 'ў', + 1039 => 'џ', + 1040 => 'а', + 1041 => 'б', + 1042 => 'в', + 1043 => 'г', + 1044 => 'д', + 1045 => 'е', + 1046 => 'ж', + 1047 => 'з', + 1048 => 'и', + 1049 => 'й', + 1050 => 'к', + 1051 => 'л', + 1052 => 'м', + 1053 => 'н', + 1054 => 'о', + 1055 => 'п', + 1056 => 'р', + 1057 => 'с', + 1058 => 'т', + 1059 => 'у', + 1060 => 'ф', + 1061 => 'х', + 1062 => 'ц', + 1063 => 'ч', + 1064 => 'ш', + 1065 => 'щ', + 1066 => 'ъ', + 1067 => 'ы', + 1068 => 'ь', + 1069 => 'э', + 1070 => 'ю', + 1071 => 'я', + 1120 => 'ѡ', + 1122 => 'ѣ', + 1124 => 'ѥ', + 1126 => 'ѧ', + 1128 => 'ѩ', + 1130 => 'ѫ', + 1132 => 'ѭ', + 1134 => 'ѯ', + 1136 => 'ѱ', + 1138 => 'ѳ', + 1140 => 'ѵ', + 1142 => 'ѷ', + 1144 => 'ѹ', + 1146 => 'ѻ', + 1148 => 'ѽ', + 1150 => 'ѿ', + 1152 => 'ҁ', + 1162 => 'ҋ', + 1164 => 'ҍ', + 1166 => 'ҏ', + 1168 => 'ґ', + 1170 => 'ғ', + 1172 => 'ҕ', + 1174 => 'җ', + 1176 => 'ҙ', + 1178 => 'қ', + 1180 => 'ҝ', + 1182 => 'ҟ', + 1184 => 'ҡ', + 1186 => 'ң', + 1188 => 'ҥ', + 1190 => 'ҧ', + 1192 => 'ҩ', + 1194 => 'ҫ', + 1196 => 'ҭ', + 1198 => 'ү', + 1200 => 'ұ', + 1202 => 'ҳ', + 1204 => 'ҵ', + 1206 => 'ҷ', + 1208 => 'ҹ', + 1210 => 'һ', + 1212 => 'ҽ', + 1214 => 'ҿ', + 1217 => 'ӂ', + 1219 => 'ӄ', + 1221 => 'ӆ', + 1223 => 'ӈ', + 1225 => 'ӊ', + 1227 => 'ӌ', + 1229 => 'ӎ', + 1232 => 'ӑ', + 1234 => 'ӓ', + 1236 => 'ӕ', + 1238 => 'ӗ', + 1240 => 'ә', + 1242 => 'ӛ', + 1244 => 'ӝ', + 1246 => 'ӟ', + 1248 => 'ӡ', + 1250 => 'ӣ', + 1252 => 'ӥ', + 1254 => 'ӧ', + 1256 => 'ө', + 1258 => 'ӫ', + 1260 => 'ӭ', + 1262 => 'ӯ', + 1264 => 'ӱ', + 1266 => 'ӳ', + 1268 => 'ӵ', + 1270 => 'ӷ', + 1272 => 'ӹ', + 1274 => 'ӻ', + 1276 => 'ӽ', + 1278 => 'ӿ', + 1280 => 'ԁ', + 1282 => 'ԃ', + 1284 => 'ԅ', + 1286 => 'ԇ', + 1288 => 'ԉ', + 1290 => 'ԋ', + 1292 => 'ԍ', + 1294 => 'ԏ', + 1296 => 'ԑ', + 1298 => 'ԓ', + 1300 => 'ԕ', + 1302 => 'ԗ', + 1304 => 'ԙ', + 1306 => 'ԛ', + 1308 => 'ԝ', + 1310 => 'ԟ', + 1312 => 'ԡ', + 1314 => 'ԣ', + 1316 => 'ԥ', + 1318 => 'ԧ', + 1320 => 'ԩ', + 1322 => 'ԫ', + 1324 => 'ԭ', + 1326 => 'ԯ', + 1329 => 'ա', + 1330 => 'բ', + 1331 => 'գ', + 1332 => 'դ', + 1333 => 'ե', + 1334 => 'զ', + 1335 => 'է', + 1336 => 'ը', + 1337 => 'թ', + 1338 => 'ժ', + 1339 => 'ի', + 1340 => 'լ', + 1341 => 'խ', + 1342 => 'ծ', + 1343 => 'կ', + 1344 => 'հ', + 1345 => 'ձ', + 1346 => 'ղ', + 1347 => 'ճ', + 1348 => 'մ', + 1349 => 'յ', + 1350 => 'ն', + 1351 => 'շ', + 1352 => 'ո', + 1353 => 'չ', + 1354 => 'պ', + 1355 => 'ջ', + 1356 => 'ռ', + 1357 => 'ս', + 1358 => 'վ', + 1359 => 'տ', + 1360 => 'ր', + 1361 => 'ց', + 1362 => 'ւ', + 1363 => 'փ', + 1364 => 'ք', + 1365 => 'օ', + 1366 => 'ֆ', + 1415 => 'եւ', + 1653 => 'اٴ', + 1654 => 'وٴ', + 1655 => 'ۇٴ', + 1656 => 'يٴ', + 2392 => 'क़', + 2393 => 'ख़', + 2394 => 'ग़', + 2395 => 'ज़', + 2396 => 'ड़', + 2397 => 'ढ़', + 2398 => 'फ़', + 2399 => 'य़', + 2524 => 'ড়', + 2525 => 'ঢ়', + 2527 => 'য়', + 2611 => 'ਲ਼', + 2614 => 'ਸ਼', + 2649 => 'ਖ਼', + 2650 => 'ਗ਼', + 2651 => 'ਜ਼', + 2654 => 'ਫ਼', + 2908 => 'ଡ଼', + 2909 => 'ଢ଼', + 3635 => 'ํา', + 3763 => 'ໍາ', + 3804 => 'ຫນ', + 3805 => 'ຫມ', + 3852 => '་', + 3907 => 'གྷ', + 3917 => 'ཌྷ', + 3922 => 'དྷ', + 3927 => 'བྷ', + 3932 => 'ཛྷ', + 3945 => 'ཀྵ', + 3955 => 'ཱི', + 3957 => 'ཱུ', + 3958 => 'ྲྀ', + 3959 => 'ྲཱྀ', + 3960 => 'ླྀ', + 3961 => 'ླཱྀ', + 3969 => 'ཱྀ', + 3987 => 'ྒྷ', + 3997 => 'ྜྷ', + 4002 => 'ྡྷ', + 4007 => 'ྦྷ', + 4012 => 'ྫྷ', + 4025 => 'ྐྵ', + 4295 => 'ⴧ', + 4301 => 'ⴭ', + 4348 => 'ნ', + 5112 => 'Ᏸ', + 5113 => 'Ᏹ', + 5114 => 'Ᏺ', + 5115 => 'Ᏻ', + 5116 => 'Ᏼ', + 5117 => 'Ᏽ', + 7296 => 'в', + 7297 => 'д', + 7298 => 'о', + 7299 => 'с', + 7300 => 'т', + 7301 => 'т', + 7302 => 'ъ', + 7303 => 'ѣ', + 7304 => 'ꙋ', + 7312 => 'ა', + 7313 => 'ბ', + 7314 => 'გ', + 7315 => 'დ', + 7316 => 'ე', + 7317 => 'ვ', + 7318 => 'ზ', + 7319 => 'თ', + 7320 => 'ი', + 7321 => 'კ', + 7322 => 'ლ', + 7323 => 'მ', + 7324 => 'ნ', + 7325 => 'ო', + 7326 => 'პ', + 7327 => 'ჟ', + 7328 => 'რ', + 7329 => 'ს', + 7330 => 'ტ', + 7331 => 'უ', + 7332 => 'ფ', + 7333 => 'ქ', + 7334 => 'ღ', + 7335 => 'ყ', + 7336 => 'შ', + 7337 => 'ჩ', + 7338 => 'ც', + 7339 => 'ძ', + 7340 => 'წ', + 7341 => 'ჭ', + 7342 => 'ხ', + 7343 => 'ჯ', + 7344 => 'ჰ', + 7345 => 'ჱ', + 7346 => 'ჲ', + 7347 => 'ჳ', + 7348 => 'ჴ', + 7349 => 'ჵ', + 7350 => 'ჶ', + 7351 => 'ჷ', + 7352 => 'ჸ', + 7353 => 'ჹ', + 7354 => 'ჺ', + 7357 => 'ჽ', + 7358 => 'ჾ', + 7359 => 'ჿ', + 7468 => 'a', + 7469 => 'æ', + 7470 => 'b', + 7472 => 'd', + 7473 => 'e', + 7474 => 'ǝ', + 7475 => 'g', + 7476 => 'h', + 7477 => 'i', + 7478 => 'j', + 7479 => 'k', + 7480 => 'l', + 7481 => 'm', + 7482 => 'n', + 7484 => 'o', + 7485 => 'ȣ', + 7486 => 'p', + 7487 => 'r', + 7488 => 't', + 7489 => 'u', + 7490 => 'w', + 7491 => 'a', + 7492 => 'ɐ', + 7493 => 'ɑ', + 7494 => 'ᴂ', + 7495 => 'b', + 7496 => 'd', + 7497 => 'e', + 7498 => 'ə', + 7499 => 'ɛ', + 7500 => 'ɜ', + 7501 => 'g', + 7503 => 'k', + 7504 => 'm', + 7505 => 'ŋ', + 7506 => 'o', + 7507 => 'ɔ', + 7508 => 'ᴖ', + 7509 => 'ᴗ', + 7510 => 'p', + 7511 => 't', + 7512 => 'u', + 7513 => 'ᴝ', + 7514 => 'ɯ', + 7515 => 'v', + 7516 => 'ᴥ', + 7517 => 'β', + 7518 => 'γ', + 7519 => 'δ', + 7520 => 'φ', + 7521 => 'χ', + 7522 => 'i', + 7523 => 'r', + 7524 => 'u', + 7525 => 'v', + 7526 => 'β', + 7527 => 'γ', + 7528 => 'ρ', + 7529 => 'φ', + 7530 => 'χ', + 7544 => 'н', + 7579 => 'ɒ', + 7580 => 'c', + 7581 => 'ɕ', + 7582 => 'ð', + 7583 => 'ɜ', + 7584 => 'f', + 7585 => 'ɟ', + 7586 => 'ɡ', + 7587 => 'ɥ', + 7588 => 'ɨ', + 7589 => 'ɩ', + 7590 => 'ɪ', + 7591 => 'ᵻ', + 7592 => 'ʝ', + 7593 => 'ɭ', + 7594 => 'ᶅ', + 7595 => 'ʟ', + 7596 => 'ɱ', + 7597 => 'ɰ', + 7598 => 'ɲ', + 7599 => 'ɳ', + 7600 => 'ɴ', + 7601 => 'ɵ', + 7602 => 'ɸ', + 7603 => 'ʂ', + 7604 => 'ʃ', + 7605 => 'ƫ', + 7606 => 'ʉ', + 7607 => 'ʊ', + 7608 => 'ᴜ', + 7609 => 'ʋ', + 7610 => 'ʌ', + 7611 => 'z', + 7612 => 'ʐ', + 7613 => 'ʑ', + 7614 => 'ʒ', + 7615 => 'θ', + 7680 => 'ḁ', + 7682 => 'ḃ', + 7684 => 'ḅ', + 7686 => 'ḇ', + 7688 => 'ḉ', + 7690 => 'ḋ', + 7692 => 'ḍ', + 7694 => 'ḏ', + 7696 => 'ḑ', + 7698 => 'ḓ', + 7700 => 'ḕ', + 7702 => 'ḗ', + 7704 => 'ḙ', + 7706 => 'ḛ', + 7708 => 'ḝ', + 7710 => 'ḟ', + 7712 => 'ḡ', + 7714 => 'ḣ', + 7716 => 'ḥ', + 7718 => 'ḧ', + 7720 => 'ḩ', + 7722 => 'ḫ', + 7724 => 'ḭ', + 7726 => 'ḯ', + 7728 => 'ḱ', + 7730 => 'ḳ', + 7732 => 'ḵ', + 7734 => 'ḷ', + 7736 => 'ḹ', + 7738 => 'ḻ', + 7740 => 'ḽ', + 7742 => 'ḿ', + 7744 => 'ṁ', + 7746 => 'ṃ', + 7748 => 'ṅ', + 7750 => 'ṇ', + 7752 => 'ṉ', + 7754 => 'ṋ', + 7756 => 'ṍ', + 7758 => 'ṏ', + 7760 => 'ṑ', + 7762 => 'ṓ', + 7764 => 'ṕ', + 7766 => 'ṗ', + 7768 => 'ṙ', + 7770 => 'ṛ', + 7772 => 'ṝ', + 7774 => 'ṟ', + 7776 => 'ṡ', + 7778 => 'ṣ', + 7780 => 'ṥ', + 7782 => 'ṧ', + 7784 => 'ṩ', + 7786 => 'ṫ', + 7788 => 'ṭ', + 7790 => 'ṯ', + 7792 => 'ṱ', + 7794 => 'ṳ', + 7796 => 'ṵ', + 7798 => 'ṷ', + 7800 => 'ṹ', + 7802 => 'ṻ', + 7804 => 'ṽ', + 7806 => 'ṿ', + 7808 => 'ẁ', + 7810 => 'ẃ', + 7812 => 'ẅ', + 7814 => 'ẇ', + 7816 => 'ẉ', + 7818 => 'ẋ', + 7820 => 'ẍ', + 7822 => 'ẏ', + 7824 => 'ẑ', + 7826 => 'ẓ', + 7828 => 'ẕ', + 7834 => 'aʾ', + 7835 => 'ṡ', + 7838 => 'ss', + 7840 => 'ạ', + 7842 => 'ả', + 7844 => 'ấ', + 7846 => 'ầ', + 7848 => 'ẩ', + 7850 => 'ẫ', + 7852 => 'ậ', + 7854 => 'ắ', + 7856 => 'ằ', + 7858 => 'ẳ', + 7860 => 'ẵ', + 7862 => 'ặ', + 7864 => 'ẹ', + 7866 => 'ẻ', + 7868 => 'ẽ', + 7870 => 'ế', + 7872 => 'ề', + 7874 => 'ể', + 7876 => 'ễ', + 7878 => 'ệ', + 7880 => 'ỉ', + 7882 => 'ị', + 7884 => 'ọ', + 7886 => 'ỏ', + 7888 => 'ố', + 7890 => 'ồ', + 7892 => 'ổ', + 7894 => 'ỗ', + 7896 => 'ộ', + 7898 => 'ớ', + 7900 => 'ờ', + 7902 => 'ở', + 7904 => 'ỡ', + 7906 => 'ợ', + 7908 => 'ụ', + 7910 => 'ủ', + 7912 => 'ứ', + 7914 => 'ừ', + 7916 => 'ử', + 7918 => 'ữ', + 7920 => 'ự', + 7922 => 'ỳ', + 7924 => 'ỵ', + 7926 => 'ỷ', + 7928 => 'ỹ', + 7930 => 'ỻ', + 7932 => 'ỽ', + 7934 => 'ỿ', + 7944 => 'ἀ', + 7945 => 'ἁ', + 7946 => 'ἂ', + 7947 => 'ἃ', + 7948 => 'ἄ', + 7949 => 'ἅ', + 7950 => 'ἆ', + 7951 => 'ἇ', + 7960 => 'ἐ', + 7961 => 'ἑ', + 7962 => 'ἒ', + 7963 => 'ἓ', + 7964 => 'ἔ', + 7965 => 'ἕ', + 7976 => 'ἠ', + 7977 => 'ἡ', + 7978 => 'ἢ', + 7979 => 'ἣ', + 7980 => 'ἤ', + 7981 => 'ἥ', + 7982 => 'ἦ', + 7983 => 'ἧ', + 7992 => 'ἰ', + 7993 => 'ἱ', + 7994 => 'ἲ', + 7995 => 'ἳ', + 7996 => 'ἴ', + 7997 => 'ἵ', + 7998 => 'ἶ', + 7999 => 'ἷ', + 8008 => 'ὀ', + 8009 => 'ὁ', + 8010 => 'ὂ', + 8011 => 'ὃ', + 8012 => 'ὄ', + 8013 => 'ὅ', + 8025 => 'ὑ', + 8027 => 'ὓ', + 8029 => 'ὕ', + 8031 => 'ὗ', + 8040 => 'ὠ', + 8041 => 'ὡ', + 8042 => 'ὢ', + 8043 => 'ὣ', + 8044 => 'ὤ', + 8045 => 'ὥ', + 8046 => 'ὦ', + 8047 => 'ὧ', + 8049 => 'ά', + 8051 => 'έ', + 8053 => 'ή', + 8055 => 'ί', + 8057 => 'ό', + 8059 => 'ύ', + 8061 => 'ώ', + 8064 => 'ἀι', + 8065 => 'ἁι', + 8066 => 'ἂι', + 8067 => 'ἃι', + 8068 => 'ἄι', + 8069 => 'ἅι', + 8070 => 'ἆι', + 8071 => 'ἇι', + 8072 => 'ἀι', + 8073 => 'ἁι', + 8074 => 'ἂι', + 8075 => 'ἃι', + 8076 => 'ἄι', + 8077 => 'ἅι', + 8078 => 'ἆι', + 8079 => 'ἇι', + 8080 => 'ἠι', + 8081 => 'ἡι', + 8082 => 'ἢι', + 8083 => 'ἣι', + 8084 => 'ἤι', + 8085 => 'ἥι', + 8086 => 'ἦι', + 8087 => 'ἧι', + 8088 => 'ἠι', + 8089 => 'ἡι', + 8090 => 'ἢι', + 8091 => 'ἣι', + 8092 => 'ἤι', + 8093 => 'ἥι', + 8094 => 'ἦι', + 8095 => 'ἧι', + 8096 => 'ὠι', + 8097 => 'ὡι', + 8098 => 'ὢι', + 8099 => 'ὣι', + 8100 => 'ὤι', + 8101 => 'ὥι', + 8102 => 'ὦι', + 8103 => 'ὧι', + 8104 => 'ὠι', + 8105 => 'ὡι', + 8106 => 'ὢι', + 8107 => 'ὣι', + 8108 => 'ὤι', + 8109 => 'ὥι', + 8110 => 'ὦι', + 8111 => 'ὧι', + 8114 => 'ὰι', + 8115 => 'αι', + 8116 => 'άι', + 8119 => 'ᾶι', + 8120 => 'ᾰ', + 8121 => 'ᾱ', + 8122 => 'ὰ', + 8123 => 'ά', + 8124 => 'αι', + 8126 => 'ι', + 8130 => 'ὴι', + 8131 => 'ηι', + 8132 => 'ήι', + 8135 => 'ῆι', + 8136 => 'ὲ', + 8137 => 'έ', + 8138 => 'ὴ', + 8139 => 'ή', + 8140 => 'ηι', + 8147 => 'ΐ', + 8152 => 'ῐ', + 8153 => 'ῑ', + 8154 => 'ὶ', + 8155 => 'ί', + 8163 => 'ΰ', + 8168 => 'ῠ', + 8169 => 'ῡ', + 8170 => 'ὺ', + 8171 => 'ύ', + 8172 => 'ῥ', + 8178 => 'ὼι', + 8179 => 'ωι', + 8180 => 'ώι', + 8183 => 'ῶι', + 8184 => 'ὸ', + 8185 => 'ό', + 8186 => 'ὼ', + 8187 => 'ώ', + 8188 => 'ωι', + 8209 => '‐', + 8243 => '′′', + 8244 => '′′′', + 8246 => '‵‵', + 8247 => '‵‵‵', + 8279 => '′′′′', + 8304 => '0', + 8305 => 'i', + 8308 => '4', + 8309 => '5', + 8310 => '6', + 8311 => '7', + 8312 => '8', + 8313 => '9', + 8315 => '−', + 8319 => 'n', + 8320 => '0', + 8321 => '1', + 8322 => '2', + 8323 => '3', + 8324 => '4', + 8325 => '5', + 8326 => '6', + 8327 => '7', + 8328 => '8', + 8329 => '9', + 8331 => '−', + 8336 => 'a', + 8337 => 'e', + 8338 => 'o', + 8339 => 'x', + 8340 => 'ə', + 8341 => 'h', + 8342 => 'k', + 8343 => 'l', + 8344 => 'm', + 8345 => 'n', + 8346 => 'p', + 8347 => 's', + 8348 => 't', + 8360 => 'rs', + 8450 => 'c', + 8451 => '°c', + 8455 => 'ɛ', + 8457 => '°f', + 8458 => 'g', + 8459 => 'h', + 8460 => 'h', + 8461 => 'h', + 8462 => 'h', + 8463 => 'ħ', + 8464 => 'i', + 8465 => 'i', + 8466 => 'l', + 8467 => 'l', + 8469 => 'n', + 8470 => 'no', + 8473 => 'p', + 8474 => 'q', + 8475 => 'r', + 8476 => 'r', + 8477 => 'r', + 8480 => 'sm', + 8481 => 'tel', + 8482 => 'tm', + 8484 => 'z', + 8486 => 'ω', + 8488 => 'z', + 8490 => 'k', + 8491 => 'å', + 8492 => 'b', + 8493 => 'c', + 8495 => 'e', + 8496 => 'e', + 8497 => 'f', + 8499 => 'm', + 8500 => 'o', + 8501 => 'א', + 8502 => 'ב', + 8503 => 'ג', + 8504 => 'ד', + 8505 => 'i', + 8507 => 'fax', + 8508 => 'π', + 8509 => 'γ', + 8510 => 'γ', + 8511 => 'π', + 8512 => '∑', + 8517 => 'd', + 8518 => 'd', + 8519 => 'e', + 8520 => 'i', + 8521 => 'j', + 8528 => '1⁄7', + 8529 => '1⁄9', + 8530 => '1⁄10', + 8531 => '1⁄3', + 8532 => '2⁄3', + 8533 => '1⁄5', + 8534 => '2⁄5', + 8535 => '3⁄5', + 8536 => '4⁄5', + 8537 => '1⁄6', + 8538 => '5⁄6', + 8539 => '1⁄8', + 8540 => '3⁄8', + 8541 => '5⁄8', + 8542 => '7⁄8', + 8543 => '1⁄', + 8544 => 'i', + 8545 => 'ii', + 8546 => 'iii', + 8547 => 'iv', + 8548 => 'v', + 8549 => 'vi', + 8550 => 'vii', + 8551 => 'viii', + 8552 => 'ix', + 8553 => 'x', + 8554 => 'xi', + 8555 => 'xii', + 8556 => 'l', + 8557 => 'c', + 8558 => 'd', + 8559 => 'm', + 8560 => 'i', + 8561 => 'ii', + 8562 => 'iii', + 8563 => 'iv', + 8564 => 'v', + 8565 => 'vi', + 8566 => 'vii', + 8567 => 'viii', + 8568 => 'ix', + 8569 => 'x', + 8570 => 'xi', + 8571 => 'xii', + 8572 => 'l', + 8573 => 'c', + 8574 => 'd', + 8575 => 'm', + 8585 => '0⁄3', + 8748 => '∫∫', + 8749 => '∫∫∫', + 8751 => '∮∮', + 8752 => '∮∮∮', + 9001 => '〈', + 9002 => '〉', + 9312 => '1', + 9313 => '2', + 9314 => '3', + 9315 => '4', + 9316 => '5', + 9317 => '6', + 9318 => '7', + 9319 => '8', + 9320 => '9', + 9321 => '10', + 9322 => '11', + 9323 => '12', + 9324 => '13', + 9325 => '14', + 9326 => '15', + 9327 => '16', + 9328 => '17', + 9329 => '18', + 9330 => '19', + 9331 => '20', + 9398 => 'a', + 9399 => 'b', + 9400 => 'c', + 9401 => 'd', + 9402 => 'e', + 9403 => 'f', + 9404 => 'g', + 9405 => 'h', + 9406 => 'i', + 9407 => 'j', + 9408 => 'k', + 9409 => 'l', + 9410 => 'm', + 9411 => 'n', + 9412 => 'o', + 9413 => 'p', + 9414 => 'q', + 9415 => 'r', + 9416 => 's', + 9417 => 't', + 9418 => 'u', + 9419 => 'v', + 9420 => 'w', + 9421 => 'x', + 9422 => 'y', + 9423 => 'z', + 9424 => 'a', + 9425 => 'b', + 9426 => 'c', + 9427 => 'd', + 9428 => 'e', + 9429 => 'f', + 9430 => 'g', + 9431 => 'h', + 9432 => 'i', + 9433 => 'j', + 9434 => 'k', + 9435 => 'l', + 9436 => 'm', + 9437 => 'n', + 9438 => 'o', + 9439 => 'p', + 9440 => 'q', + 9441 => 'r', + 9442 => 's', + 9443 => 't', + 9444 => 'u', + 9445 => 'v', + 9446 => 'w', + 9447 => 'x', + 9448 => 'y', + 9449 => 'z', + 9450 => '0', + 10764 => '∫∫∫∫', + 10972 => '⫝̸', + 11264 => 'ⰰ', + 11265 => 'ⰱ', + 11266 => 'ⰲ', + 11267 => 'ⰳ', + 11268 => 'ⰴ', + 11269 => 'ⰵ', + 11270 => 'ⰶ', + 11271 => 'ⰷ', + 11272 => 'ⰸ', + 11273 => 'ⰹ', + 11274 => 'ⰺ', + 11275 => 'ⰻ', + 11276 => 'ⰼ', + 11277 => 'ⰽ', + 11278 => 'ⰾ', + 11279 => 'ⰿ', + 11280 => 'ⱀ', + 11281 => 'ⱁ', + 11282 => 'ⱂ', + 11283 => 'ⱃ', + 11284 => 'ⱄ', + 11285 => 'ⱅ', + 11286 => 'ⱆ', + 11287 => 'ⱇ', + 11288 => 'ⱈ', + 11289 => 'ⱉ', + 11290 => 'ⱊ', + 11291 => 'ⱋ', + 11292 => 'ⱌ', + 11293 => 'ⱍ', + 11294 => 'ⱎ', + 11295 => 'ⱏ', + 11296 => 'ⱐ', + 11297 => 'ⱑ', + 11298 => 'ⱒ', + 11299 => 'ⱓ', + 11300 => 'ⱔ', + 11301 => 'ⱕ', + 11302 => 'ⱖ', + 11303 => 'ⱗ', + 11304 => 'ⱘ', + 11305 => 'ⱙ', + 11306 => 'ⱚ', + 11307 => 'ⱛ', + 11308 => 'ⱜ', + 11309 => 'ⱝ', + 11310 => 'ⱞ', + 11360 => 'ⱡ', + 11362 => 'ɫ', + 11363 => 'ᵽ', + 11364 => 'ɽ', + 11367 => 'ⱨ', + 11369 => 'ⱪ', + 11371 => 'ⱬ', + 11373 => 'ɑ', + 11374 => 'ɱ', + 11375 => 'ɐ', + 11376 => 'ɒ', + 11378 => 'ⱳ', + 11381 => 'ⱶ', + 11388 => 'j', + 11389 => 'v', + 11390 => 'ȿ', + 11391 => 'ɀ', + 11392 => 'ⲁ', + 11394 => 'ⲃ', + 11396 => 'ⲅ', + 11398 => 'ⲇ', + 11400 => 'ⲉ', + 11402 => 'ⲋ', + 11404 => 'ⲍ', + 11406 => 'ⲏ', + 11408 => 'ⲑ', + 11410 => 'ⲓ', + 11412 => 'ⲕ', + 11414 => 'ⲗ', + 11416 => 'ⲙ', + 11418 => 'ⲛ', + 11420 => 'ⲝ', + 11422 => 'ⲟ', + 11424 => 'ⲡ', + 11426 => 'ⲣ', + 11428 => 'ⲥ', + 11430 => 'ⲧ', + 11432 => 'ⲩ', + 11434 => 'ⲫ', + 11436 => 'ⲭ', + 11438 => 'ⲯ', + 11440 => 'ⲱ', + 11442 => 'ⲳ', + 11444 => 'ⲵ', + 11446 => 'ⲷ', + 11448 => 'ⲹ', + 11450 => 'ⲻ', + 11452 => 'ⲽ', + 11454 => 'ⲿ', + 11456 => 'ⳁ', + 11458 => 'ⳃ', + 11460 => 'ⳅ', + 11462 => 'ⳇ', + 11464 => 'ⳉ', + 11466 => 'ⳋ', + 11468 => 'ⳍ', + 11470 => 'ⳏ', + 11472 => 'ⳑ', + 11474 => 'ⳓ', + 11476 => 'ⳕ', + 11478 => 'ⳗ', + 11480 => 'ⳙ', + 11482 => 'ⳛ', + 11484 => 'ⳝ', + 11486 => 'ⳟ', + 11488 => 'ⳡ', + 11490 => 'ⳣ', + 11499 => 'ⳬ', + 11501 => 'ⳮ', + 11506 => 'ⳳ', + 11631 => 'ⵡ', + 11935 => '母', + 12019 => '龟', + 12032 => '一', + 12033 => '丨', + 12034 => '丶', + 12035 => '丿', + 12036 => '乙', + 12037 => '亅', + 12038 => '二', + 12039 => '亠', + 12040 => '人', + 12041 => '儿', + 12042 => '入', + 12043 => '八', + 12044 => '冂', + 12045 => '冖', + 12046 => '冫', + 12047 => '几', + 12048 => '凵', + 12049 => '刀', + 12050 => '力', + 12051 => '勹', + 12052 => '匕', + 12053 => '匚', + 12054 => '匸', + 12055 => '十', + 12056 => '卜', + 12057 => '卩', + 12058 => '厂', + 12059 => '厶', + 12060 => '又', + 12061 => '口', + 12062 => '囗', + 12063 => '土', + 12064 => '士', + 12065 => '夂', + 12066 => '夊', + 12067 => '夕', + 12068 => '大', + 12069 => '女', + 12070 => '子', + 12071 => '宀', + 12072 => '寸', + 12073 => '小', + 12074 => '尢', + 12075 => '尸', + 12076 => '屮', + 12077 => '山', + 12078 => '巛', + 12079 => '工', + 12080 => '己', + 12081 => '巾', + 12082 => '干', + 12083 => '幺', + 12084 => '广', + 12085 => '廴', + 12086 => '廾', + 12087 => '弋', + 12088 => '弓', + 12089 => '彐', + 12090 => '彡', + 12091 => '彳', + 12092 => '心', + 12093 => '戈', + 12094 => '戶', + 12095 => '手', + 12096 => '支', + 12097 => '攴', + 12098 => '文', + 12099 => '斗', + 12100 => '斤', + 12101 => '方', + 12102 => '无', + 12103 => '日', + 12104 => '曰', + 12105 => '月', + 12106 => '木', + 12107 => '欠', + 12108 => '止', + 12109 => '歹', + 12110 => '殳', + 12111 => '毋', + 12112 => '比', + 12113 => '毛', + 12114 => '氏', + 12115 => '气', + 12116 => '水', + 12117 => '火', + 12118 => '爪', + 12119 => '父', + 12120 => '爻', + 12121 => '爿', + 12122 => '片', + 12123 => '牙', + 12124 => '牛', + 12125 => '犬', + 12126 => '玄', + 12127 => '玉', + 12128 => '瓜', + 12129 => '瓦', + 12130 => '甘', + 12131 => '生', + 12132 => '用', + 12133 => '田', + 12134 => '疋', + 12135 => '疒', + 12136 => '癶', + 12137 => '白', + 12138 => '皮', + 12139 => '皿', + 12140 => '目', + 12141 => '矛', + 12142 => '矢', + 12143 => '石', + 12144 => '示', + 12145 => '禸', + 12146 => '禾', + 12147 => '穴', + 12148 => '立', + 12149 => '竹', + 12150 => '米', + 12151 => '糸', + 12152 => '缶', + 12153 => '网', + 12154 => '羊', + 12155 => '羽', + 12156 => '老', + 12157 => '而', + 12158 => '耒', + 12159 => '耳', + 12160 => '聿', + 12161 => '肉', + 12162 => '臣', + 12163 => '自', + 12164 => '至', + 12165 => '臼', + 12166 => '舌', + 12167 => '舛', + 12168 => '舟', + 12169 => '艮', + 12170 => '色', + 12171 => '艸', + 12172 => '虍', + 12173 => '虫', + 12174 => '血', + 12175 => '行', + 12176 => '衣', + 12177 => '襾', + 12178 => '見', + 12179 => '角', + 12180 => '言', + 12181 => '谷', + 12182 => '豆', + 12183 => '豕', + 12184 => '豸', + 12185 => '貝', + 12186 => '赤', + 12187 => '走', + 12188 => '足', + 12189 => '身', + 12190 => '車', + 12191 => '辛', + 12192 => '辰', + 12193 => '辵', + 12194 => '邑', + 12195 => '酉', + 12196 => '釆', + 12197 => '里', + 12198 => '金', + 12199 => '長', + 12200 => '門', + 12201 => '阜', + 12202 => '隶', + 12203 => '隹', + 12204 => '雨', + 12205 => '靑', + 12206 => '非', + 12207 => '面', + 12208 => '革', + 12209 => '韋', + 12210 => '韭', + 12211 => '音', + 12212 => '頁', + 12213 => '風', + 12214 => '飛', + 12215 => '食', + 12216 => '首', + 12217 => '香', + 12218 => '馬', + 12219 => '骨', + 12220 => '高', + 12221 => '髟', + 12222 => '鬥', + 12223 => '鬯', + 12224 => '鬲', + 12225 => '鬼', + 12226 => '魚', + 12227 => '鳥', + 12228 => '鹵', + 12229 => '鹿', + 12230 => '麥', + 12231 => '麻', + 12232 => '黃', + 12233 => '黍', + 12234 => '黑', + 12235 => '黹', + 12236 => '黽', + 12237 => '鼎', + 12238 => '鼓', + 12239 => '鼠', + 12240 => '鼻', + 12241 => '齊', + 12242 => '齒', + 12243 => '龍', + 12244 => '龜', + 12245 => '龠', + 12290 => '.', + 12342 => '〒', + 12344 => '十', + 12345 => '卄', + 12346 => '卅', + 12447 => 'より', + 12543 => 'コト', + 12593 => 'ᄀ', + 12594 => 'ᄁ', + 12595 => 'ᆪ', + 12596 => 'ᄂ', + 12597 => 'ᆬ', + 12598 => 'ᆭ', + 12599 => 'ᄃ', + 12600 => 'ᄄ', + 12601 => 'ᄅ', + 12602 => 'ᆰ', + 12603 => 'ᆱ', + 12604 => 'ᆲ', + 12605 => 'ᆳ', + 12606 => 'ᆴ', + 12607 => 'ᆵ', + 12608 => 'ᄚ', + 12609 => 'ᄆ', + 12610 => 'ᄇ', + 12611 => 'ᄈ', + 12612 => 'ᄡ', + 12613 => 'ᄉ', + 12614 => 'ᄊ', + 12615 => 'ᄋ', + 12616 => 'ᄌ', + 12617 => 'ᄍ', + 12618 => 'ᄎ', + 12619 => 'ᄏ', + 12620 => 'ᄐ', + 12621 => 'ᄑ', + 12622 => 'ᄒ', + 12623 => 'ᅡ', + 12624 => 'ᅢ', + 12625 => 'ᅣ', + 12626 => 'ᅤ', + 12627 => 'ᅥ', + 12628 => 'ᅦ', + 12629 => 'ᅧ', + 12630 => 'ᅨ', + 12631 => 'ᅩ', + 12632 => 'ᅪ', + 12633 => 'ᅫ', + 12634 => 'ᅬ', + 12635 => 'ᅭ', + 12636 => 'ᅮ', + 12637 => 'ᅯ', + 12638 => 'ᅰ', + 12639 => 'ᅱ', + 12640 => 'ᅲ', + 12641 => 'ᅳ', + 12642 => 'ᅴ', + 12643 => 'ᅵ', + 12645 => 'ᄔ', + 12646 => 'ᄕ', + 12647 => 'ᇇ', + 12648 => 'ᇈ', + 12649 => 'ᇌ', + 12650 => 'ᇎ', + 12651 => 'ᇓ', + 12652 => 'ᇗ', + 12653 => 'ᇙ', + 12654 => 'ᄜ', + 12655 => 'ᇝ', + 12656 => 'ᇟ', + 12657 => 'ᄝ', + 12658 => 'ᄞ', + 12659 => 'ᄠ', + 12660 => 'ᄢ', + 12661 => 'ᄣ', + 12662 => 'ᄧ', + 12663 => 'ᄩ', + 12664 => 'ᄫ', + 12665 => 'ᄬ', + 12666 => 'ᄭ', + 12667 => 'ᄮ', + 12668 => 'ᄯ', + 12669 => 'ᄲ', + 12670 => 'ᄶ', + 12671 => 'ᅀ', + 12672 => 'ᅇ', + 12673 => 'ᅌ', + 12674 => 'ᇱ', + 12675 => 'ᇲ', + 12676 => 'ᅗ', + 12677 => 'ᅘ', + 12678 => 'ᅙ', + 12679 => 'ᆄ', + 12680 => 'ᆅ', + 12681 => 'ᆈ', + 12682 => 'ᆑ', + 12683 => 'ᆒ', + 12684 => 'ᆔ', + 12685 => 'ᆞ', + 12686 => 'ᆡ', + 12690 => '一', + 12691 => '二', + 12692 => '三', + 12693 => '四', + 12694 => '上', + 12695 => '中', + 12696 => '下', + 12697 => '甲', + 12698 => '乙', + 12699 => '丙', + 12700 => '丁', + 12701 => '天', + 12702 => '地', + 12703 => '人', + 12868 => '問', + 12869 => '幼', + 12870 => '文', + 12871 => '箏', + 12880 => 'pte', + 12881 => '21', + 12882 => '22', + 12883 => '23', + 12884 => '24', + 12885 => '25', + 12886 => '26', + 12887 => '27', + 12888 => '28', + 12889 => '29', + 12890 => '30', + 12891 => '31', + 12892 => '32', + 12893 => '33', + 12894 => '34', + 12895 => '35', + 12896 => 'ᄀ', + 12897 => 'ᄂ', + 12898 => 'ᄃ', + 12899 => 'ᄅ', + 12900 => 'ᄆ', + 12901 => 'ᄇ', + 12902 => 'ᄉ', + 12903 => 'ᄋ', + 12904 => 'ᄌ', + 12905 => 'ᄎ', + 12906 => 'ᄏ', + 12907 => 'ᄐ', + 12908 => 'ᄑ', + 12909 => 'ᄒ', + 12910 => '가', + 12911 => '나', + 12912 => '다', + 12913 => '라', + 12914 => '마', + 12915 => '바', + 12916 => '사', + 12917 => '아', + 12918 => '자', + 12919 => '차', + 12920 => '카', + 12921 => '타', + 12922 => '파', + 12923 => '하', + 12924 => '참고', + 12925 => '주의', + 12926 => '우', + 12928 => '一', + 12929 => '二', + 12930 => '三', + 12931 => '四', + 12932 => '五', + 12933 => '六', + 12934 => '七', + 12935 => '八', + 12936 => '九', + 12937 => '十', + 12938 => '月', + 12939 => '火', + 12940 => '水', + 12941 => '木', + 12942 => '金', + 12943 => '土', + 12944 => '日', + 12945 => '株', + 12946 => '有', + 12947 => '社', + 12948 => '名', + 12949 => '特', + 12950 => '財', + 12951 => '祝', + 12952 => '労', + 12953 => '秘', + 12954 => '男', + 12955 => '女', + 12956 => '適', + 12957 => '優', + 12958 => '印', + 12959 => '注', + 12960 => '項', + 12961 => '休', + 12962 => '写', + 12963 => '正', + 12964 => '上', + 12965 => '中', + 12966 => '下', + 12967 => '左', + 12968 => '右', + 12969 => '医', + 12970 => '宗', + 12971 => '学', + 12972 => '監', + 12973 => '企', + 12974 => '資', + 12975 => '協', + 12976 => '夜', + 12977 => '36', + 12978 => '37', + 12979 => '38', + 12980 => '39', + 12981 => '40', + 12982 => '41', + 12983 => '42', + 12984 => '43', + 12985 => '44', + 12986 => '45', + 12987 => '46', + 12988 => '47', + 12989 => '48', + 12990 => '49', + 12991 => '50', + 12992 => '1月', + 12993 => '2月', + 12994 => '3月', + 12995 => '4月', + 12996 => '5月', + 12997 => '6月', + 12998 => '7月', + 12999 => '8月', + 13000 => '9月', + 13001 => '10月', + 13002 => '11月', + 13003 => '12月', + 13004 => 'hg', + 13005 => 'erg', + 13006 => 'ev', + 13007 => 'ltd', + 13008 => 'ア', + 13009 => 'イ', + 13010 => 'ウ', + 13011 => 'エ', + 13012 => 'オ', + 13013 => 'カ', + 13014 => 'キ', + 13015 => 'ク', + 13016 => 'ケ', + 13017 => 'コ', + 13018 => 'サ', + 13019 => 'シ', + 13020 => 'ス', + 13021 => 'セ', + 13022 => 'ソ', + 13023 => 'タ', + 13024 => 'チ', + 13025 => 'ツ', + 13026 => 'テ', + 13027 => 'ト', + 13028 => 'ナ', + 13029 => 'ニ', + 13030 => 'ヌ', + 13031 => 'ネ', + 13032 => 'ノ', + 13033 => 'ハ', + 13034 => 'ヒ', + 13035 => 'フ', + 13036 => 'ヘ', + 13037 => 'ホ', + 13038 => 'マ', + 13039 => 'ミ', + 13040 => 'ム', + 13041 => 'メ', + 13042 => 'モ', + 13043 => 'ヤ', + 13044 => 'ユ', + 13045 => 'ヨ', + 13046 => 'ラ', + 13047 => 'リ', + 13048 => 'ル', + 13049 => 'レ', + 13050 => 'ロ', + 13051 => 'ワ', + 13052 => 'ヰ', + 13053 => 'ヱ', + 13054 => 'ヲ', + 13055 => '令和', + 13056 => 'アパート', + 13057 => 'アルファ', + 13058 => 'アンペア', + 13059 => 'アール', + 13060 => 'イニング', + 13061 => 'インチ', + 13062 => 'ウォン', + 13063 => 'エスクード', + 13064 => 'エーカー', + 13065 => 'オンス', + 13066 => 'オーム', + 13067 => 'カイリ', + 13068 => 'カラット', + 13069 => 'カロリー', + 13070 => 'ガロン', + 13071 => 'ガンマ', + 13072 => 'ギガ', + 13073 => 'ギニー', + 13074 => 'キュリー', + 13075 => 'ギルダー', + 13076 => 'キロ', + 13077 => 'キログラム', + 13078 => 'キロメートル', + 13079 => 'キロワット', + 13080 => 'グラム', + 13081 => 'グラムトン', + 13082 => 'クルゼイロ', + 13083 => 'クローネ', + 13084 => 'ケース', + 13085 => 'コルナ', + 13086 => 'コーポ', + 13087 => 'サイクル', + 13088 => 'サンチーム', + 13089 => 'シリング', + 13090 => 'センチ', + 13091 => 'セント', + 13092 => 'ダース', + 13093 => 'デシ', + 13094 => 'ドル', + 13095 => 'トン', + 13096 => 'ナノ', + 13097 => 'ノット', + 13098 => 'ハイツ', + 13099 => 'パーセント', + 13100 => 'パーツ', + 13101 => 'バーレル', + 13102 => 'ピアストル', + 13103 => 'ピクル', + 13104 => 'ピコ', + 13105 => 'ビル', + 13106 => 'ファラッド', + 13107 => 'フィート', + 13108 => 'ブッシェル', + 13109 => 'フラン', + 13110 => 'ヘクタール', + 13111 => 'ペソ', + 13112 => 'ペニヒ', + 13113 => 'ヘルツ', + 13114 => 'ペンス', + 13115 => 'ページ', + 13116 => 'ベータ', + 13117 => 'ポイント', + 13118 => 'ボルト', + 13119 => 'ホン', + 13120 => 'ポンド', + 13121 => 'ホール', + 13122 => 'ホーン', + 13123 => 'マイクロ', + 13124 => 'マイル', + 13125 => 'マッハ', + 13126 => 'マルク', + 13127 => 'マンション', + 13128 => 'ミクロン', + 13129 => 'ミリ', + 13130 => 'ミリバール', + 13131 => 'メガ', + 13132 => 'メガトン', + 13133 => 'メートル', + 13134 => 'ヤード', + 13135 => 'ヤール', + 13136 => 'ユアン', + 13137 => 'リットル', + 13138 => 'リラ', + 13139 => 'ルピー', + 13140 => 'ルーブル', + 13141 => 'レム', + 13142 => 'レントゲン', + 13143 => 'ワット', + 13144 => '0点', + 13145 => '1点', + 13146 => '2点', + 13147 => '3点', + 13148 => '4点', + 13149 => '5点', + 13150 => '6点', + 13151 => '7点', + 13152 => '8点', + 13153 => '9点', + 13154 => '10点', + 13155 => '11点', + 13156 => '12点', + 13157 => '13点', + 13158 => '14点', + 13159 => '15点', + 13160 => '16点', + 13161 => '17点', + 13162 => '18点', + 13163 => '19点', + 13164 => '20点', + 13165 => '21点', + 13166 => '22点', + 13167 => '23点', + 13168 => '24点', + 13169 => 'hpa', + 13170 => 'da', + 13171 => 'au', + 13172 => 'bar', + 13173 => 'ov', + 13174 => 'pc', + 13175 => 'dm', + 13176 => 'dm2', + 13177 => 'dm3', + 13178 => 'iu', + 13179 => '平成', + 13180 => '昭和', + 13181 => '大正', + 13182 => '明治', + 13183 => '株式会社', + 13184 => 'pa', + 13185 => 'na', + 13186 => 'μa', + 13187 => 'ma', + 13188 => 'ka', + 13189 => 'kb', + 13190 => 'mb', + 13191 => 'gb', + 13192 => 'cal', + 13193 => 'kcal', + 13194 => 'pf', + 13195 => 'nf', + 13196 => 'μf', + 13197 => 'μg', + 13198 => 'mg', + 13199 => 'kg', + 13200 => 'hz', + 13201 => 'khz', + 13202 => 'mhz', + 13203 => 'ghz', + 13204 => 'thz', + 13205 => 'μl', + 13206 => 'ml', + 13207 => 'dl', + 13208 => 'kl', + 13209 => 'fm', + 13210 => 'nm', + 13211 => 'μm', + 13212 => 'mm', + 13213 => 'cm', + 13214 => 'km', + 13215 => 'mm2', + 13216 => 'cm2', + 13217 => 'm2', + 13218 => 'km2', + 13219 => 'mm3', + 13220 => 'cm3', + 13221 => 'm3', + 13222 => 'km3', + 13223 => 'm∕s', + 13224 => 'm∕s2', + 13225 => 'pa', + 13226 => 'kpa', + 13227 => 'mpa', + 13228 => 'gpa', + 13229 => 'rad', + 13230 => 'rad∕s', + 13231 => 'rad∕s2', + 13232 => 'ps', + 13233 => 'ns', + 13234 => 'μs', + 13235 => 'ms', + 13236 => 'pv', + 13237 => 'nv', + 13238 => 'μv', + 13239 => 'mv', + 13240 => 'kv', + 13241 => 'mv', + 13242 => 'pw', + 13243 => 'nw', + 13244 => 'μw', + 13245 => 'mw', + 13246 => 'kw', + 13247 => 'mw', + 13248 => 'kω', + 13249 => 'mω', + 13251 => 'bq', + 13252 => 'cc', + 13253 => 'cd', + 13254 => 'c∕kg', + 13256 => 'db', + 13257 => 'gy', + 13258 => 'ha', + 13259 => 'hp', + 13260 => 'in', + 13261 => 'kk', + 13262 => 'km', + 13263 => 'kt', + 13264 => 'lm', + 13265 => 'ln', + 13266 => 'log', + 13267 => 'lx', + 13268 => 'mb', + 13269 => 'mil', + 13270 => 'mol', + 13271 => 'ph', + 13273 => 'ppm', + 13274 => 'pr', + 13275 => 'sr', + 13276 => 'sv', + 13277 => 'wb', + 13278 => 'v∕m', + 13279 => 'a∕m', + 13280 => '1日', + 13281 => '2日', + 13282 => '3日', + 13283 => '4日', + 13284 => '5日', + 13285 => '6日', + 13286 => '7日', + 13287 => '8日', + 13288 => '9日', + 13289 => '10日', + 13290 => '11日', + 13291 => '12日', + 13292 => '13日', + 13293 => '14日', + 13294 => '15日', + 13295 => '16日', + 13296 => '17日', + 13297 => '18日', + 13298 => '19日', + 13299 => '20日', + 13300 => '21日', + 13301 => '22日', + 13302 => '23日', + 13303 => '24日', + 13304 => '25日', + 13305 => '26日', + 13306 => '27日', + 13307 => '28日', + 13308 => '29日', + 13309 => '30日', + 13310 => '31日', + 13311 => 'gal', + 42560 => 'ꙁ', + 42562 => 'ꙃ', + 42564 => 'ꙅ', + 42566 => 'ꙇ', + 42568 => 'ꙉ', + 42570 => 'ꙋ', + 42572 => 'ꙍ', + 42574 => 'ꙏ', + 42576 => 'ꙑ', + 42578 => 'ꙓ', + 42580 => 'ꙕ', + 42582 => 'ꙗ', + 42584 => 'ꙙ', + 42586 => 'ꙛ', + 42588 => 'ꙝ', + 42590 => 'ꙟ', + 42592 => 'ꙡ', + 42594 => 'ꙣ', + 42596 => 'ꙥ', + 42598 => 'ꙧ', + 42600 => 'ꙩ', + 42602 => 'ꙫ', + 42604 => 'ꙭ', + 42624 => 'ꚁ', + 42626 => 'ꚃ', + 42628 => 'ꚅ', + 42630 => 'ꚇ', + 42632 => 'ꚉ', + 42634 => 'ꚋ', + 42636 => 'ꚍ', + 42638 => 'ꚏ', + 42640 => 'ꚑ', + 42642 => 'ꚓ', + 42644 => 'ꚕ', + 42646 => 'ꚗ', + 42648 => 'ꚙ', + 42650 => 'ꚛ', + 42652 => 'ъ', + 42653 => 'ь', + 42786 => 'ꜣ', + 42788 => 'ꜥ', + 42790 => 'ꜧ', + 42792 => 'ꜩ', + 42794 => 'ꜫ', + 42796 => 'ꜭ', + 42798 => 'ꜯ', + 42802 => 'ꜳ', + 42804 => 'ꜵ', + 42806 => 'ꜷ', + 42808 => 'ꜹ', + 42810 => 'ꜻ', + 42812 => 'ꜽ', + 42814 => 'ꜿ', + 42816 => 'ꝁ', + 42818 => 'ꝃ', + 42820 => 'ꝅ', + 42822 => 'ꝇ', + 42824 => 'ꝉ', + 42826 => 'ꝋ', + 42828 => 'ꝍ', + 42830 => 'ꝏ', + 42832 => 'ꝑ', + 42834 => 'ꝓ', + 42836 => 'ꝕ', + 42838 => 'ꝗ', + 42840 => 'ꝙ', + 42842 => 'ꝛ', + 42844 => 'ꝝ', + 42846 => 'ꝟ', + 42848 => 'ꝡ', + 42850 => 'ꝣ', + 42852 => 'ꝥ', + 42854 => 'ꝧ', + 42856 => 'ꝩ', + 42858 => 'ꝫ', + 42860 => 'ꝭ', + 42862 => 'ꝯ', + 42864 => 'ꝯ', + 42873 => 'ꝺ', + 42875 => 'ꝼ', + 42877 => 'ᵹ', + 42878 => 'ꝿ', + 42880 => 'ꞁ', + 42882 => 'ꞃ', + 42884 => 'ꞅ', + 42886 => 'ꞇ', + 42891 => 'ꞌ', + 42893 => 'ɥ', + 42896 => 'ꞑ', + 42898 => 'ꞓ', + 42902 => 'ꞗ', + 42904 => 'ꞙ', + 42906 => 'ꞛ', + 42908 => 'ꞝ', + 42910 => 'ꞟ', + 42912 => 'ꞡ', + 42914 => 'ꞣ', + 42916 => 'ꞥ', + 42918 => 'ꞧ', + 42920 => 'ꞩ', + 42922 => 'ɦ', + 42923 => 'ɜ', + 42924 => 'ɡ', + 42925 => 'ɬ', + 42926 => 'ɪ', + 42928 => 'ʞ', + 42929 => 'ʇ', + 42930 => 'ʝ', + 42931 => 'ꭓ', + 42932 => 'ꞵ', + 42934 => 'ꞷ', + 42936 => 'ꞹ', + 42938 => 'ꞻ', + 42940 => 'ꞽ', + 42942 => 'ꞿ', + 42946 => 'ꟃ', + 42948 => 'ꞔ', + 42949 => 'ʂ', + 42950 => 'ᶎ', + 42951 => 'ꟈ', + 42953 => 'ꟊ', + 42997 => 'ꟶ', + 43000 => 'ħ', + 43001 => 'œ', + 43868 => 'ꜧ', + 43869 => 'ꬷ', + 43870 => 'ɫ', + 43871 => 'ꭒ', + 43881 => 'ʍ', + 43888 => 'Ꭰ', + 43889 => 'Ꭱ', + 43890 => 'Ꭲ', + 43891 => 'Ꭳ', + 43892 => 'Ꭴ', + 43893 => 'Ꭵ', + 43894 => 'Ꭶ', + 43895 => 'Ꭷ', + 43896 => 'Ꭸ', + 43897 => 'Ꭹ', + 43898 => 'Ꭺ', + 43899 => 'Ꭻ', + 43900 => 'Ꭼ', + 43901 => 'Ꭽ', + 43902 => 'Ꭾ', + 43903 => 'Ꭿ', + 43904 => 'Ꮀ', + 43905 => 'Ꮁ', + 43906 => 'Ꮂ', + 43907 => 'Ꮃ', + 43908 => 'Ꮄ', + 43909 => 'Ꮅ', + 43910 => 'Ꮆ', + 43911 => 'Ꮇ', + 43912 => 'Ꮈ', + 43913 => 'Ꮉ', + 43914 => 'Ꮊ', + 43915 => 'Ꮋ', + 43916 => 'Ꮌ', + 43917 => 'Ꮍ', + 43918 => 'Ꮎ', + 43919 => 'Ꮏ', + 43920 => 'Ꮐ', + 43921 => 'Ꮑ', + 43922 => 'Ꮒ', + 43923 => 'Ꮓ', + 43924 => 'Ꮔ', + 43925 => 'Ꮕ', + 43926 => 'Ꮖ', + 43927 => 'Ꮗ', + 43928 => 'Ꮘ', + 43929 => 'Ꮙ', + 43930 => 'Ꮚ', + 43931 => 'Ꮛ', + 43932 => 'Ꮜ', + 43933 => 'Ꮝ', + 43934 => 'Ꮞ', + 43935 => 'Ꮟ', + 43936 => 'Ꮠ', + 43937 => 'Ꮡ', + 43938 => 'Ꮢ', + 43939 => 'Ꮣ', + 43940 => 'Ꮤ', + 43941 => 'Ꮥ', + 43942 => 'Ꮦ', + 43943 => 'Ꮧ', + 43944 => 'Ꮨ', + 43945 => 'Ꮩ', + 43946 => 'Ꮪ', + 43947 => 'Ꮫ', + 43948 => 'Ꮬ', + 43949 => 'Ꮭ', + 43950 => 'Ꮮ', + 43951 => 'Ꮯ', + 43952 => 'Ꮰ', + 43953 => 'Ꮱ', + 43954 => 'Ꮲ', + 43955 => 'Ꮳ', + 43956 => 'Ꮴ', + 43957 => 'Ꮵ', + 43958 => 'Ꮶ', + 43959 => 'Ꮷ', + 43960 => 'Ꮸ', + 43961 => 'Ꮹ', + 43962 => 'Ꮺ', + 43963 => 'Ꮻ', + 43964 => 'Ꮼ', + 43965 => 'Ꮽ', + 43966 => 'Ꮾ', + 43967 => 'Ꮿ', + 63744 => '豈', + 63745 => '更', + 63746 => '車', + 63747 => '賈', + 63748 => '滑', + 63749 => '串', + 63750 => '句', + 63751 => '龜', + 63752 => '龜', + 63753 => '契', + 63754 => '金', + 63755 => '喇', + 63756 => '奈', + 63757 => '懶', + 63758 => '癩', + 63759 => '羅', + 63760 => '蘿', + 63761 => '螺', + 63762 => '裸', + 63763 => '邏', + 63764 => '樂', + 63765 => '洛', + 63766 => '烙', + 63767 => '珞', + 63768 => '落', + 63769 => '酪', + 63770 => '駱', + 63771 => '亂', + 63772 => '卵', + 63773 => '欄', + 63774 => '爛', + 63775 => '蘭', + 63776 => '鸞', + 63777 => '嵐', + 63778 => '濫', + 63779 => '藍', + 63780 => '襤', + 63781 => '拉', + 63782 => '臘', + 63783 => '蠟', + 63784 => '廊', + 63785 => '朗', + 63786 => '浪', + 63787 => '狼', + 63788 => '郎', + 63789 => '來', + 63790 => '冷', + 63791 => '勞', + 63792 => '擄', + 63793 => '櫓', + 63794 => '爐', + 63795 => '盧', + 63796 => '老', + 63797 => '蘆', + 63798 => '虜', + 63799 => '路', + 63800 => '露', + 63801 => '魯', + 63802 => '鷺', + 63803 => '碌', + 63804 => '祿', + 63805 => '綠', + 63806 => '菉', + 63807 => '錄', + 63808 => '鹿', + 63809 => '論', + 63810 => '壟', + 63811 => '弄', + 63812 => '籠', + 63813 => '聾', + 63814 => '牢', + 63815 => '磊', + 63816 => '賂', + 63817 => '雷', + 63818 => '壘', + 63819 => '屢', + 63820 => '樓', + 63821 => '淚', + 63822 => '漏', + 63823 => '累', + 63824 => '縷', + 63825 => '陋', + 63826 => '勒', + 63827 => '肋', + 63828 => '凜', + 63829 => '凌', + 63830 => '稜', + 63831 => '綾', + 63832 => '菱', + 63833 => '陵', + 63834 => '讀', + 63835 => '拏', + 63836 => '樂', + 63837 => '諾', + 63838 => '丹', + 63839 => '寧', + 63840 => '怒', + 63841 => '率', + 63842 => '異', + 63843 => '北', + 63844 => '磻', + 63845 => '便', + 63846 => '復', + 63847 => '不', + 63848 => '泌', + 63849 => '數', + 63850 => '索', + 63851 => '參', + 63852 => '塞', + 63853 => '省', + 63854 => '葉', + 63855 => '說', + 63856 => '殺', + 63857 => '辰', + 63858 => '沈', + 63859 => '拾', + 63860 => '若', + 63861 => '掠', + 63862 => '略', + 63863 => '亮', + 63864 => '兩', + 63865 => '凉', + 63866 => '梁', + 63867 => '糧', + 63868 => '良', + 63869 => '諒', + 63870 => '量', + 63871 => '勵', + 63872 => '呂', + 63873 => '女', + 63874 => '廬', + 63875 => '旅', + 63876 => '濾', + 63877 => '礪', + 63878 => '閭', + 63879 => '驪', + 63880 => '麗', + 63881 => '黎', + 63882 => '力', + 63883 => '曆', + 63884 => '歷', + 63885 => '轢', + 63886 => '年', + 63887 => '憐', + 63888 => '戀', + 63889 => '撚', + 63890 => '漣', + 63891 => '煉', + 63892 => '璉', + 63893 => '秊', + 63894 => '練', + 63895 => '聯', + 63896 => '輦', + 63897 => '蓮', + 63898 => '連', + 63899 => '鍊', + 63900 => '列', + 63901 => '劣', + 63902 => '咽', + 63903 => '烈', + 63904 => '裂', + 63905 => '說', + 63906 => '廉', + 63907 => '念', + 63908 => '捻', + 63909 => '殮', + 63910 => '簾', + 63911 => '獵', + 63912 => '令', + 63913 => '囹', + 63914 => '寧', + 63915 => '嶺', + 63916 => '怜', + 63917 => '玲', + 63918 => '瑩', + 63919 => '羚', + 63920 => '聆', + 63921 => '鈴', + 63922 => '零', + 63923 => '靈', + 63924 => '領', + 63925 => '例', + 63926 => '禮', + 63927 => '醴', + 63928 => '隸', + 63929 => '惡', + 63930 => '了', + 63931 => '僚', + 63932 => '寮', + 63933 => '尿', + 63934 => '料', + 63935 => '樂', + 63936 => '燎', + 63937 => '療', + 63938 => '蓼', + 63939 => '遼', + 63940 => '龍', + 63941 => '暈', + 63942 => '阮', + 63943 => '劉', + 63944 => '杻', + 63945 => '柳', + 63946 => '流', + 63947 => '溜', + 63948 => '琉', + 63949 => '留', + 63950 => '硫', + 63951 => '紐', + 63952 => '類', + 63953 => '六', + 63954 => '戮', + 63955 => '陸', + 63956 => '倫', + 63957 => '崙', + 63958 => '淪', + 63959 => '輪', + 63960 => '律', + 63961 => '慄', + 63962 => '栗', + 63963 => '率', + 63964 => '隆', + 63965 => '利', + 63966 => '吏', + 63967 => '履', + 63968 => '易', + 63969 => '李', + 63970 => '梨', + 63971 => '泥', + 63972 => '理', + 63973 => '痢', + 63974 => '罹', + 63975 => '裏', + 63976 => '裡', + 63977 => '里', + 63978 => '離', + 63979 => '匿', + 63980 => '溺', + 63981 => '吝', + 63982 => '燐', + 63983 => '璘', + 63984 => '藺', + 63985 => '隣', + 63986 => '鱗', + 63987 => '麟', + 63988 => '林', + 63989 => '淋', + 63990 => '臨', + 63991 => '立', + 63992 => '笠', + 63993 => '粒', + 63994 => '狀', + 63995 => '炙', + 63996 => '識', + 63997 => '什', + 63998 => '茶', + 63999 => '刺', + 64000 => '切', + 64001 => '度', + 64002 => '拓', + 64003 => '糖', + 64004 => '宅', + 64005 => '洞', + 64006 => '暴', + 64007 => '輻', + 64008 => '行', + 64009 => '降', + 64010 => '見', + 64011 => '廓', + 64012 => '兀', + 64013 => '嗀', + 64016 => '塚', + 64018 => '晴', + 64021 => '凞', + 64022 => '猪', + 64023 => '益', + 64024 => '礼', + 64025 => '神', + 64026 => '祥', + 64027 => '福', + 64028 => '靖', + 64029 => '精', + 64030 => '羽', + 64032 => '蘒', + 64034 => '諸', + 64037 => '逸', + 64038 => '都', + 64042 => '飯', + 64043 => '飼', + 64044 => '館', + 64045 => '鶴', + 64046 => '郞', + 64047 => '隷', + 64048 => '侮', + 64049 => '僧', + 64050 => '免', + 64051 => '勉', + 64052 => '勤', + 64053 => '卑', + 64054 => '喝', + 64055 => '嘆', + 64056 => '器', + 64057 => '塀', + 64058 => '墨', + 64059 => '層', + 64060 => '屮', + 64061 => '悔', + 64062 => '慨', + 64063 => '憎', + 64064 => '懲', + 64065 => '敏', + 64066 => '既', + 64067 => '暑', + 64068 => '梅', + 64069 => '海', + 64070 => '渚', + 64071 => '漢', + 64072 => '煮', + 64073 => '爫', + 64074 => '琢', + 64075 => '碑', + 64076 => '社', + 64077 => '祉', + 64078 => '祈', + 64079 => '祐', + 64080 => '祖', + 64081 => '祝', + 64082 => '禍', + 64083 => '禎', + 64084 => '穀', + 64085 => '突', + 64086 => '節', + 64087 => '練', + 64088 => '縉', + 64089 => '繁', + 64090 => '署', + 64091 => '者', + 64092 => '臭', + 64093 => '艹', + 64094 => '艹', + 64095 => '著', + 64096 => '褐', + 64097 => '視', + 64098 => '謁', + 64099 => '謹', + 64100 => '賓', + 64101 => '贈', + 64102 => '辶', + 64103 => '逸', + 64104 => '難', + 64105 => '響', + 64106 => '頻', + 64107 => '恵', + 64108 => '𤋮', + 64109 => '舘', + 64112 => '並', + 64113 => '况', + 64114 => '全', + 64115 => '侀', + 64116 => '充', + 64117 => '冀', + 64118 => '勇', + 64119 => '勺', + 64120 => '喝', + 64121 => '啕', + 64122 => '喙', + 64123 => '嗢', + 64124 => '塚', + 64125 => '墳', + 64126 => '奄', + 64127 => '奔', + 64128 => '婢', + 64129 => '嬨', + 64130 => '廒', + 64131 => '廙', + 64132 => '彩', + 64133 => '徭', + 64134 => '惘', + 64135 => '慎', + 64136 => '愈', + 64137 => '憎', + 64138 => '慠', + 64139 => '懲', + 64140 => '戴', + 64141 => '揄', + 64142 => '搜', + 64143 => '摒', + 64144 => '敖', + 64145 => '晴', + 64146 => '朗', + 64147 => '望', + 64148 => '杖', + 64149 => '歹', + 64150 => '殺', + 64151 => '流', + 64152 => '滛', + 64153 => '滋', + 64154 => '漢', + 64155 => '瀞', + 64156 => '煮', + 64157 => '瞧', + 64158 => '爵', + 64159 => '犯', + 64160 => '猪', + 64161 => '瑱', + 64162 => '甆', + 64163 => '画', + 64164 => '瘝', + 64165 => '瘟', + 64166 => '益', + 64167 => '盛', + 64168 => '直', + 64169 => '睊', + 64170 => '着', + 64171 => '磌', + 64172 => '窱', + 64173 => '節', + 64174 => '类', + 64175 => '絛', + 64176 => '練', + 64177 => '缾', + 64178 => '者', + 64179 => '荒', + 64180 => '華', + 64181 => '蝹', + 64182 => '襁', + 64183 => '覆', + 64184 => '視', + 64185 => '調', + 64186 => '諸', + 64187 => '請', + 64188 => '謁', + 64189 => '諾', + 64190 => '諭', + 64191 => '謹', + 64192 => '變', + 64193 => '贈', + 64194 => '輸', + 64195 => '遲', + 64196 => '醙', + 64197 => '鉶', + 64198 => '陼', + 64199 => '難', + 64200 => '靖', + 64201 => '韛', + 64202 => '響', + 64203 => '頋', + 64204 => '頻', + 64205 => '鬒', + 64206 => '龜', + 64207 => '𢡊', + 64208 => '𢡄', + 64209 => '𣏕', + 64210 => '㮝', + 64211 => '䀘', + 64212 => '䀹', + 64213 => '𥉉', + 64214 => '𥳐', + 64215 => '𧻓', + 64216 => '齃', + 64217 => '龎', + 64256 => 'ff', + 64257 => 'fi', + 64258 => 'fl', + 64259 => 'ffi', + 64260 => 'ffl', + 64261 => 'st', + 64262 => 'st', + 64275 => 'մն', + 64276 => 'մե', + 64277 => 'մի', + 64278 => 'վն', + 64279 => 'մխ', + 64285 => 'יִ', + 64287 => 'ײַ', + 64288 => 'ע', + 64289 => 'א', + 64290 => 'ד', + 64291 => 'ה', + 64292 => 'כ', + 64293 => 'ל', + 64294 => 'ם', + 64295 => 'ר', + 64296 => 'ת', + 64298 => 'שׁ', + 64299 => 'שׂ', + 64300 => 'שּׁ', + 64301 => 'שּׂ', + 64302 => 'אַ', + 64303 => 'אָ', + 64304 => 'אּ', + 64305 => 'בּ', + 64306 => 'גּ', + 64307 => 'דּ', + 64308 => 'הּ', + 64309 => 'וּ', + 64310 => 'זּ', + 64312 => 'טּ', + 64313 => 'יּ', + 64314 => 'ךּ', + 64315 => 'כּ', + 64316 => 'לּ', + 64318 => 'מּ', + 64320 => 'נּ', + 64321 => 'סּ', + 64323 => 'ףּ', + 64324 => 'פּ', + 64326 => 'צּ', + 64327 => 'קּ', + 64328 => 'רּ', + 64329 => 'שּ', + 64330 => 'תּ', + 64331 => 'וֹ', + 64332 => 'בֿ', + 64333 => 'כֿ', + 64334 => 'פֿ', + 64335 => 'אל', + 64336 => 'ٱ', + 64337 => 'ٱ', + 64338 => 'ٻ', + 64339 => 'ٻ', + 64340 => 'ٻ', + 64341 => 'ٻ', + 64342 => 'پ', + 64343 => 'پ', + 64344 => 'پ', + 64345 => 'پ', + 64346 => 'ڀ', + 64347 => 'ڀ', + 64348 => 'ڀ', + 64349 => 'ڀ', + 64350 => 'ٺ', + 64351 => 'ٺ', + 64352 => 'ٺ', + 64353 => 'ٺ', + 64354 => 'ٿ', + 64355 => 'ٿ', + 64356 => 'ٿ', + 64357 => 'ٿ', + 64358 => 'ٹ', + 64359 => 'ٹ', + 64360 => 'ٹ', + 64361 => 'ٹ', + 64362 => 'ڤ', + 64363 => 'ڤ', + 64364 => 'ڤ', + 64365 => 'ڤ', + 64366 => 'ڦ', + 64367 => 'ڦ', + 64368 => 'ڦ', + 64369 => 'ڦ', + 64370 => 'ڄ', + 64371 => 'ڄ', + 64372 => 'ڄ', + 64373 => 'ڄ', + 64374 => 'ڃ', + 64375 => 'ڃ', + 64376 => 'ڃ', + 64377 => 'ڃ', + 64378 => 'چ', + 64379 => 'چ', + 64380 => 'چ', + 64381 => 'چ', + 64382 => 'ڇ', + 64383 => 'ڇ', + 64384 => 'ڇ', + 64385 => 'ڇ', + 64386 => 'ڍ', + 64387 => 'ڍ', + 64388 => 'ڌ', + 64389 => 'ڌ', + 64390 => 'ڎ', + 64391 => 'ڎ', + 64392 => 'ڈ', + 64393 => 'ڈ', + 64394 => 'ژ', + 64395 => 'ژ', + 64396 => 'ڑ', + 64397 => 'ڑ', + 64398 => 'ک', + 64399 => 'ک', + 64400 => 'ک', + 64401 => 'ک', + 64402 => 'گ', + 64403 => 'گ', + 64404 => 'گ', + 64405 => 'گ', + 64406 => 'ڳ', + 64407 => 'ڳ', + 64408 => 'ڳ', + 64409 => 'ڳ', + 64410 => 'ڱ', + 64411 => 'ڱ', + 64412 => 'ڱ', + 64413 => 'ڱ', + 64414 => 'ں', + 64415 => 'ں', + 64416 => 'ڻ', + 64417 => 'ڻ', + 64418 => 'ڻ', + 64419 => 'ڻ', + 64420 => 'ۀ', + 64421 => 'ۀ', + 64422 => 'ہ', + 64423 => 'ہ', + 64424 => 'ہ', + 64425 => 'ہ', + 64426 => 'ھ', + 64427 => 'ھ', + 64428 => 'ھ', + 64429 => 'ھ', + 64430 => 'ے', + 64431 => 'ے', + 64432 => 'ۓ', + 64433 => 'ۓ', + 64467 => 'ڭ', + 64468 => 'ڭ', + 64469 => 'ڭ', + 64470 => 'ڭ', + 64471 => 'ۇ', + 64472 => 'ۇ', + 64473 => 'ۆ', + 64474 => 'ۆ', + 64475 => 'ۈ', + 64476 => 'ۈ', + 64477 => 'ۇٴ', + 64478 => 'ۋ', + 64479 => 'ۋ', + 64480 => 'ۅ', + 64481 => 'ۅ', + 64482 => 'ۉ', + 64483 => 'ۉ', + 64484 => 'ې', + 64485 => 'ې', + 64486 => 'ې', + 64487 => 'ې', + 64488 => 'ى', + 64489 => 'ى', + 64490 => 'ئا', + 64491 => 'ئا', + 64492 => 'ئە', + 64493 => 'ئە', + 64494 => 'ئو', + 64495 => 'ئو', + 64496 => 'ئۇ', + 64497 => 'ئۇ', + 64498 => 'ئۆ', + 64499 => 'ئۆ', + 64500 => 'ئۈ', + 64501 => 'ئۈ', + 64502 => 'ئې', + 64503 => 'ئې', + 64504 => 'ئې', + 64505 => 'ئى', + 64506 => 'ئى', + 64507 => 'ئى', + 64508 => 'ی', + 64509 => 'ی', + 64510 => 'ی', + 64511 => 'ی', + 64512 => 'ئج', + 64513 => 'ئح', + 64514 => 'ئم', + 64515 => 'ئى', + 64516 => 'ئي', + 64517 => 'بج', + 64518 => 'بح', + 64519 => 'بخ', + 64520 => 'بم', + 64521 => 'بى', + 64522 => 'بي', + 64523 => 'تج', + 64524 => 'تح', + 64525 => 'تخ', + 64526 => 'تم', + 64527 => 'تى', + 64528 => 'تي', + 64529 => 'ثج', + 64530 => 'ثم', + 64531 => 'ثى', + 64532 => 'ثي', + 64533 => 'جح', + 64534 => 'جم', + 64535 => 'حج', + 64536 => 'حم', + 64537 => 'خج', + 64538 => 'خح', + 64539 => 'خم', + 64540 => 'سج', + 64541 => 'سح', + 64542 => 'سخ', + 64543 => 'سم', + 64544 => 'صح', + 64545 => 'صم', + 64546 => 'ضج', + 64547 => 'ضح', + 64548 => 'ضخ', + 64549 => 'ضم', + 64550 => 'طح', + 64551 => 'طم', + 64552 => 'ظم', + 64553 => 'عج', + 64554 => 'عم', + 64555 => 'غج', + 64556 => 'غم', + 64557 => 'فج', + 64558 => 'فح', + 64559 => 'فخ', + 64560 => 'فم', + 64561 => 'فى', + 64562 => 'في', + 64563 => 'قح', + 64564 => 'قم', + 64565 => 'قى', + 64566 => 'قي', + 64567 => 'كا', + 64568 => 'كج', + 64569 => 'كح', + 64570 => 'كخ', + 64571 => 'كل', + 64572 => 'كم', + 64573 => 'كى', + 64574 => 'كي', + 64575 => 'لج', + 64576 => 'لح', + 64577 => 'لخ', + 64578 => 'لم', + 64579 => 'لى', + 64580 => 'لي', + 64581 => 'مج', + 64582 => 'مح', + 64583 => 'مخ', + 64584 => 'مم', + 64585 => 'مى', + 64586 => 'مي', + 64587 => 'نج', + 64588 => 'نح', + 64589 => 'نخ', + 64590 => 'نم', + 64591 => 'نى', + 64592 => 'ني', + 64593 => 'هج', + 64594 => 'هم', + 64595 => 'هى', + 64596 => 'هي', + 64597 => 'يج', + 64598 => 'يح', + 64599 => 'يخ', + 64600 => 'يم', + 64601 => 'يى', + 64602 => 'يي', + 64603 => 'ذٰ', + 64604 => 'رٰ', + 64605 => 'ىٰ', + 64612 => 'ئر', + 64613 => 'ئز', + 64614 => 'ئم', + 64615 => 'ئن', + 64616 => 'ئى', + 64617 => 'ئي', + 64618 => 'بر', + 64619 => 'بز', + 64620 => 'بم', + 64621 => 'بن', + 64622 => 'بى', + 64623 => 'بي', + 64624 => 'تر', + 64625 => 'تز', + 64626 => 'تم', + 64627 => 'تن', + 64628 => 'تى', + 64629 => 'تي', + 64630 => 'ثر', + 64631 => 'ثز', + 64632 => 'ثم', + 64633 => 'ثن', + 64634 => 'ثى', + 64635 => 'ثي', + 64636 => 'فى', + 64637 => 'في', + 64638 => 'قى', + 64639 => 'قي', + 64640 => 'كا', + 64641 => 'كل', + 64642 => 'كم', + 64643 => 'كى', + 64644 => 'كي', + 64645 => 'لم', + 64646 => 'لى', + 64647 => 'لي', + 64648 => 'ما', + 64649 => 'مم', + 64650 => 'نر', + 64651 => 'نز', + 64652 => 'نم', + 64653 => 'نن', + 64654 => 'نى', + 64655 => 'ني', + 64656 => 'ىٰ', + 64657 => 'ير', + 64658 => 'يز', + 64659 => 'يم', + 64660 => 'ين', + 64661 => 'يى', + 64662 => 'يي', + 64663 => 'ئج', + 64664 => 'ئح', + 64665 => 'ئخ', + 64666 => 'ئم', + 64667 => 'ئه', + 64668 => 'بج', + 64669 => 'بح', + 64670 => 'بخ', + 64671 => 'بم', + 64672 => 'به', + 64673 => 'تج', + 64674 => 'تح', + 64675 => 'تخ', + 64676 => 'تم', + 64677 => 'ته', + 64678 => 'ثم', + 64679 => 'جح', + 64680 => 'جم', + 64681 => 'حج', + 64682 => 'حم', + 64683 => 'خج', + 64684 => 'خم', + 64685 => 'سج', + 64686 => 'سح', + 64687 => 'سخ', + 64688 => 'سم', + 64689 => 'صح', + 64690 => 'صخ', + 64691 => 'صم', + 64692 => 'ضج', + 64693 => 'ضح', + 64694 => 'ضخ', + 64695 => 'ضم', + 64696 => 'طح', + 64697 => 'ظم', + 64698 => 'عج', + 64699 => 'عم', + 64700 => 'غج', + 64701 => 'غم', + 64702 => 'فج', + 64703 => 'فح', + 64704 => 'فخ', + 64705 => 'فم', + 64706 => 'قح', + 64707 => 'قم', + 64708 => 'كج', + 64709 => 'كح', + 64710 => 'كخ', + 64711 => 'كل', + 64712 => 'كم', + 64713 => 'لج', + 64714 => 'لح', + 64715 => 'لخ', + 64716 => 'لم', + 64717 => 'له', + 64718 => 'مج', + 64719 => 'مح', + 64720 => 'مخ', + 64721 => 'مم', + 64722 => 'نج', + 64723 => 'نح', + 64724 => 'نخ', + 64725 => 'نم', + 64726 => 'نه', + 64727 => 'هج', + 64728 => 'هم', + 64729 => 'هٰ', + 64730 => 'يج', + 64731 => 'يح', + 64732 => 'يخ', + 64733 => 'يم', + 64734 => 'يه', + 64735 => 'ئم', + 64736 => 'ئه', + 64737 => 'بم', + 64738 => 'به', + 64739 => 'تم', + 64740 => 'ته', + 64741 => 'ثم', + 64742 => 'ثه', + 64743 => 'سم', + 64744 => 'سه', + 64745 => 'شم', + 64746 => 'شه', + 64747 => 'كل', + 64748 => 'كم', + 64749 => 'لم', + 64750 => 'نم', + 64751 => 'نه', + 64752 => 'يم', + 64753 => 'يه', + 64754 => 'ـَّ', + 64755 => 'ـُّ', + 64756 => 'ـِّ', + 64757 => 'طى', + 64758 => 'طي', + 64759 => 'عى', + 64760 => 'عي', + 64761 => 'غى', + 64762 => 'غي', + 64763 => 'سى', + 64764 => 'سي', + 64765 => 'شى', + 64766 => 'شي', + 64767 => 'حى', + 64768 => 'حي', + 64769 => 'جى', + 64770 => 'جي', + 64771 => 'خى', + 64772 => 'خي', + 64773 => 'صى', + 64774 => 'صي', + 64775 => 'ضى', + 64776 => 'ضي', + 64777 => 'شج', + 64778 => 'شح', + 64779 => 'شخ', + 64780 => 'شم', + 64781 => 'شر', + 64782 => 'سر', + 64783 => 'صر', + 64784 => 'ضر', + 64785 => 'طى', + 64786 => 'طي', + 64787 => 'عى', + 64788 => 'عي', + 64789 => 'غى', + 64790 => 'غي', + 64791 => 'سى', + 64792 => 'سي', + 64793 => 'شى', + 64794 => 'شي', + 64795 => 'حى', + 64796 => 'حي', + 64797 => 'جى', + 64798 => 'جي', + 64799 => 'خى', + 64800 => 'خي', + 64801 => 'صى', + 64802 => 'صي', + 64803 => 'ضى', + 64804 => 'ضي', + 64805 => 'شج', + 64806 => 'شح', + 64807 => 'شخ', + 64808 => 'شم', + 64809 => 'شر', + 64810 => 'سر', + 64811 => 'صر', + 64812 => 'ضر', + 64813 => 'شج', + 64814 => 'شح', + 64815 => 'شخ', + 64816 => 'شم', + 64817 => 'سه', + 64818 => 'شه', + 64819 => 'طم', + 64820 => 'سج', + 64821 => 'سح', + 64822 => 'سخ', + 64823 => 'شج', + 64824 => 'شح', + 64825 => 'شخ', + 64826 => 'طم', + 64827 => 'ظم', + 64828 => 'اً', + 64829 => 'اً', + 64848 => 'تجم', + 64849 => 'تحج', + 64850 => 'تحج', + 64851 => 'تحم', + 64852 => 'تخم', + 64853 => 'تمج', + 64854 => 'تمح', + 64855 => 'تمخ', + 64856 => 'جمح', + 64857 => 'جمح', + 64858 => 'حمي', + 64859 => 'حمى', + 64860 => 'سحج', + 64861 => 'سجح', + 64862 => 'سجى', + 64863 => 'سمح', + 64864 => 'سمح', + 64865 => 'سمج', + 64866 => 'سمم', + 64867 => 'سمم', + 64868 => 'صحح', + 64869 => 'صحح', + 64870 => 'صمم', + 64871 => 'شحم', + 64872 => 'شحم', + 64873 => 'شجي', + 64874 => 'شمخ', + 64875 => 'شمخ', + 64876 => 'شمم', + 64877 => 'شمم', + 64878 => 'ضحى', + 64879 => 'ضخم', + 64880 => 'ضخم', + 64881 => 'طمح', + 64882 => 'طمح', + 64883 => 'طمم', + 64884 => 'طمي', + 64885 => 'عجم', + 64886 => 'عمم', + 64887 => 'عمم', + 64888 => 'عمى', + 64889 => 'غمم', + 64890 => 'غمي', + 64891 => 'غمى', + 64892 => 'فخم', + 64893 => 'فخم', + 64894 => 'قمح', + 64895 => 'قمم', + 64896 => 'لحم', + 64897 => 'لحي', + 64898 => 'لحى', + 64899 => 'لجج', + 64900 => 'لجج', + 64901 => 'لخم', + 64902 => 'لخم', + 64903 => 'لمح', + 64904 => 'لمح', + 64905 => 'محج', + 64906 => 'محم', + 64907 => 'محي', + 64908 => 'مجح', + 64909 => 'مجم', + 64910 => 'مخج', + 64911 => 'مخم', + 64914 => 'مجخ', + 64915 => 'همج', + 64916 => 'همم', + 64917 => 'نحم', + 64918 => 'نحى', + 64919 => 'نجم', + 64920 => 'نجم', + 64921 => 'نجى', + 64922 => 'نمي', + 64923 => 'نمى', + 64924 => 'يمم', + 64925 => 'يمم', + 64926 => 'بخي', + 64927 => 'تجي', + 64928 => 'تجى', + 64929 => 'تخي', + 64930 => 'تخى', + 64931 => 'تمي', + 64932 => 'تمى', + 64933 => 'جمي', + 64934 => 'جحى', + 64935 => 'جمى', + 64936 => 'سخى', + 64937 => 'صحي', + 64938 => 'شحي', + 64939 => 'ضحي', + 64940 => 'لجي', + 64941 => 'لمي', + 64942 => 'يحي', + 64943 => 'يجي', + 64944 => 'يمي', + 64945 => 'ممي', + 64946 => 'قمي', + 64947 => 'نحي', + 64948 => 'قمح', + 64949 => 'لحم', + 64950 => 'عمي', + 64951 => 'كمي', + 64952 => 'نجح', + 64953 => 'مخي', + 64954 => 'لجم', + 64955 => 'كمم', + 64956 => 'لجم', + 64957 => 'نجح', + 64958 => 'جحي', + 64959 => 'حجي', + 64960 => 'مجي', + 64961 => 'فمي', + 64962 => 'بحي', + 64963 => 'كمم', + 64964 => 'عجم', + 64965 => 'صمم', + 64966 => 'سخي', + 64967 => 'نجي', + 65008 => 'صلے', + 65009 => 'قلے', + 65010 => 'الله', + 65011 => 'اكبر', + 65012 => 'محمد', + 65013 => 'صلعم', + 65014 => 'رسول', + 65015 => 'عليه', + 65016 => 'وسلم', + 65017 => 'صلى', + 65020 => 'ریال', + 65041 => '、', + 65047 => '〖', + 65048 => '〗', + 65073 => '—', + 65074 => '–', + 65081 => '〔', + 65082 => '〕', + 65083 => '【', + 65084 => '】', + 65085 => '《', + 65086 => '》', + 65087 => '〈', + 65088 => '〉', + 65089 => '「', + 65090 => '」', + 65091 => '『', + 65092 => '』', + 65105 => '、', + 65112 => '—', + 65117 => '〔', + 65118 => '〕', + 65123 => '-', + 65137 => 'ـً', + 65143 => 'ـَ', + 65145 => 'ـُ', + 65147 => 'ـِ', + 65149 => 'ـّ', + 65151 => 'ـْ', + 65152 => 'ء', + 65153 => 'آ', + 65154 => 'آ', + 65155 => 'أ', + 65156 => 'أ', + 65157 => 'ؤ', + 65158 => 'ؤ', + 65159 => 'إ', + 65160 => 'إ', + 65161 => 'ئ', + 65162 => 'ئ', + 65163 => 'ئ', + 65164 => 'ئ', + 65165 => 'ا', + 65166 => 'ا', + 65167 => 'ب', + 65168 => 'ب', + 65169 => 'ب', + 65170 => 'ب', + 65171 => 'ة', + 65172 => 'ة', + 65173 => 'ت', + 65174 => 'ت', + 65175 => 'ت', + 65176 => 'ت', + 65177 => 'ث', + 65178 => 'ث', + 65179 => 'ث', + 65180 => 'ث', + 65181 => 'ج', + 65182 => 'ج', + 65183 => 'ج', + 65184 => 'ج', + 65185 => 'ح', + 65186 => 'ح', + 65187 => 'ح', + 65188 => 'ح', + 65189 => 'خ', + 65190 => 'خ', + 65191 => 'خ', + 65192 => 'خ', + 65193 => 'د', + 65194 => 'د', + 65195 => 'ذ', + 65196 => 'ذ', + 65197 => 'ر', + 65198 => 'ر', + 65199 => 'ز', + 65200 => 'ز', + 65201 => 'س', + 65202 => 'س', + 65203 => 'س', + 65204 => 'س', + 65205 => 'ش', + 65206 => 'ش', + 65207 => 'ش', + 65208 => 'ش', + 65209 => 'ص', + 65210 => 'ص', + 65211 => 'ص', + 65212 => 'ص', + 65213 => 'ض', + 65214 => 'ض', + 65215 => 'ض', + 65216 => 'ض', + 65217 => 'ط', + 65218 => 'ط', + 65219 => 'ط', + 65220 => 'ط', + 65221 => 'ظ', + 65222 => 'ظ', + 65223 => 'ظ', + 65224 => 'ظ', + 65225 => 'ع', + 65226 => 'ع', + 65227 => 'ع', + 65228 => 'ع', + 65229 => 'غ', + 65230 => 'غ', + 65231 => 'غ', + 65232 => 'غ', + 65233 => 'ف', + 65234 => 'ف', + 65235 => 'ف', + 65236 => 'ف', + 65237 => 'ق', + 65238 => 'ق', + 65239 => 'ق', + 65240 => 'ق', + 65241 => 'ك', + 65242 => 'ك', + 65243 => 'ك', + 65244 => 'ك', + 65245 => 'ل', + 65246 => 'ل', + 65247 => 'ل', + 65248 => 'ل', + 65249 => 'م', + 65250 => 'م', + 65251 => 'م', + 65252 => 'م', + 65253 => 'ن', + 65254 => 'ن', + 65255 => 'ن', + 65256 => 'ن', + 65257 => 'ه', + 65258 => 'ه', + 65259 => 'ه', + 65260 => 'ه', + 65261 => 'و', + 65262 => 'و', + 65263 => 'ى', + 65264 => 'ى', + 65265 => 'ي', + 65266 => 'ي', + 65267 => 'ي', + 65268 => 'ي', + 65269 => 'لآ', + 65270 => 'لآ', + 65271 => 'لأ', + 65272 => 'لأ', + 65273 => 'لإ', + 65274 => 'لإ', + 65275 => 'لا', + 65276 => 'لا', + 65293 => '-', + 65294 => '.', + 65296 => '0', + 65297 => '1', + 65298 => '2', + 65299 => '3', + 65300 => '4', + 65301 => '5', + 65302 => '6', + 65303 => '7', + 65304 => '8', + 65305 => '9', + 65313 => 'a', + 65314 => 'b', + 65315 => 'c', + 65316 => 'd', + 65317 => 'e', + 65318 => 'f', + 65319 => 'g', + 65320 => 'h', + 65321 => 'i', + 65322 => 'j', + 65323 => 'k', + 65324 => 'l', + 65325 => 'm', + 65326 => 'n', + 65327 => 'o', + 65328 => 'p', + 65329 => 'q', + 65330 => 'r', + 65331 => 's', + 65332 => 't', + 65333 => 'u', + 65334 => 'v', + 65335 => 'w', + 65336 => 'x', + 65337 => 'y', + 65338 => 'z', + 65345 => 'a', + 65346 => 'b', + 65347 => 'c', + 65348 => 'd', + 65349 => 'e', + 65350 => 'f', + 65351 => 'g', + 65352 => 'h', + 65353 => 'i', + 65354 => 'j', + 65355 => 'k', + 65356 => 'l', + 65357 => 'm', + 65358 => 'n', + 65359 => 'o', + 65360 => 'p', + 65361 => 'q', + 65362 => 'r', + 65363 => 's', + 65364 => 't', + 65365 => 'u', + 65366 => 'v', + 65367 => 'w', + 65368 => 'x', + 65369 => 'y', + 65370 => 'z', + 65375 => '⦅', + 65376 => '⦆', + 65377 => '.', + 65378 => '「', + 65379 => '」', + 65380 => '、', + 65381 => '・', + 65382 => 'ヲ', + 65383 => 'ァ', + 65384 => 'ィ', + 65385 => 'ゥ', + 65386 => 'ェ', + 65387 => 'ォ', + 65388 => 'ャ', + 65389 => 'ュ', + 65390 => 'ョ', + 65391 => 'ッ', + 65392 => 'ー', + 65393 => 'ア', + 65394 => 'イ', + 65395 => 'ウ', + 65396 => 'エ', + 65397 => 'オ', + 65398 => 'カ', + 65399 => 'キ', + 65400 => 'ク', + 65401 => 'ケ', + 65402 => 'コ', + 65403 => 'サ', + 65404 => 'シ', + 65405 => 'ス', + 65406 => 'セ', + 65407 => 'ソ', + 65408 => 'タ', + 65409 => 'チ', + 65410 => 'ツ', + 65411 => 'テ', + 65412 => 'ト', + 65413 => 'ナ', + 65414 => 'ニ', + 65415 => 'ヌ', + 65416 => 'ネ', + 65417 => 'ノ', + 65418 => 'ハ', + 65419 => 'ヒ', + 65420 => 'フ', + 65421 => 'ヘ', + 65422 => 'ホ', + 65423 => 'マ', + 65424 => 'ミ', + 65425 => 'ム', + 65426 => 'メ', + 65427 => 'モ', + 65428 => 'ヤ', + 65429 => 'ユ', + 65430 => 'ヨ', + 65431 => 'ラ', + 65432 => 'リ', + 65433 => 'ル', + 65434 => 'レ', + 65435 => 'ロ', + 65436 => 'ワ', + 65437 => 'ン', + 65438 => '゙', + 65439 => '゚', + 65441 => 'ᄀ', + 65442 => 'ᄁ', + 65443 => 'ᆪ', + 65444 => 'ᄂ', + 65445 => 'ᆬ', + 65446 => 'ᆭ', + 65447 => 'ᄃ', + 65448 => 'ᄄ', + 65449 => 'ᄅ', + 65450 => 'ᆰ', + 65451 => 'ᆱ', + 65452 => 'ᆲ', + 65453 => 'ᆳ', + 65454 => 'ᆴ', + 65455 => 'ᆵ', + 65456 => 'ᄚ', + 65457 => 'ᄆ', + 65458 => 'ᄇ', + 65459 => 'ᄈ', + 65460 => 'ᄡ', + 65461 => 'ᄉ', + 65462 => 'ᄊ', + 65463 => 'ᄋ', + 65464 => 'ᄌ', + 65465 => 'ᄍ', + 65466 => 'ᄎ', + 65467 => 'ᄏ', + 65468 => 'ᄐ', + 65469 => 'ᄑ', + 65470 => 'ᄒ', + 65474 => 'ᅡ', + 65475 => 'ᅢ', + 65476 => 'ᅣ', + 65477 => 'ᅤ', + 65478 => 'ᅥ', + 65479 => 'ᅦ', + 65482 => 'ᅧ', + 65483 => 'ᅨ', + 65484 => 'ᅩ', + 65485 => 'ᅪ', + 65486 => 'ᅫ', + 65487 => 'ᅬ', + 65490 => 'ᅭ', + 65491 => 'ᅮ', + 65492 => 'ᅯ', + 65493 => 'ᅰ', + 65494 => 'ᅱ', + 65495 => 'ᅲ', + 65498 => 'ᅳ', + 65499 => 'ᅴ', + 65500 => 'ᅵ', + 65504 => '¢', + 65505 => '£', + 65506 => '¬', + 65508 => '¦', + 65509 => '¥', + 65510 => '₩', + 65512 => '│', + 65513 => '←', + 65514 => '↑', + 65515 => '→', + 65516 => '↓', + 65517 => '■', + 65518 => '○', + 66560 => '𐐨', + 66561 => '𐐩', + 66562 => '𐐪', + 66563 => '𐐫', + 66564 => '𐐬', + 66565 => '𐐭', + 66566 => '𐐮', + 66567 => '𐐯', + 66568 => '𐐰', + 66569 => '𐐱', + 66570 => '𐐲', + 66571 => '𐐳', + 66572 => '𐐴', + 66573 => '𐐵', + 66574 => '𐐶', + 66575 => '𐐷', + 66576 => '𐐸', + 66577 => '𐐹', + 66578 => '𐐺', + 66579 => '𐐻', + 66580 => '𐐼', + 66581 => '𐐽', + 66582 => '𐐾', + 66583 => '𐐿', + 66584 => '𐑀', + 66585 => '𐑁', + 66586 => '𐑂', + 66587 => '𐑃', + 66588 => '𐑄', + 66589 => '𐑅', + 66590 => '𐑆', + 66591 => '𐑇', + 66592 => '𐑈', + 66593 => '𐑉', + 66594 => '𐑊', + 66595 => '𐑋', + 66596 => '𐑌', + 66597 => '𐑍', + 66598 => '𐑎', + 66599 => '𐑏', + 66736 => '𐓘', + 66737 => '𐓙', + 66738 => '𐓚', + 66739 => '𐓛', + 66740 => '𐓜', + 66741 => '𐓝', + 66742 => '𐓞', + 66743 => '𐓟', + 66744 => '𐓠', + 66745 => '𐓡', + 66746 => '𐓢', + 66747 => '𐓣', + 66748 => '𐓤', + 66749 => '𐓥', + 66750 => '𐓦', + 66751 => '𐓧', + 66752 => '𐓨', + 66753 => '𐓩', + 66754 => '𐓪', + 66755 => '𐓫', + 66756 => '𐓬', + 66757 => '𐓭', + 66758 => '𐓮', + 66759 => '𐓯', + 66760 => '𐓰', + 66761 => '𐓱', + 66762 => '𐓲', + 66763 => '𐓳', + 66764 => '𐓴', + 66765 => '𐓵', + 66766 => '𐓶', + 66767 => '𐓷', + 66768 => '𐓸', + 66769 => '𐓹', + 66770 => '𐓺', + 66771 => '𐓻', + 68736 => '𐳀', + 68737 => '𐳁', + 68738 => '𐳂', + 68739 => '𐳃', + 68740 => '𐳄', + 68741 => '𐳅', + 68742 => '𐳆', + 68743 => '𐳇', + 68744 => '𐳈', + 68745 => '𐳉', + 68746 => '𐳊', + 68747 => '𐳋', + 68748 => '𐳌', + 68749 => '𐳍', + 68750 => '𐳎', + 68751 => '𐳏', + 68752 => '𐳐', + 68753 => '𐳑', + 68754 => '𐳒', + 68755 => '𐳓', + 68756 => '𐳔', + 68757 => '𐳕', + 68758 => '𐳖', + 68759 => '𐳗', + 68760 => '𐳘', + 68761 => '𐳙', + 68762 => '𐳚', + 68763 => '𐳛', + 68764 => '𐳜', + 68765 => '𐳝', + 68766 => '𐳞', + 68767 => '𐳟', + 68768 => '𐳠', + 68769 => '𐳡', + 68770 => '𐳢', + 68771 => '𐳣', + 68772 => '𐳤', + 68773 => '𐳥', + 68774 => '𐳦', + 68775 => '𐳧', + 68776 => '𐳨', + 68777 => '𐳩', + 68778 => '𐳪', + 68779 => '𐳫', + 68780 => '𐳬', + 68781 => '𐳭', + 68782 => '𐳮', + 68783 => '𐳯', + 68784 => '𐳰', + 68785 => '𐳱', + 68786 => '𐳲', + 71840 => '𑣀', + 71841 => '𑣁', + 71842 => '𑣂', + 71843 => '𑣃', + 71844 => '𑣄', + 71845 => '𑣅', + 71846 => '𑣆', + 71847 => '𑣇', + 71848 => '𑣈', + 71849 => '𑣉', + 71850 => '𑣊', + 71851 => '𑣋', + 71852 => '𑣌', + 71853 => '𑣍', + 71854 => '𑣎', + 71855 => '𑣏', + 71856 => '𑣐', + 71857 => '𑣑', + 71858 => '𑣒', + 71859 => '𑣓', + 71860 => '𑣔', + 71861 => '𑣕', + 71862 => '𑣖', + 71863 => '𑣗', + 71864 => '𑣘', + 71865 => '𑣙', + 71866 => '𑣚', + 71867 => '𑣛', + 71868 => '𑣜', + 71869 => '𑣝', + 71870 => '𑣞', + 71871 => '𑣟', + 93760 => '𖹠', + 93761 => '𖹡', + 93762 => '𖹢', + 93763 => '𖹣', + 93764 => '𖹤', + 93765 => '𖹥', + 93766 => '𖹦', + 93767 => '𖹧', + 93768 => '𖹨', + 93769 => '𖹩', + 93770 => '𖹪', + 93771 => '𖹫', + 93772 => '𖹬', + 93773 => '𖹭', + 93774 => '𖹮', + 93775 => '𖹯', + 93776 => '𖹰', + 93777 => '𖹱', + 93778 => '𖹲', + 93779 => '𖹳', + 93780 => '𖹴', + 93781 => '𖹵', + 93782 => '𖹶', + 93783 => '𖹷', + 93784 => '𖹸', + 93785 => '𖹹', + 93786 => '𖹺', + 93787 => '𖹻', + 93788 => '𖹼', + 93789 => '𖹽', + 93790 => '𖹾', + 93791 => '𖹿', + 119134 => '𝅗𝅥', + 119135 => '𝅘𝅥', + 119136 => '𝅘𝅥𝅮', + 119137 => '𝅘𝅥𝅯', + 119138 => '𝅘𝅥𝅰', + 119139 => '𝅘𝅥𝅱', + 119140 => '𝅘𝅥𝅲', + 119227 => '𝆹𝅥', + 119228 => '𝆺𝅥', + 119229 => '𝆹𝅥𝅮', + 119230 => '𝆺𝅥𝅮', + 119231 => '𝆹𝅥𝅯', + 119232 => '𝆺𝅥𝅯', + 119808 => 'a', + 119809 => 'b', + 119810 => 'c', + 119811 => 'd', + 119812 => 'e', + 119813 => 'f', + 119814 => 'g', + 119815 => 'h', + 119816 => 'i', + 119817 => 'j', + 119818 => 'k', + 119819 => 'l', + 119820 => 'm', + 119821 => 'n', + 119822 => 'o', + 119823 => 'p', + 119824 => 'q', + 119825 => 'r', + 119826 => 's', + 119827 => 't', + 119828 => 'u', + 119829 => 'v', + 119830 => 'w', + 119831 => 'x', + 119832 => 'y', + 119833 => 'z', + 119834 => 'a', + 119835 => 'b', + 119836 => 'c', + 119837 => 'd', + 119838 => 'e', + 119839 => 'f', + 119840 => 'g', + 119841 => 'h', + 119842 => 'i', + 119843 => 'j', + 119844 => 'k', + 119845 => 'l', + 119846 => 'm', + 119847 => 'n', + 119848 => 'o', + 119849 => 'p', + 119850 => 'q', + 119851 => 'r', + 119852 => 's', + 119853 => 't', + 119854 => 'u', + 119855 => 'v', + 119856 => 'w', + 119857 => 'x', + 119858 => 'y', + 119859 => 'z', + 119860 => 'a', + 119861 => 'b', + 119862 => 'c', + 119863 => 'd', + 119864 => 'e', + 119865 => 'f', + 119866 => 'g', + 119867 => 'h', + 119868 => 'i', + 119869 => 'j', + 119870 => 'k', + 119871 => 'l', + 119872 => 'm', + 119873 => 'n', + 119874 => 'o', + 119875 => 'p', + 119876 => 'q', + 119877 => 'r', + 119878 => 's', + 119879 => 't', + 119880 => 'u', + 119881 => 'v', + 119882 => 'w', + 119883 => 'x', + 119884 => 'y', + 119885 => 'z', + 119886 => 'a', + 119887 => 'b', + 119888 => 'c', + 119889 => 'd', + 119890 => 'e', + 119891 => 'f', + 119892 => 'g', + 119894 => 'i', + 119895 => 'j', + 119896 => 'k', + 119897 => 'l', + 119898 => 'm', + 119899 => 'n', + 119900 => 'o', + 119901 => 'p', + 119902 => 'q', + 119903 => 'r', + 119904 => 's', + 119905 => 't', + 119906 => 'u', + 119907 => 'v', + 119908 => 'w', + 119909 => 'x', + 119910 => 'y', + 119911 => 'z', + 119912 => 'a', + 119913 => 'b', + 119914 => 'c', + 119915 => 'd', + 119916 => 'e', + 119917 => 'f', + 119918 => 'g', + 119919 => 'h', + 119920 => 'i', + 119921 => 'j', + 119922 => 'k', + 119923 => 'l', + 119924 => 'm', + 119925 => 'n', + 119926 => 'o', + 119927 => 'p', + 119928 => 'q', + 119929 => 'r', + 119930 => 's', + 119931 => 't', + 119932 => 'u', + 119933 => 'v', + 119934 => 'w', + 119935 => 'x', + 119936 => 'y', + 119937 => 'z', + 119938 => 'a', + 119939 => 'b', + 119940 => 'c', + 119941 => 'd', + 119942 => 'e', + 119943 => 'f', + 119944 => 'g', + 119945 => 'h', + 119946 => 'i', + 119947 => 'j', + 119948 => 'k', + 119949 => 'l', + 119950 => 'm', + 119951 => 'n', + 119952 => 'o', + 119953 => 'p', + 119954 => 'q', + 119955 => 'r', + 119956 => 's', + 119957 => 't', + 119958 => 'u', + 119959 => 'v', + 119960 => 'w', + 119961 => 'x', + 119962 => 'y', + 119963 => 'z', + 119964 => 'a', + 119966 => 'c', + 119967 => 'd', + 119970 => 'g', + 119973 => 'j', + 119974 => 'k', + 119977 => 'n', + 119978 => 'o', + 119979 => 'p', + 119980 => 'q', + 119982 => 's', + 119983 => 't', + 119984 => 'u', + 119985 => 'v', + 119986 => 'w', + 119987 => 'x', + 119988 => 'y', + 119989 => 'z', + 119990 => 'a', + 119991 => 'b', + 119992 => 'c', + 119993 => 'd', + 119995 => 'f', + 119997 => 'h', + 119998 => 'i', + 119999 => 'j', + 120000 => 'k', + 120001 => 'l', + 120002 => 'm', + 120003 => 'n', + 120005 => 'p', + 120006 => 'q', + 120007 => 'r', + 120008 => 's', + 120009 => 't', + 120010 => 'u', + 120011 => 'v', + 120012 => 'w', + 120013 => 'x', + 120014 => 'y', + 120015 => 'z', + 120016 => 'a', + 120017 => 'b', + 120018 => 'c', + 120019 => 'd', + 120020 => 'e', + 120021 => 'f', + 120022 => 'g', + 120023 => 'h', + 120024 => 'i', + 120025 => 'j', + 120026 => 'k', + 120027 => 'l', + 120028 => 'm', + 120029 => 'n', + 120030 => 'o', + 120031 => 'p', + 120032 => 'q', + 120033 => 'r', + 120034 => 's', + 120035 => 't', + 120036 => 'u', + 120037 => 'v', + 120038 => 'w', + 120039 => 'x', + 120040 => 'y', + 120041 => 'z', + 120042 => 'a', + 120043 => 'b', + 120044 => 'c', + 120045 => 'd', + 120046 => 'e', + 120047 => 'f', + 120048 => 'g', + 120049 => 'h', + 120050 => 'i', + 120051 => 'j', + 120052 => 'k', + 120053 => 'l', + 120054 => 'm', + 120055 => 'n', + 120056 => 'o', + 120057 => 'p', + 120058 => 'q', + 120059 => 'r', + 120060 => 's', + 120061 => 't', + 120062 => 'u', + 120063 => 'v', + 120064 => 'w', + 120065 => 'x', + 120066 => 'y', + 120067 => 'z', + 120068 => 'a', + 120069 => 'b', + 120071 => 'd', + 120072 => 'e', + 120073 => 'f', + 120074 => 'g', + 120077 => 'j', + 120078 => 'k', + 120079 => 'l', + 120080 => 'm', + 120081 => 'n', + 120082 => 'o', + 120083 => 'p', + 120084 => 'q', + 120086 => 's', + 120087 => 't', + 120088 => 'u', + 120089 => 'v', + 120090 => 'w', + 120091 => 'x', + 120092 => 'y', + 120094 => 'a', + 120095 => 'b', + 120096 => 'c', + 120097 => 'd', + 120098 => 'e', + 120099 => 'f', + 120100 => 'g', + 120101 => 'h', + 120102 => 'i', + 120103 => 'j', + 120104 => 'k', + 120105 => 'l', + 120106 => 'm', + 120107 => 'n', + 120108 => 'o', + 120109 => 'p', + 120110 => 'q', + 120111 => 'r', + 120112 => 's', + 120113 => 't', + 120114 => 'u', + 120115 => 'v', + 120116 => 'w', + 120117 => 'x', + 120118 => 'y', + 120119 => 'z', + 120120 => 'a', + 120121 => 'b', + 120123 => 'd', + 120124 => 'e', + 120125 => 'f', + 120126 => 'g', + 120128 => 'i', + 120129 => 'j', + 120130 => 'k', + 120131 => 'l', + 120132 => 'm', + 120134 => 'o', + 120138 => 's', + 120139 => 't', + 120140 => 'u', + 120141 => 'v', + 120142 => 'w', + 120143 => 'x', + 120144 => 'y', + 120146 => 'a', + 120147 => 'b', + 120148 => 'c', + 120149 => 'd', + 120150 => 'e', + 120151 => 'f', + 120152 => 'g', + 120153 => 'h', + 120154 => 'i', + 120155 => 'j', + 120156 => 'k', + 120157 => 'l', + 120158 => 'm', + 120159 => 'n', + 120160 => 'o', + 120161 => 'p', + 120162 => 'q', + 120163 => 'r', + 120164 => 's', + 120165 => 't', + 120166 => 'u', + 120167 => 'v', + 120168 => 'w', + 120169 => 'x', + 120170 => 'y', + 120171 => 'z', + 120172 => 'a', + 120173 => 'b', + 120174 => 'c', + 120175 => 'd', + 120176 => 'e', + 120177 => 'f', + 120178 => 'g', + 120179 => 'h', + 120180 => 'i', + 120181 => 'j', + 120182 => 'k', + 120183 => 'l', + 120184 => 'm', + 120185 => 'n', + 120186 => 'o', + 120187 => 'p', + 120188 => 'q', + 120189 => 'r', + 120190 => 's', + 120191 => 't', + 120192 => 'u', + 120193 => 'v', + 120194 => 'w', + 120195 => 'x', + 120196 => 'y', + 120197 => 'z', + 120198 => 'a', + 120199 => 'b', + 120200 => 'c', + 120201 => 'd', + 120202 => 'e', + 120203 => 'f', + 120204 => 'g', + 120205 => 'h', + 120206 => 'i', + 120207 => 'j', + 120208 => 'k', + 120209 => 'l', + 120210 => 'm', + 120211 => 'n', + 120212 => 'o', + 120213 => 'p', + 120214 => 'q', + 120215 => 'r', + 120216 => 's', + 120217 => 't', + 120218 => 'u', + 120219 => 'v', + 120220 => 'w', + 120221 => 'x', + 120222 => 'y', + 120223 => 'z', + 120224 => 'a', + 120225 => 'b', + 120226 => 'c', + 120227 => 'd', + 120228 => 'e', + 120229 => 'f', + 120230 => 'g', + 120231 => 'h', + 120232 => 'i', + 120233 => 'j', + 120234 => 'k', + 120235 => 'l', + 120236 => 'm', + 120237 => 'n', + 120238 => 'o', + 120239 => 'p', + 120240 => 'q', + 120241 => 'r', + 120242 => 's', + 120243 => 't', + 120244 => 'u', + 120245 => 'v', + 120246 => 'w', + 120247 => 'x', + 120248 => 'y', + 120249 => 'z', + 120250 => 'a', + 120251 => 'b', + 120252 => 'c', + 120253 => 'd', + 120254 => 'e', + 120255 => 'f', + 120256 => 'g', + 120257 => 'h', + 120258 => 'i', + 120259 => 'j', + 120260 => 'k', + 120261 => 'l', + 120262 => 'm', + 120263 => 'n', + 120264 => 'o', + 120265 => 'p', + 120266 => 'q', + 120267 => 'r', + 120268 => 's', + 120269 => 't', + 120270 => 'u', + 120271 => 'v', + 120272 => 'w', + 120273 => 'x', + 120274 => 'y', + 120275 => 'z', + 120276 => 'a', + 120277 => 'b', + 120278 => 'c', + 120279 => 'd', + 120280 => 'e', + 120281 => 'f', + 120282 => 'g', + 120283 => 'h', + 120284 => 'i', + 120285 => 'j', + 120286 => 'k', + 120287 => 'l', + 120288 => 'm', + 120289 => 'n', + 120290 => 'o', + 120291 => 'p', + 120292 => 'q', + 120293 => 'r', + 120294 => 's', + 120295 => 't', + 120296 => 'u', + 120297 => 'v', + 120298 => 'w', + 120299 => 'x', + 120300 => 'y', + 120301 => 'z', + 120302 => 'a', + 120303 => 'b', + 120304 => 'c', + 120305 => 'd', + 120306 => 'e', + 120307 => 'f', + 120308 => 'g', + 120309 => 'h', + 120310 => 'i', + 120311 => 'j', + 120312 => 'k', + 120313 => 'l', + 120314 => 'm', + 120315 => 'n', + 120316 => 'o', + 120317 => 'p', + 120318 => 'q', + 120319 => 'r', + 120320 => 's', + 120321 => 't', + 120322 => 'u', + 120323 => 'v', + 120324 => 'w', + 120325 => 'x', + 120326 => 'y', + 120327 => 'z', + 120328 => 'a', + 120329 => 'b', + 120330 => 'c', + 120331 => 'd', + 120332 => 'e', + 120333 => 'f', + 120334 => 'g', + 120335 => 'h', + 120336 => 'i', + 120337 => 'j', + 120338 => 'k', + 120339 => 'l', + 120340 => 'm', + 120341 => 'n', + 120342 => 'o', + 120343 => 'p', + 120344 => 'q', + 120345 => 'r', + 120346 => 's', + 120347 => 't', + 120348 => 'u', + 120349 => 'v', + 120350 => 'w', + 120351 => 'x', + 120352 => 'y', + 120353 => 'z', + 120354 => 'a', + 120355 => 'b', + 120356 => 'c', + 120357 => 'd', + 120358 => 'e', + 120359 => 'f', + 120360 => 'g', + 120361 => 'h', + 120362 => 'i', + 120363 => 'j', + 120364 => 'k', + 120365 => 'l', + 120366 => 'm', + 120367 => 'n', + 120368 => 'o', + 120369 => 'p', + 120370 => 'q', + 120371 => 'r', + 120372 => 's', + 120373 => 't', + 120374 => 'u', + 120375 => 'v', + 120376 => 'w', + 120377 => 'x', + 120378 => 'y', + 120379 => 'z', + 120380 => 'a', + 120381 => 'b', + 120382 => 'c', + 120383 => 'd', + 120384 => 'e', + 120385 => 'f', + 120386 => 'g', + 120387 => 'h', + 120388 => 'i', + 120389 => 'j', + 120390 => 'k', + 120391 => 'l', + 120392 => 'm', + 120393 => 'n', + 120394 => 'o', + 120395 => 'p', + 120396 => 'q', + 120397 => 'r', + 120398 => 's', + 120399 => 't', + 120400 => 'u', + 120401 => 'v', + 120402 => 'w', + 120403 => 'x', + 120404 => 'y', + 120405 => 'z', + 120406 => 'a', + 120407 => 'b', + 120408 => 'c', + 120409 => 'd', + 120410 => 'e', + 120411 => 'f', + 120412 => 'g', + 120413 => 'h', + 120414 => 'i', + 120415 => 'j', + 120416 => 'k', + 120417 => 'l', + 120418 => 'm', + 120419 => 'n', + 120420 => 'o', + 120421 => 'p', + 120422 => 'q', + 120423 => 'r', + 120424 => 's', + 120425 => 't', + 120426 => 'u', + 120427 => 'v', + 120428 => 'w', + 120429 => 'x', + 120430 => 'y', + 120431 => 'z', + 120432 => 'a', + 120433 => 'b', + 120434 => 'c', + 120435 => 'd', + 120436 => 'e', + 120437 => 'f', + 120438 => 'g', + 120439 => 'h', + 120440 => 'i', + 120441 => 'j', + 120442 => 'k', + 120443 => 'l', + 120444 => 'm', + 120445 => 'n', + 120446 => 'o', + 120447 => 'p', + 120448 => 'q', + 120449 => 'r', + 120450 => 's', + 120451 => 't', + 120452 => 'u', + 120453 => 'v', + 120454 => 'w', + 120455 => 'x', + 120456 => 'y', + 120457 => 'z', + 120458 => 'a', + 120459 => 'b', + 120460 => 'c', + 120461 => 'd', + 120462 => 'e', + 120463 => 'f', + 120464 => 'g', + 120465 => 'h', + 120466 => 'i', + 120467 => 'j', + 120468 => 'k', + 120469 => 'l', + 120470 => 'm', + 120471 => 'n', + 120472 => 'o', + 120473 => 'p', + 120474 => 'q', + 120475 => 'r', + 120476 => 's', + 120477 => 't', + 120478 => 'u', + 120479 => 'v', + 120480 => 'w', + 120481 => 'x', + 120482 => 'y', + 120483 => 'z', + 120484 => 'ı', + 120485 => 'ȷ', + 120488 => 'α', + 120489 => 'β', + 120490 => 'γ', + 120491 => 'δ', + 120492 => 'ε', + 120493 => 'ζ', + 120494 => 'η', + 120495 => 'θ', + 120496 => 'ι', + 120497 => 'κ', + 120498 => 'λ', + 120499 => 'μ', + 120500 => 'ν', + 120501 => 'ξ', + 120502 => 'ο', + 120503 => 'π', + 120504 => 'ρ', + 120505 => 'θ', + 120506 => 'σ', + 120507 => 'τ', + 120508 => 'υ', + 120509 => 'φ', + 120510 => 'χ', + 120511 => 'ψ', + 120512 => 'ω', + 120513 => '∇', + 120514 => 'α', + 120515 => 'β', + 120516 => 'γ', + 120517 => 'δ', + 120518 => 'ε', + 120519 => 'ζ', + 120520 => 'η', + 120521 => 'θ', + 120522 => 'ι', + 120523 => 'κ', + 120524 => 'λ', + 120525 => 'μ', + 120526 => 'ν', + 120527 => 'ξ', + 120528 => 'ο', + 120529 => 'π', + 120530 => 'ρ', + 120531 => 'σ', + 120532 => 'σ', + 120533 => 'τ', + 120534 => 'υ', + 120535 => 'φ', + 120536 => 'χ', + 120537 => 'ψ', + 120538 => 'ω', + 120539 => '∂', + 120540 => 'ε', + 120541 => 'θ', + 120542 => 'κ', + 120543 => 'φ', + 120544 => 'ρ', + 120545 => 'π', + 120546 => 'α', + 120547 => 'β', + 120548 => 'γ', + 120549 => 'δ', + 120550 => 'ε', + 120551 => 'ζ', + 120552 => 'η', + 120553 => 'θ', + 120554 => 'ι', + 120555 => 'κ', + 120556 => 'λ', + 120557 => 'μ', + 120558 => 'ν', + 120559 => 'ξ', + 120560 => 'ο', + 120561 => 'π', + 120562 => 'ρ', + 120563 => 'θ', + 120564 => 'σ', + 120565 => 'τ', + 120566 => 'υ', + 120567 => 'φ', + 120568 => 'χ', + 120569 => 'ψ', + 120570 => 'ω', + 120571 => '∇', + 120572 => 'α', + 120573 => 'β', + 120574 => 'γ', + 120575 => 'δ', + 120576 => 'ε', + 120577 => 'ζ', + 120578 => 'η', + 120579 => 'θ', + 120580 => 'ι', + 120581 => 'κ', + 120582 => 'λ', + 120583 => 'μ', + 120584 => 'ν', + 120585 => 'ξ', + 120586 => 'ο', + 120587 => 'π', + 120588 => 'ρ', + 120589 => 'σ', + 120590 => 'σ', + 120591 => 'τ', + 120592 => 'υ', + 120593 => 'φ', + 120594 => 'χ', + 120595 => 'ψ', + 120596 => 'ω', + 120597 => '∂', + 120598 => 'ε', + 120599 => 'θ', + 120600 => 'κ', + 120601 => 'φ', + 120602 => 'ρ', + 120603 => 'π', + 120604 => 'α', + 120605 => 'β', + 120606 => 'γ', + 120607 => 'δ', + 120608 => 'ε', + 120609 => 'ζ', + 120610 => 'η', + 120611 => 'θ', + 120612 => 'ι', + 120613 => 'κ', + 120614 => 'λ', + 120615 => 'μ', + 120616 => 'ν', + 120617 => 'ξ', + 120618 => 'ο', + 120619 => 'π', + 120620 => 'ρ', + 120621 => 'θ', + 120622 => 'σ', + 120623 => 'τ', + 120624 => 'υ', + 120625 => 'φ', + 120626 => 'χ', + 120627 => 'ψ', + 120628 => 'ω', + 120629 => '∇', + 120630 => 'α', + 120631 => 'β', + 120632 => 'γ', + 120633 => 'δ', + 120634 => 'ε', + 120635 => 'ζ', + 120636 => 'η', + 120637 => 'θ', + 120638 => 'ι', + 120639 => 'κ', + 120640 => 'λ', + 120641 => 'μ', + 120642 => 'ν', + 120643 => 'ξ', + 120644 => 'ο', + 120645 => 'π', + 120646 => 'ρ', + 120647 => 'σ', + 120648 => 'σ', + 120649 => 'τ', + 120650 => 'υ', + 120651 => 'φ', + 120652 => 'χ', + 120653 => 'ψ', + 120654 => 'ω', + 120655 => '∂', + 120656 => 'ε', + 120657 => 'θ', + 120658 => 'κ', + 120659 => 'φ', + 120660 => 'ρ', + 120661 => 'π', + 120662 => 'α', + 120663 => 'β', + 120664 => 'γ', + 120665 => 'δ', + 120666 => 'ε', + 120667 => 'ζ', + 120668 => 'η', + 120669 => 'θ', + 120670 => 'ι', + 120671 => 'κ', + 120672 => 'λ', + 120673 => 'μ', + 120674 => 'ν', + 120675 => 'ξ', + 120676 => 'ο', + 120677 => 'π', + 120678 => 'ρ', + 120679 => 'θ', + 120680 => 'σ', + 120681 => 'τ', + 120682 => 'υ', + 120683 => 'φ', + 120684 => 'χ', + 120685 => 'ψ', + 120686 => 'ω', + 120687 => '∇', + 120688 => 'α', + 120689 => 'β', + 120690 => 'γ', + 120691 => 'δ', + 120692 => 'ε', + 120693 => 'ζ', + 120694 => 'η', + 120695 => 'θ', + 120696 => 'ι', + 120697 => 'κ', + 120698 => 'λ', + 120699 => 'μ', + 120700 => 'ν', + 120701 => 'ξ', + 120702 => 'ο', + 120703 => 'π', + 120704 => 'ρ', + 120705 => 'σ', + 120706 => 'σ', + 120707 => 'τ', + 120708 => 'υ', + 120709 => 'φ', + 120710 => 'χ', + 120711 => 'ψ', + 120712 => 'ω', + 120713 => '∂', + 120714 => 'ε', + 120715 => 'θ', + 120716 => 'κ', + 120717 => 'φ', + 120718 => 'ρ', + 120719 => 'π', + 120720 => 'α', + 120721 => 'β', + 120722 => 'γ', + 120723 => 'δ', + 120724 => 'ε', + 120725 => 'ζ', + 120726 => 'η', + 120727 => 'θ', + 120728 => 'ι', + 120729 => 'κ', + 120730 => 'λ', + 120731 => 'μ', + 120732 => 'ν', + 120733 => 'ξ', + 120734 => 'ο', + 120735 => 'π', + 120736 => 'ρ', + 120737 => 'θ', + 120738 => 'σ', + 120739 => 'τ', + 120740 => 'υ', + 120741 => 'φ', + 120742 => 'χ', + 120743 => 'ψ', + 120744 => 'ω', + 120745 => '∇', + 120746 => 'α', + 120747 => 'β', + 120748 => 'γ', + 120749 => 'δ', + 120750 => 'ε', + 120751 => 'ζ', + 120752 => 'η', + 120753 => 'θ', + 120754 => 'ι', + 120755 => 'κ', + 120756 => 'λ', + 120757 => 'μ', + 120758 => 'ν', + 120759 => 'ξ', + 120760 => 'ο', + 120761 => 'π', + 120762 => 'ρ', + 120763 => 'σ', + 120764 => 'σ', + 120765 => 'τ', + 120766 => 'υ', + 120767 => 'φ', + 120768 => 'χ', + 120769 => 'ψ', + 120770 => 'ω', + 120771 => '∂', + 120772 => 'ε', + 120773 => 'θ', + 120774 => 'κ', + 120775 => 'φ', + 120776 => 'ρ', + 120777 => 'π', + 120778 => 'ϝ', + 120779 => 'ϝ', + 120782 => '0', + 120783 => '1', + 120784 => '2', + 120785 => '3', + 120786 => '4', + 120787 => '5', + 120788 => '6', + 120789 => '7', + 120790 => '8', + 120791 => '9', + 120792 => '0', + 120793 => '1', + 120794 => '2', + 120795 => '3', + 120796 => '4', + 120797 => '5', + 120798 => '6', + 120799 => '7', + 120800 => '8', + 120801 => '9', + 120802 => '0', + 120803 => '1', + 120804 => '2', + 120805 => '3', + 120806 => '4', + 120807 => '5', + 120808 => '6', + 120809 => '7', + 120810 => '8', + 120811 => '9', + 120812 => '0', + 120813 => '1', + 120814 => '2', + 120815 => '3', + 120816 => '4', + 120817 => '5', + 120818 => '6', + 120819 => '7', + 120820 => '8', + 120821 => '9', + 120822 => '0', + 120823 => '1', + 120824 => '2', + 120825 => '3', + 120826 => '4', + 120827 => '5', + 120828 => '6', + 120829 => '7', + 120830 => '8', + 120831 => '9', + 125184 => '𞤢', + 125185 => '𞤣', + 125186 => '𞤤', + 125187 => '𞤥', + 125188 => '𞤦', + 125189 => '𞤧', + 125190 => '𞤨', + 125191 => '𞤩', + 125192 => '𞤪', + 125193 => '𞤫', + 125194 => '𞤬', + 125195 => '𞤭', + 125196 => '𞤮', + 125197 => '𞤯', + 125198 => '𞤰', + 125199 => '𞤱', + 125200 => '𞤲', + 125201 => '𞤳', + 125202 => '𞤴', + 125203 => '𞤵', + 125204 => '𞤶', + 125205 => '𞤷', + 125206 => '𞤸', + 125207 => '𞤹', + 125208 => '𞤺', + 125209 => '𞤻', + 125210 => '𞤼', + 125211 => '𞤽', + 125212 => '𞤾', + 125213 => '𞤿', + 125214 => '𞥀', + 125215 => '𞥁', + 125216 => '𞥂', + 125217 => '𞥃', + 126464 => 'ا', + 126465 => 'ب', + 126466 => 'ج', + 126467 => 'د', + 126469 => 'و', + 126470 => 'ز', + 126471 => 'ح', + 126472 => 'ط', + 126473 => 'ي', + 126474 => 'ك', + 126475 => 'ل', + 126476 => 'م', + 126477 => 'ن', + 126478 => 'س', + 126479 => 'ع', + 126480 => 'ف', + 126481 => 'ص', + 126482 => 'ق', + 126483 => 'ر', + 126484 => 'ش', + 126485 => 'ت', + 126486 => 'ث', + 126487 => 'خ', + 126488 => 'ذ', + 126489 => 'ض', + 126490 => 'ظ', + 126491 => 'غ', + 126492 => 'ٮ', + 126493 => 'ں', + 126494 => 'ڡ', + 126495 => 'ٯ', + 126497 => 'ب', + 126498 => 'ج', + 126500 => 'ه', + 126503 => 'ح', + 126505 => 'ي', + 126506 => 'ك', + 126507 => 'ل', + 126508 => 'م', + 126509 => 'ن', + 126510 => 'س', + 126511 => 'ع', + 126512 => 'ف', + 126513 => 'ص', + 126514 => 'ق', + 126516 => 'ش', + 126517 => 'ت', + 126518 => 'ث', + 126519 => 'خ', + 126521 => 'ض', + 126523 => 'غ', + 126530 => 'ج', + 126535 => 'ح', + 126537 => 'ي', + 126539 => 'ل', + 126541 => 'ن', + 126542 => 'س', + 126543 => 'ع', + 126545 => 'ص', + 126546 => 'ق', + 126548 => 'ش', + 126551 => 'خ', + 126553 => 'ض', + 126555 => 'غ', + 126557 => 'ں', + 126559 => 'ٯ', + 126561 => 'ب', + 126562 => 'ج', + 126564 => 'ه', + 126567 => 'ح', + 126568 => 'ط', + 126569 => 'ي', + 126570 => 'ك', + 126572 => 'م', + 126573 => 'ن', + 126574 => 'س', + 126575 => 'ع', + 126576 => 'ف', + 126577 => 'ص', + 126578 => 'ق', + 126580 => 'ش', + 126581 => 'ت', + 126582 => 'ث', + 126583 => 'خ', + 126585 => 'ض', + 126586 => 'ظ', + 126587 => 'غ', + 126588 => 'ٮ', + 126590 => 'ڡ', + 126592 => 'ا', + 126593 => 'ب', + 126594 => 'ج', + 126595 => 'د', + 126596 => 'ه', + 126597 => 'و', + 126598 => 'ز', + 126599 => 'ح', + 126600 => 'ط', + 126601 => 'ي', + 126603 => 'ل', + 126604 => 'م', + 126605 => 'ن', + 126606 => 'س', + 126607 => 'ع', + 126608 => 'ف', + 126609 => 'ص', + 126610 => 'ق', + 126611 => 'ر', + 126612 => 'ش', + 126613 => 'ت', + 126614 => 'ث', + 126615 => 'خ', + 126616 => 'ذ', + 126617 => 'ض', + 126618 => 'ظ', + 126619 => 'غ', + 126625 => 'ب', + 126626 => 'ج', + 126627 => 'د', + 126629 => 'و', + 126630 => 'ز', + 126631 => 'ح', + 126632 => 'ط', + 126633 => 'ي', + 126635 => 'ل', + 126636 => 'م', + 126637 => 'ن', + 126638 => 'س', + 126639 => 'ع', + 126640 => 'ف', + 126641 => 'ص', + 126642 => 'ق', + 126643 => 'ر', + 126644 => 'ش', + 126645 => 'ت', + 126646 => 'ث', + 126647 => 'خ', + 126648 => 'ذ', + 126649 => 'ض', + 126650 => 'ظ', + 126651 => 'غ', + 127274 => '〔s〕', + 127275 => 'c', + 127276 => 'r', + 127277 => 'cd', + 127278 => 'wz', + 127280 => 'a', + 127281 => 'b', + 127282 => 'c', + 127283 => 'd', + 127284 => 'e', + 127285 => 'f', + 127286 => 'g', + 127287 => 'h', + 127288 => 'i', + 127289 => 'j', + 127290 => 'k', + 127291 => 'l', + 127292 => 'm', + 127293 => 'n', + 127294 => 'o', + 127295 => 'p', + 127296 => 'q', + 127297 => 'r', + 127298 => 's', + 127299 => 't', + 127300 => 'u', + 127301 => 'v', + 127302 => 'w', + 127303 => 'x', + 127304 => 'y', + 127305 => 'z', + 127306 => 'hv', + 127307 => 'mv', + 127308 => 'sd', + 127309 => 'ss', + 127310 => 'ppv', + 127311 => 'wc', + 127338 => 'mc', + 127339 => 'md', + 127340 => 'mr', + 127376 => 'dj', + 127488 => 'ほか', + 127489 => 'ココ', + 127490 => 'サ', + 127504 => '手', + 127505 => '字', + 127506 => '双', + 127507 => 'デ', + 127508 => '二', + 127509 => '多', + 127510 => '解', + 127511 => '天', + 127512 => '交', + 127513 => '映', + 127514 => '無', + 127515 => '料', + 127516 => '前', + 127517 => '後', + 127518 => '再', + 127519 => '新', + 127520 => '初', + 127521 => '終', + 127522 => '生', + 127523 => '販', + 127524 => '声', + 127525 => '吹', + 127526 => '演', + 127527 => '投', + 127528 => '捕', + 127529 => '一', + 127530 => '三', + 127531 => '遊', + 127532 => '左', + 127533 => '中', + 127534 => '右', + 127535 => '指', + 127536 => '走', + 127537 => '打', + 127538 => '禁', + 127539 => '空', + 127540 => '合', + 127541 => '満', + 127542 => '有', + 127543 => '月', + 127544 => '申', + 127545 => '割', + 127546 => '営', + 127547 => '配', + 127552 => '〔本〕', + 127553 => '〔三〕', + 127554 => '〔二〕', + 127555 => '〔安〕', + 127556 => '〔点〕', + 127557 => '〔打〕', + 127558 => '〔盗〕', + 127559 => '〔勝〕', + 127560 => '〔敗〕', + 127568 => '得', + 127569 => '可', + 130032 => '0', + 130033 => '1', + 130034 => '2', + 130035 => '3', + 130036 => '4', + 130037 => '5', + 130038 => '6', + 130039 => '7', + 130040 => '8', + 130041 => '9', + 194560 => '丽', + 194561 => '丸', + 194562 => '乁', + 194563 => '𠄢', + 194564 => '你', + 194565 => '侮', + 194566 => '侻', + 194567 => '倂', + 194568 => '偺', + 194569 => '備', + 194570 => '僧', + 194571 => '像', + 194572 => '㒞', + 194573 => '𠘺', + 194574 => '免', + 194575 => '兔', + 194576 => '兤', + 194577 => '具', + 194578 => '𠔜', + 194579 => '㒹', + 194580 => '內', + 194581 => '再', + 194582 => '𠕋', + 194583 => '冗', + 194584 => '冤', + 194585 => '仌', + 194586 => '冬', + 194587 => '况', + 194588 => '𩇟', + 194589 => '凵', + 194590 => '刃', + 194591 => '㓟', + 194592 => '刻', + 194593 => '剆', + 194594 => '割', + 194595 => '剷', + 194596 => '㔕', + 194597 => '勇', + 194598 => '勉', + 194599 => '勤', + 194600 => '勺', + 194601 => '包', + 194602 => '匆', + 194603 => '北', + 194604 => '卉', + 194605 => '卑', + 194606 => '博', + 194607 => '即', + 194608 => '卽', + 194609 => '卿', + 194610 => '卿', + 194611 => '卿', + 194612 => '𠨬', + 194613 => '灰', + 194614 => '及', + 194615 => '叟', + 194616 => '𠭣', + 194617 => '叫', + 194618 => '叱', + 194619 => '吆', + 194620 => '咞', + 194621 => '吸', + 194622 => '呈', + 194623 => '周', + 194624 => '咢', + 194625 => '哶', + 194626 => '唐', + 194627 => '啓', + 194628 => '啣', + 194629 => '善', + 194630 => '善', + 194631 => '喙', + 194632 => '喫', + 194633 => '喳', + 194634 => '嗂', + 194635 => '圖', + 194636 => '嘆', + 194637 => '圗', + 194638 => '噑', + 194639 => '噴', + 194640 => '切', + 194641 => '壮', + 194642 => '城', + 194643 => '埴', + 194644 => '堍', + 194645 => '型', + 194646 => '堲', + 194647 => '報', + 194648 => '墬', + 194649 => '𡓤', + 194650 => '売', + 194651 => '壷', + 194652 => '夆', + 194653 => '多', + 194654 => '夢', + 194655 => '奢', + 194656 => '𡚨', + 194657 => '𡛪', + 194658 => '姬', + 194659 => '娛', + 194660 => '娧', + 194661 => '姘', + 194662 => '婦', + 194663 => '㛮', + 194665 => '嬈', + 194666 => '嬾', + 194667 => '嬾', + 194668 => '𡧈', + 194669 => '寃', + 194670 => '寘', + 194671 => '寧', + 194672 => '寳', + 194673 => '𡬘', + 194674 => '寿', + 194675 => '将', + 194677 => '尢', + 194678 => '㞁', + 194679 => '屠', + 194680 => '屮', + 194681 => '峀', + 194682 => '岍', + 194683 => '𡷤', + 194684 => '嵃', + 194685 => '𡷦', + 194686 => '嵮', + 194687 => '嵫', + 194688 => '嵼', + 194689 => '巡', + 194690 => '巢', + 194691 => '㠯', + 194692 => '巽', + 194693 => '帨', + 194694 => '帽', + 194695 => '幩', + 194696 => '㡢', + 194697 => '𢆃', + 194698 => '㡼', + 194699 => '庰', + 194700 => '庳', + 194701 => '庶', + 194702 => '廊', + 194703 => '𪎒', + 194704 => '廾', + 194705 => '𢌱', + 194706 => '𢌱', + 194707 => '舁', + 194708 => '弢', + 194709 => '弢', + 194710 => '㣇', + 194711 => '𣊸', + 194712 => '𦇚', + 194713 => '形', + 194714 => '彫', + 194715 => '㣣', + 194716 => '徚', + 194717 => '忍', + 194718 => '志', + 194719 => '忹', + 194720 => '悁', + 194721 => '㤺', + 194722 => '㤜', + 194723 => '悔', + 194724 => '𢛔', + 194725 => '惇', + 194726 => '慈', + 194727 => '慌', + 194728 => '慎', + 194729 => '慌', + 194730 => '慺', + 194731 => '憎', + 194732 => '憲', + 194733 => '憤', + 194734 => '憯', + 194735 => '懞', + 194736 => '懲', + 194737 => '懶', + 194738 => '成', + 194739 => '戛', + 194740 => '扝', + 194741 => '抱', + 194742 => '拔', + 194743 => '捐', + 194744 => '𢬌', + 194745 => '挽', + 194746 => '拼', + 194747 => '捨', + 194748 => '掃', + 194749 => '揤', + 194750 => '𢯱', + 194751 => '搢', + 194752 => '揅', + 194753 => '掩', + 194754 => '㨮', + 194755 => '摩', + 194756 => '摾', + 194757 => '撝', + 194758 => '摷', + 194759 => '㩬', + 194760 => '敏', + 194761 => '敬', + 194762 => '𣀊', + 194763 => '旣', + 194764 => '書', + 194765 => '晉', + 194766 => '㬙', + 194767 => '暑', + 194768 => '㬈', + 194769 => '㫤', + 194770 => '冒', + 194771 => '冕', + 194772 => '最', + 194773 => '暜', + 194774 => '肭', + 194775 => '䏙', + 194776 => '朗', + 194777 => '望', + 194778 => '朡', + 194779 => '杞', + 194780 => '杓', + 194781 => '𣏃', + 194782 => '㭉', + 194783 => '柺', + 194784 => '枅', + 194785 => '桒', + 194786 => '梅', + 194787 => '𣑭', + 194788 => '梎', + 194789 => '栟', + 194790 => '椔', + 194791 => '㮝', + 194792 => '楂', + 194793 => '榣', + 194794 => '槪', + 194795 => '檨', + 194796 => '𣚣', + 194797 => '櫛', + 194798 => '㰘', + 194799 => '次', + 194800 => '𣢧', + 194801 => '歔', + 194802 => '㱎', + 194803 => '歲', + 194804 => '殟', + 194805 => '殺', + 194806 => '殻', + 194807 => '𣪍', + 194808 => '𡴋', + 194809 => '𣫺', + 194810 => '汎', + 194811 => '𣲼', + 194812 => '沿', + 194813 => '泍', + 194814 => '汧', + 194815 => '洖', + 194816 => '派', + 194817 => '海', + 194818 => '流', + 194819 => '浩', + 194820 => '浸', + 194821 => '涅', + 194822 => '𣴞', + 194823 => '洴', + 194824 => '港', + 194825 => '湮', + 194826 => '㴳', + 194827 => '滋', + 194828 => '滇', + 194829 => '𣻑', + 194830 => '淹', + 194831 => '潮', + 194832 => '𣽞', + 194833 => '𣾎', + 194834 => '濆', + 194835 => '瀹', + 194836 => '瀞', + 194837 => '瀛', + 194838 => '㶖', + 194839 => '灊', + 194840 => '災', + 194841 => '灷', + 194842 => '炭', + 194843 => '𠔥', + 194844 => '煅', + 194845 => '𤉣', + 194846 => '熜', + 194848 => '爨', + 194849 => '爵', + 194850 => '牐', + 194851 => '𤘈', + 194852 => '犀', + 194853 => '犕', + 194854 => '𤜵', + 194855 => '𤠔', + 194856 => '獺', + 194857 => '王', + 194858 => '㺬', + 194859 => '玥', + 194860 => '㺸', + 194861 => '㺸', + 194862 => '瑇', + 194863 => '瑜', + 194864 => '瑱', + 194865 => '璅', + 194866 => '瓊', + 194867 => '㼛', + 194868 => '甤', + 194869 => '𤰶', + 194870 => '甾', + 194871 => '𤲒', + 194872 => '異', + 194873 => '𢆟', + 194874 => '瘐', + 194875 => '𤾡', + 194876 => '𤾸', + 194877 => '𥁄', + 194878 => '㿼', + 194879 => '䀈', + 194880 => '直', + 194881 => '𥃳', + 194882 => '𥃲', + 194883 => '𥄙', + 194884 => '𥄳', + 194885 => '眞', + 194886 => '真', + 194887 => '真', + 194888 => '睊', + 194889 => '䀹', + 194890 => '瞋', + 194891 => '䁆', + 194892 => '䂖', + 194893 => '𥐝', + 194894 => '硎', + 194895 => '碌', + 194896 => '磌', + 194897 => '䃣', + 194898 => '𥘦', + 194899 => '祖', + 194900 => '𥚚', + 194901 => '𥛅', + 194902 => '福', + 194903 => '秫', + 194904 => '䄯', + 194905 => '穀', + 194906 => '穊', + 194907 => '穏', + 194908 => '𥥼', + 194909 => '𥪧', + 194910 => '𥪧', + 194912 => '䈂', + 194913 => '𥮫', + 194914 => '篆', + 194915 => '築', + 194916 => '䈧', + 194917 => '𥲀', + 194918 => '糒', + 194919 => '䊠', + 194920 => '糨', + 194921 => '糣', + 194922 => '紀', + 194923 => '𥾆', + 194924 => '絣', + 194925 => '䌁', + 194926 => '緇', + 194927 => '縂', + 194928 => '繅', + 194929 => '䌴', + 194930 => '𦈨', + 194931 => '𦉇', + 194932 => '䍙', + 194933 => '𦋙', + 194934 => '罺', + 194935 => '𦌾', + 194936 => '羕', + 194937 => '翺', + 194938 => '者', + 194939 => '𦓚', + 194940 => '𦔣', + 194941 => '聠', + 194942 => '𦖨', + 194943 => '聰', + 194944 => '𣍟', + 194945 => '䏕', + 194946 => '育', + 194947 => '脃', + 194948 => '䐋', + 194949 => '脾', + 194950 => '媵', + 194951 => '𦞧', + 194952 => '𦞵', + 194953 => '𣎓', + 194954 => '𣎜', + 194955 => '舁', + 194956 => '舄', + 194957 => '辞', + 194958 => '䑫', + 194959 => '芑', + 194960 => '芋', + 194961 => '芝', + 194962 => '劳', + 194963 => '花', + 194964 => '芳', + 194965 => '芽', + 194966 => '苦', + 194967 => '𦬼', + 194968 => '若', + 194969 => '茝', + 194970 => '荣', + 194971 => '莭', + 194972 => '茣', + 194973 => '莽', + 194974 => '菧', + 194975 => '著', + 194976 => '荓', + 194977 => '菊', + 194978 => '菌', + 194979 => '菜', + 194980 => '𦰶', + 194981 => '𦵫', + 194982 => '𦳕', + 194983 => '䔫', + 194984 => '蓱', + 194985 => '蓳', + 194986 => '蔖', + 194987 => '𧏊', + 194988 => '蕤', + 194989 => '𦼬', + 194990 => '䕝', + 194991 => '䕡', + 194992 => '𦾱', + 194993 => '𧃒', + 194994 => '䕫', + 194995 => '虐', + 194996 => '虜', + 194997 => '虧', + 194998 => '虩', + 194999 => '蚩', + 195000 => '蚈', + 195001 => '蜎', + 195002 => '蛢', + 195003 => '蝹', + 195004 => '蜨', + 195005 => '蝫', + 195006 => '螆', + 195008 => '蟡', + 195009 => '蠁', + 195010 => '䗹', + 195011 => '衠', + 195012 => '衣', + 195013 => '𧙧', + 195014 => '裗', + 195015 => '裞', + 195016 => '䘵', + 195017 => '裺', + 195018 => '㒻', + 195019 => '𧢮', + 195020 => '𧥦', + 195021 => '䚾', + 195022 => '䛇', + 195023 => '誠', + 195024 => '諭', + 195025 => '變', + 195026 => '豕', + 195027 => '𧲨', + 195028 => '貫', + 195029 => '賁', + 195030 => '贛', + 195031 => '起', + 195032 => '𧼯', + 195033 => '𠠄', + 195034 => '跋', + 195035 => '趼', + 195036 => '跰', + 195037 => '𠣞', + 195038 => '軔', + 195039 => '輸', + 195040 => '𨗒', + 195041 => '𨗭', + 195042 => '邔', + 195043 => '郱', + 195044 => '鄑', + 195045 => '𨜮', + 195046 => '鄛', + 195047 => '鈸', + 195048 => '鋗', + 195049 => '鋘', + 195050 => '鉼', + 195051 => '鏹', + 195052 => '鐕', + 195053 => '𨯺', + 195054 => '開', + 195055 => '䦕', + 195056 => '閷', + 195057 => '𨵷', + 195058 => '䧦', + 195059 => '雃', + 195060 => '嶲', + 195061 => '霣', + 195062 => '𩅅', + 195063 => '𩈚', + 195064 => '䩮', + 195065 => '䩶', + 195066 => '韠', + 195067 => '𩐊', + 195068 => '䪲', + 195069 => '𩒖', + 195070 => '頋', + 195071 => '頋', + 195072 => '頩', + 195073 => '𩖶', + 195074 => '飢', + 195075 => '䬳', + 195076 => '餩', + 195077 => '馧', + 195078 => '駂', + 195079 => '駾', + 195080 => '䯎', + 195081 => '𩬰', + 195082 => '鬒', + 195083 => '鱀', + 195084 => '鳽', + 195085 => '䳎', + 195086 => '䳭', + 195087 => '鵧', + 195088 => '𪃎', + 195089 => '䳸', + 195090 => '𪄅', + 195091 => '𪈎', + 195092 => '𪊑', + 195093 => '麻', + 195094 => '䵖', + 195095 => '黹', + 195096 => '黾', + 195097 => '鼅', + 195098 => '鼏', + 195099 => '鼖', + 195100 => '鼻', + 195101 => '𪘀', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/virama.php b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/virama.php new file mode 100644 index 000000000..1958e37ed --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/intl/unidata/virama.php @@ -0,0 +1,65 @@ + 9, + 2509 => 9, + 2637 => 9, + 2765 => 9, + 2893 => 9, + 3021 => 9, + 3149 => 9, + 3277 => 9, + 3387 => 9, + 3388 => 9, + 3405 => 9, + 3530 => 9, + 3642 => 9, + 3770 => 9, + 3972 => 9, + 4153 => 9, + 4154 => 9, + 5908 => 9, + 5940 => 9, + 6098 => 9, + 6752 => 9, + 6980 => 9, + 7082 => 9, + 7083 => 9, + 7154 => 9, + 7155 => 9, + 11647 => 9, + 43014 => 9, + 43052 => 9, + 43204 => 9, + 43347 => 9, + 43456 => 9, + 43766 => 9, + 44013 => 9, + 68159 => 9, + 69702 => 9, + 69759 => 9, + 69817 => 9, + 69939 => 9, + 69940 => 9, + 70080 => 9, + 70197 => 9, + 70378 => 9, + 70477 => 9, + 70722 => 9, + 70850 => 9, + 71103 => 9, + 71231 => 9, + 71350 => 9, + 71467 => 9, + 71737 => 9, + 71997 => 9, + 71998 => 9, + 72160 => 9, + 72244 => 9, + 72263 => 9, + 72345 => 9, + 72767 => 9, + 73028 => 9, + 73029 => 9, + 73111 => 9, +); From f17609b442ba4bb9dec999f1246607b0c4967814 Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 31 Mar 2021 22:59:06 +0200 Subject: [PATCH 29/90] Resolve #73 --- .../v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 2035deaaf..43c971e81 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -161,13 +161,12 @@ class ImapClient extends \MailSo\Net\NetClient $this->sLogginedUser = $sLogin; // $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']); - $type = ''; + $type = 'LOGIN'; // RFC3501 6.2.3 $types = [ // 'SCRAM-SHA-256' => 1, // !$encrypted // 'SCRAM-SHA-1' => 1, // !$encrypted 'CRAM-MD5' => $bUseAuthCramMd5IfSupported, 'PLAIN' => $bUseAuthPlainIfSupported, - 'LOGIN' => 1 // $encrypted ]; foreach ($types as $sasl_type => $active) { if ($active && $this->IsSupported("AUTH={$sasl_type}") && \SnappyMail\SASL::isSupported($sasl_type)) { @@ -248,12 +247,6 @@ class ImapClient extends \MailSo\Net\NetClient $this->EscapeString($sPassword) )); } -// else -// { -// $this->writeLogException( -// new Exceptions\LoginBadMethodException, -// \MailSo\Log\Enumerations\Type::NOTICE, true); -// } if (0 < \strlen($sProxyAuthUser)) { From bb836fdb59983c927da6506d69d6de2cf8d3addf Mon Sep 17 00:00:00 2001 From: djmaze Date: Wed, 31 Mar 2021 23:01:03 +0200 Subject: [PATCH 30/90] Detect IMAP LOGINDISABLED capability Enable IMAP SASL-IR capability --- .../v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 43c971e81..f0d809d8b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -149,7 +149,7 @@ class ImapClient extends \MailSo\Net\NetClient public function Login(string $sLogin, string $sPassword, string $sProxyAuthUser = '', bool $bUseAuthPlainIfSupported = true, bool $bUseAuthCramMd5IfSupported = true) : self { - if (!strlen(\trim($sLogin)) || !strlen(\trim($sPassword))) + if (!strlen(\trim($sLogin)) || !strlen(\trim($sPassword)) || $this->IsSupported('LOGINDISABLED')) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException, @@ -190,8 +190,7 @@ class ImapClient extends \MailSo\Net\NetClient { $sToken = $SASL->authenticate($sLogin, $sPassword, $sTicket); - $sTicket = \base64_decode($sTicket); - $this->oLogger->Write('ticket: '.$sTicket); + $this->oLogger->Write('ticket: '.\base64_decode($sTicket)); if ($this->oLogger) { @@ -223,7 +222,7 @@ class ImapClient extends \MailSo\Net\NetClient $this->oLogger->AddSecret($sToken); } - if ($this->IsSupported('AUTH=SASL-IR') && false) + if ($this->IsSupported('SASL-IR')) { $this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN', $sToken)); } From e96ced3bde334756756e83fa5ea36170a257aad4 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 1 Apr 2021 08:17:02 +0200 Subject: [PATCH 31/90] Bugfix: LOGINDISABLED is only for the LOGIN command --- snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index f0d809d8b..246b89694 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -149,7 +149,7 @@ class ImapClient extends \MailSo\Net\NetClient public function Login(string $sLogin, string $sPassword, string $sProxyAuthUser = '', bool $bUseAuthPlainIfSupported = true, bool $bUseAuthCramMd5IfSupported = true) : self { - if (!strlen(\trim($sLogin)) || !strlen(\trim($sPassword)) || $this->IsSupported('LOGINDISABLED')) + if (!strlen(\trim($sLogin)) || !strlen(\trim($sPassword))) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException, @@ -161,7 +161,7 @@ class ImapClient extends \MailSo\Net\NetClient $this->sLogginedUser = $sLogin; // $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']); - $type = 'LOGIN'; // RFC3501 6.2.3 + $type = $this->IsSupported('LOGINDISABLED') ? '' : 'LOGIN'; // RFC3501 6.2.3 $types = [ // 'SCRAM-SHA-256' => 1, // !$encrypted // 'SCRAM-SHA-1' => 1, // !$encrypted From fcc219cc0775103f7d42451efdded6f2628bc3a3 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 1 Apr 2021 09:48:42 +0200 Subject: [PATCH 32/90] Improved SASL --- .../app/libraries/MailSo/Imap/ImapClient.php | 85 ++++++++++--------- .../MailSo/Sieve/ManageSieveClient.php | 29 +++++-- .../v/0.0.0/app/libraries/snappymail/sasl.php | 4 +- .../app/libraries/snappymail/sasl/login.php | 2 +- .../app/libraries/snappymail/sasl/scram.php | 2 +- 5 files changed, 71 insertions(+), 51 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 246b89694..1d15db1c8 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -180,56 +180,40 @@ class ImapClient extends \MailSo\Net\NetClient try { - if ('CRAM-MD5' === $type) + if (0 === \strpos($type, 'SCRAM-SHA-')) { - $oContinuationResponse = $this->SendRequestGetResponse('AUTHENTICATE', array($type))->getLast(); - if ($oContinuationResponse && Enumerations\ResponseType::CONTINUATION === $oContinuationResponse->ResponseType) - { - $sTicket = $oContinuationResponse->ResponseList[1] ?? null; - if ($sTicket) - { - $sToken = $SASL->authenticate($sLogin, $sPassword, $sTicket); - - $this->oLogger->Write('ticket: '.\base64_decode($sTicket)); - - if ($this->oLogger) - { - $this->oLogger->AddSecret($sToken); - } - - $this->sendRaw($sToken, true, '*******'); - $this->getResponse(); - } - else - { - $this->writeLogException( - new Exceptions\LoginException, - \MailSo\Log\Enumerations\Type::NOTICE, true); - } + $sAuthzid = $this->getResponseValue($this->SendRequestGetResponse('AUTHENTICATE', array($type)), Enumerations\ResponseType::CONTINUATION); + $this->sendRaw($SASL->authenticate($sLogin, $sPassword/*, $sAuthzid*/)); + $sChallenge = $SASL->challenge($this->getResponseValue($this->getResponse(), Enumerations\ResponseType::CONTINUATION)); + if ($this->oLogger) { + $this->oLogger->AddSecret($sChallenge); } - else - { - $this->writeLogException( - new Exceptions\LoginException, - \MailSo\Log\Enumerations\Type::NOTICE, true); + $this->sendRaw($sChallenge, true, '*******'); + $sSignature = $this->getResponseValue($this->getResponse()); + $SASL->verify($sSignature); + } + else if ('CRAM-MD5' === $type) + { + $sChallenge = $this->getResponseValue($this->SendRequestGetResponse('AUTHENTICATE', array($type)), Enumerations\ResponseType::CONTINUATION); + $this->oLogger->Write('challenge: '.\base64_decode($sChallenge)); + $sAuth = $SASL->authenticate($sLogin, $sPassword, $sChallenge); + if ($this->oLogger) { + $this->oLogger->AddSecret($sAuth); } + $this->sendRaw($sAuth, true, '*******'); + $this->getResponse(); } else if ('PLAIN' === $type) { - $sToken = $SASL->authenticate($sLogin, $sPassword); - if ($this->oLogger) - { - $this->oLogger->AddSecret($sToken); + $sAuth = $SASL->authenticate($sLogin, $sPassword); + if ($this->oLogger) { + $this->oLogger->AddSecret($sAuth); } - - if ($this->IsSupported('SASL-IR')) - { - $this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN', $sToken)); - } - else - { + if ($this->IsSupported('SASL-IR')) { + $this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN', $sAuth)); + } else { $this->SendRequestGetResponse('AUTHENTICATE', array('PLAIN')); - $this->sendRaw($sToken, true, '*******'); + $this->sendRaw($sAuth, true, '*******'); $this->getResponse(); } } @@ -1025,6 +1009,23 @@ class ImapClient extends \MailSo\Net\NetClient return $this->getResponse(); } + private function getResponseValue(ResponseCollection $oResponseCollection, int $type = 0) : string + { + $oResponse = $oResponseCollection->getLast(); + if ($oResponse && (!$type || $type === $oResponse->ResponseType)) { + $sResult = $oResponse->ResponseList[1] ?? null; + if ($sResult) { + return $sResult; + } + $this->writeLogException( + new Exceptions\LoginException, + \MailSo\Log\Enumerations\Type::NOTICE, true); + } + $this->writeLogException( + new Exceptions\LoginException, + \MailSo\Log\Enumerations\Type::NOTICE, true); + } + public function GetLastResponse() : ResponseCollection { return $this->oLastResponse; diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php index 69752b485..aefaededa 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php @@ -120,12 +120,31 @@ class ManageSieveClient extends \MailSo\Net\NetClient if ($this->IsSupported('SASL')) { +// $encrypted = !empty(\stream_get_meta_data($this->rConnect)['crypto']); + $type = ''; + $types = [ +// 'SCRAM-SHA-256' => 1, // !$encrypted +// 'SCRAM-SHA-1' => 1, // !$encrypted +// 'CRAM-MD5' => 1, // $encrypted + 'PLAIN' => 1, // $encrypted + 'LOGIN' => 1 // $encrypted + ]; + foreach ($types as $sasl_type => $active) { + if ($active && $this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) { + $type = $sasl_type; + break; + } + } + + $SASL = \SnappyMail\SASL::factory($type); + $SASL->base64 = true; + $bAuth = false; try { - if ($this->IsAuthSupported('PLAIN')) + if ('PLAIN' === $type) { - $sAuth = \base64_encode($sLoginAuthKey."\0".$sLogin."\0".$sPassword); + $sAuth = $SASL->authenticate($sLogin, $sPassword, $sLoginAuthKey); if ($this->__USE_INITIAL_AUTH_PLAIN_COMMAND) { @@ -142,10 +161,10 @@ class ManageSieveClient extends \MailSo\Net\NetClient $this->parseStartupResponse($aResponse); $bAuth = true; } - else if ($this->IsAuthSupported('LOGIN')) + else if ('LOGIN' === $type) { - $sLogin = \base64_encode($sLogin); - $sPassword = \base64_encode($sPassword); + $sLogin = $SASL->authenticate($sLogin, $sPassword); + $sPassword = $SASL->challenge(''); $this->sendRequest('AUTHENTICATE "LOGIN"'); $this->sendRequest('{'.\strlen($sLogin).'+}'); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php index bc5b6020f..9d0dfdbcf 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl.php @@ -7,9 +7,9 @@ abstract class SASL public $base64 = false; - abstract public function authenticate(string $authcid, string $passphrase, ?string $authzid = null); + abstract public function authenticate(string $authcid, string $passphrase, ?string $authzid = null) : string; - public function challenge(string $challenge) : string + public function challenge(string $challenge) : ?string { return null; } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php index 743a62f6f..4a84b4835 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php @@ -16,7 +16,7 @@ class Login extends \SnappyMail\SASL return $this->encode($username); } - public function challenge(string $challenge) : string + public function challenge(string $challenge) : ?string { if ($challenge && 'Password:' !== $this->decode($challenge)) { throw new \Exception("invalid response: {$challenge}"); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php index f1b630917..f4ea724d4 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/scram.php @@ -42,7 +42,7 @@ class Scram extends \SnappyMail\SASL return $this->encode($this->gs2_header . $this->auth_message); } - public function challenge(string $challenge) : string + public function challenge(string $challenge) : ?string { $challenge = $this->decode($challenge); $values = static::parseMessage($challenge); From 94abf288ba73c8f73364294e4408f1d438ded56f Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 1 Apr 2021 12:34:01 +0200 Subject: [PATCH 33/90] Replace Imagine and PHPThumb with simple image processor Simple version uses GMagick/IMagick else GD2 --- .../Imagine/Draw/DrawerInterface.php | 149 -- .../Imagine/Effects/EffectsInterface.php | 80 - .../libraries/Imagine/Exception/Exception.php | 19 - .../Exception/InvalidArgumentException.php | 19 - .../Exception/NotSupportedException.php | 19 - .../Exception/OutOfBoundsException.php | 19 - .../Imagine/Exception/RuntimeException.php | 19 - .../Imagine/Filter/Advanced/Border.php | 98 -- .../Imagine/Filter/Advanced/Canvas.php | 74 - .../Imagine/Filter/Advanced/Grayscale.php | 30 - .../Imagine/Filter/Advanced/OnPixelBased.php | 57 - .../Filter/Advanced/RelativeResize.php | 50 - .../Imagine/Filter/Basic/ApplyMask.php | 42 - .../Imagine/Filter/Basic/Autorotate.php | 87 - .../libraries/Imagine/Filter/Basic/Copy.php | 29 - .../libraries/Imagine/Filter/Basic/Crop.php | 54 - .../libraries/Imagine/Filter/Basic/Fill.php | 43 - .../Imagine/Filter/Basic/FlipHorizontally.php | 29 - .../Imagine/Filter/Basic/FlipVertically.php | 29 - .../libraries/Imagine/Filter/Basic/Paste.php | 53 - .../libraries/Imagine/Filter/Basic/Resize.php | 48 - .../libraries/Imagine/Filter/Basic/Rotate.php | 52 - .../libraries/Imagine/Filter/Basic/Save.php | 51 - .../libraries/Imagine/Filter/Basic/Show.php | 51 - .../libraries/Imagine/Filter/Basic/Strip.php | 29 - .../Imagine/Filter/Basic/Thumbnail.php | 59 - .../Imagine/Filter/Basic/WebOptimization.php | 57 - .../Imagine/Filter/FilterInterface.php | 30 - .../libraries/Imagine/Filter/ImagineAware.php | 54 - .../Imagine/Filter/Transformation.php | 240 --- .../0.0.0/app/libraries/Imagine/Gd/Drawer.php | 333 ---- .../app/libraries/Imagine/Gd/Effects.php | 109 -- .../v/0.0.0/app/libraries/Imagine/Gd/Font.php | 41 - .../0.0.0/app/libraries/Imagine/Gd/Image.php | 735 --------- .../app/libraries/Imagine/Gd/Imagine.php | 195 --- .../0.0.0/app/libraries/Imagine/Gd/Layers.php | 144 -- .../app/libraries/Imagine/Gmagick/Drawer.php | 356 ----- .../app/libraries/Imagine/Gmagick/Effects.php | 106 -- .../app/libraries/Imagine/Gmagick/Font.php | 63 - .../app/libraries/Imagine/Gmagick/Image.php | 786 --------- .../app/libraries/Imagine/Gmagick/Imagine.php | 167 -- .../app/libraries/Imagine/Gmagick/Layers.php | 272 ---- .../libraries/Imagine/Image/AbstractFont.php | 75 - .../libraries/Imagine/Image/AbstractImage.php | 120 -- .../Imagine/Image/AbstractImagine.php | 79 - .../Imagine/Image/AbstractLayers.php | 61 - .../0.0.0/app/libraries/Imagine/Image/Box.php | 122 -- .../libraries/Imagine/Image/BoxInterface.php | 94 -- .../Imagine/Image/Fill/FillInterface.php | 30 - .../Image/Fill/Gradient/Horizontal.php | 28 - .../Imagine/Image/Fill/Gradient/Linear.php | 95 -- .../Imagine/Image/Fill/Gradient/Vertical.php | 28 - .../libraries/Imagine/Image/FontInterface.php | 51 - .../Imagine/Image/Histogram/Bucket.php | 56 - .../Imagine/Image/Histogram/Range.php | 56 - .../Imagine/Image/ImageInterface.php | 173 -- .../Imagine/Image/ImagineInterface.php | 83 - .../Imagine/Image/LayersInterface.php | 107 -- .../Imagine/Image/ManipulatorInterface.php | 181 --- .../Image/Metadata/AbstractMetadataReader.php | 101 -- .../Image/Metadata/DefaultMetadataReader.php | 42 - .../Image/Metadata/ExifMetadataReader.php | 114 -- .../Imagine/Image/Metadata/MetadataBag.php | 97 -- .../Metadata/MetadataReaderInterface.php | 48 - .../libraries/Imagine/Image/Palette/CMYK.php | 118 -- .../Imagine/Image/Palette/Color/CMYK.php | 219 --- .../Image/Palette/Color/ColorInterface.php | 95 -- .../Imagine/Image/Palette/Color/Gray.php | 164 -- .../Imagine/Image/Palette/Color/RGB.php | 214 --- .../Imagine/Image/Palette/ColorParser.php | 153 -- .../Imagine/Image/Palette/Grayscale.php | 123 -- .../Image/Palette/PaletteInterface.php | 87 - .../libraries/Imagine/Image/Palette/RGB.php | 129 -- .../app/libraries/Imagine/Image/Point.php | 88 - .../libraries/Imagine/Image/Point/Center.php | 77 - .../Imagine/Image/PointInterface.php | 56 - .../app/libraries/Imagine/Image/Profile.php | 60 - .../Imagine/Image/ProfileInterface.php | 29 - .../app/libraries/Imagine/Imagick/Drawer.php | 404 ----- .../app/libraries/Imagine/Imagick/Effects.php | 113 -- .../app/libraries/Imagine/Imagick/Font.php | 68 - .../app/libraries/Imagine/Imagick/Image.php | 880 ---------- .../app/libraries/Imagine/Imagick/Imagine.php | 176 -- .../app/libraries/Imagine/Imagick/Layers.php | 271 ---- .../v/0.0.0/app/libraries/PHPThumb/GD.php | 1417 ----------------- .../0.0.0/app/libraries/PHPThumb/PHPThumb.php | 143 -- .../libraries/PHPThumb/PluginInterface.php | 12 - .../libraries/PHPThumb/Plugins/Reflection.php | 261 --- .../app/libraries/RainLoop/Actions/Raw.php | 69 +- .../0.0.0/app/libraries/snappymail/image.php | 10 + .../app/libraries/snappymail/image/gd2.php | 247 +++ .../snappymail/image/gd2/imagerotate.inc | 73 + .../libraries/snappymail/image/gmagick.php | 60 + .../libraries/snappymail/image/imagick.php | 28 + 94 files changed, 447 insertions(+), 12215 deletions(-) delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Draw/DrawerInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Effects/EffectsInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Exception/Exception.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Exception/InvalidArgumentException.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Exception/NotSupportedException.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Exception/OutOfBoundsException.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Exception/RuntimeException.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Border.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Canvas.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Grayscale.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/OnPixelBased.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/RelativeResize.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/ApplyMask.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Autorotate.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Copy.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Crop.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Fill.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipHorizontally.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipVertically.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Paste.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Resize.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Rotate.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Save.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Show.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Strip.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Thumbnail.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/WebOptimization.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/FilterInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/ImagineAware.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Filter/Transformation.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gd/Drawer.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gd/Effects.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gd/Font.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gd/Image.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gd/Imagine.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gd/Layers.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Drawer.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Effects.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Font.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Image.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Imagine.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Layers.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractFont.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImage.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImagine.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractLayers.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Box.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/BoxInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/FillInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Horizontal.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Linear.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Vertical.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/FontInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Bucket.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Range.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/ImageInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/ImagineInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/LayersInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/ManipulatorInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/AbstractMetadataReader.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/DefaultMetadataReader.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/ExifMetadataReader.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataBag.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataReaderInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/CMYK.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/CMYK.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/ColorInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/Gray.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/RGB.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/ColorParser.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Grayscale.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/PaletteInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/RGB.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Point.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Point/Center.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/PointInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/Profile.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Image/ProfileInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Drawer.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Effects.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Font.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Image.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Imagine.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Layers.php delete mode 100644 snappymail/v/0.0.0/app/libraries/PHPThumb/GD.php delete mode 100644 snappymail/v/0.0.0/app/libraries/PHPThumb/PHPThumb.php delete mode 100644 snappymail/v/0.0.0/app/libraries/PHPThumb/PluginInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/PHPThumb/Plugins/Reflection.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/image.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/image/gd2.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/image/gd2/imagerotate.inc create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Draw/DrawerInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Draw/DrawerInterface.php deleted file mode 100644 index c48dff876..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Draw/DrawerInterface.php +++ /dev/null @@ -1,149 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Draw; - -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\PointInterface; -use Imagine\Exception\RuntimeException; - -/** - * Interface for the drawer - */ -interface DrawerInterface -{ - /** - * Draws an arc on a starting at a given x, y coordinates under a given - * start and end angles - * - * @param PointInterface $center - * @param BoxInterface $size - * @param integer $start - * @param integer $end - * @param ColorInterface $color - * @param integer $thickness - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1); - - /** - * Same as arc, but also connects end points with a straight line - * - * @param PointInterface $center - * @param BoxInterface $size - * @param integer $start - * @param integer $end - * @param ColorInterface $color - * @param Boolean $fill - * @param integer $thickness - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1); - - /** - * Draws and ellipse with center at the given x, y coordinates, and given - * width and height - * - * @param PointInterface $center - * @param BoxInterface $size - * @param ColorInterface $color - * @param Boolean $fill - * @param integer $thickness - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1); - - /** - * Draws a line from start(x, y) to end(x, y) coordinates - * - * @param PointInterface $start - * @param PointInterface $end - * @param ColorInterface $outline - * @param integer $thickness - * - * @return DrawerInterface - */ - public function line(PointInterface $start, PointInterface $end, ColorInterface $outline, $thickness = 1); - - /** - * Same as arc, but connects end points and the center - * - * @param PointInterface $center - * @param BoxInterface $size - * @param integer $start - * @param integer $end - * @param ColorInterface $color - * @param Boolean $fill - * @param integer $thickness - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1); - - /** - * Places a one pixel point at specific coordinates and fills it with - * specified color - * - * @param PointInterface $position - * @param ColorInterface $color - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function dot(PointInterface $position, ColorInterface $color); - - /** - * Draws a polygon using array of x, y coordinates. Must contain at least - * three coordinates - * - * @param array $coordinates - * @param ColorInterface $color - * @param Boolean $fill - * @param integer $thickness - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1); - - /** - * Annotates image with specified text at a given position starting on the - * top left of the final text box - * - * The rotation is done CW - * - * @param string $string - * @param AbstractFont $font - * @param PointInterface $position - * @param integer $angle - * @param integer $width - * - * @throws RuntimeException - * - * @return DrawerInterface - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Effects/EffectsInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Effects/EffectsInterface.php deleted file mode 100644 index 3593d75da..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Effects/EffectsInterface.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Effects; - -use Imagine\Exception\RuntimeException; -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * Interface for the effects - */ -interface EffectsInterface -{ - /** - * Apply gamma correction - * - * @param float $correction - * @return EffectsInterface - * - * @throws RuntimeException - */ - public function gamma($correction); - - /** - * Invert the colors of the image - * - * @return EffectsInterface - * - * @throws RuntimeException - */ - public function negative(); - - /** - * Grayscale the image - * - * @return EffectsInterface - * - * @throws RuntimeException - */ - public function grayscale(); - - /** - * Colorize the image - * - * @param ColorInterface $color - * - * @return EffectsInterface - * - * @throws RuntimeException - */ - public function colorize(ColorInterface $color); - - /** - * Sharpens the image - * - * @return EffectsInterface - * - * @throws RuntimeException - */ - public function sharpen(); - - /** - * Blur the image - * - * @param float|int $sigma - * - * @return EffectsInterface - * - * @throws RuntimeException - */ - public function blur($sigma); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/Exception.php b/snappymail/v/0.0.0/app/libraries/Imagine/Exception/Exception.php deleted file mode 100644 index 39a67afed..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/Exception.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -/** - * Imagine-specific exception - */ -interface Exception -{ -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/InvalidArgumentException.php b/snappymail/v/0.0.0/app/libraries/Imagine/Exception/InvalidArgumentException.php deleted file mode 100644 index 5ac139672..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -/** - * Imagine-specific invalid argument exception - */ -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/NotSupportedException.php b/snappymail/v/0.0.0/app/libraries/Imagine/Exception/NotSupportedException.php deleted file mode 100644 index fd68ce7c0..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/NotSupportedException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -/** - * Should be used when a driver does not support an operation. - */ -class NotSupportedException extends RuntimeException implements Exception -{ -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/OutOfBoundsException.php b/snappymail/v/0.0.0/app/libraries/Imagine/Exception/OutOfBoundsException.php deleted file mode 100644 index f51cc9b4f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/OutOfBoundsException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -/** - * Imagine-specific out of bounds exception - */ -class OutOfBoundsException extends \OutOfBoundsException implements Exception -{ -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/RuntimeException.php b/snappymail/v/0.0.0/app/libraries/Imagine/Exception/RuntimeException.php deleted file mode 100644 index 205ad85e8..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Exception/RuntimeException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -/** - * Imagine-specific runtime exception - */ -class RuntimeException extends \RuntimeException implements Exception -{ -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Border.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Border.php deleted file mode 100644 index 47a3ceb1f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Border.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Advanced; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Point; - -/** - * A border filter - */ -class Border implements FilterInterface -{ - /** - * @var ColorInterface - */ - private $color; - - /** - * @var integer - */ - private $width; - - /** - * @var integer - */ - private $height; - - /** - * Constructs Border filter with given color, width and height - * - * @param ColorInterface $color - * @param integer $width Width of the border on the left and right sides of the image - * @param integer $height Height of the border on the top and bottom sides of the image - */ - public function __construct(ColorInterface $color, $width = 1, $height = 1) - { - $this->color = $color; - $this->width = $width; - $this->height = $height; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - $size = $image->getSize(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - $draw = $image->draw(); - - // Draw top and bottom lines - $draw - ->line( - new Point(0, 0), - new Point($width - 1, 0), - $this->color, - $this->height - ) - ->line( - new Point($width - 1, $height - 1), - new Point(0, $height - 1), - $this->color, - $this->height - ) - ; - - // Draw sides - $draw - ->line( - new Point(0, 0), - new Point(0, $height - 1), - $this->color, - $this->width - ) - ->line( - new Point($width - 1, 0), - new Point($width - 1, $height - 1), - $this->color, - $this->width - ) - ; - - return $image; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Canvas.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Canvas.php deleted file mode 100644 index 685b8ae73..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Canvas.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Advanced; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\ImagineInterface; - -/** - * A canvas filter - */ -class Canvas implements FilterInterface -{ - /** - * @var BoxInterface - */ - private $size; - - /** - * @var PointInterface - */ - private $placement; - - /** - * @var ColorInterface - */ - private $background; - - /** - * @var ImagineInterface - */ - private $imagine; - - /** - * Constructs Canvas filter with given width and height and the placement of the current image - * inside the new canvas - * - * @param ImagineInterface $imagine - * @param BoxInterface $size - * @param PointInterface $placement - * @param ColorInterface $background - */ - public function __construct(ImagineInterface $imagine, BoxInterface $size, PointInterface $placement = null, ColorInterface $background = null) - { - $this->imagine = $imagine; - $this->size = $size; - $this->placement = $placement ?: new Point(0, 0); - $this->background = $background; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - $canvas = $this->imagine->create($this->size, $this->background); - $canvas->paste($image, $this->placement); - - return $canvas; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Grayscale.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Grayscale.php deleted file mode 100644 index b3a08b403..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/Grayscale.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Advanced; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\Point; - -/** - * The Grayscale filter calculates the gray-value based on RGB. - */ -class Grayscale extends OnPixelBased implements FilterInterface -{ - public function __construct() - { - parent::__construct(function (ImageInterface $image, Point $point) { - $color = $image->getColorAt($point); - $image->draw()->dot($point, $color->grayscale()); - }); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/OnPixelBased.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/OnPixelBased.php deleted file mode 100644 index ef297eaf6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/OnPixelBased.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Advanced; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\Point; - -/** - * The OnPixelBased takes a callable, and for each pixel, this callable is called with the - * image (\Imagine\Image\ImageInterface) and the current point (\Imagine\Image\Point) - */ -class OnPixelBased implements FilterInterface -{ - protected $callback; - - public function __construct($callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException('$callback has to be callable'); - } - - $this->callback = $callback; - } - - /** - * Applies scheduled transformation to ImageInterface instance - * Returns processed ImageInterface instance - * - * @param ImageInterface $image - * - * @return ImageInterface - */ - public function apply(ImageInterface $image) - { - $w = $image->getSize()->getWidth(); - $h = $image->getSize()->getHeight(); - - for ($x = 0; $x < $w; $x++) { - for ($y = 0; $y < $h; $y++) { - call_user_func($this->callback, $image, new Point($x, $y)); - } - } - - return $image; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/RelativeResize.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/RelativeResize.php deleted file mode 100644 index 954587ad2..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Advanced/RelativeResize.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Advanced; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; - -/** - * The RelativeResize filter allows images to be resized relative to their - * existing dimensions. - */ -class RelativeResize implements FilterInterface -{ - private $method; - private $parameter; - - /** - * Constructs a RelativeResize filter with the given method and argument. - * - * @param string $method BoxInterface method - * @param mixed $parameter Parameter for BoxInterface method - */ - public function __construct($method, $parameter) - { - if (!in_array($method, array('heighten', 'increase', 'scale', 'widen'))) { - throw new InvalidArgumentException(sprintf('Unsupported method: ', $method)); - } - - $this->method = $method; - $this->parameter = $parameter; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->resize(call_user_func(array($image->getSize(), $this->method), $this->parameter)); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/ApplyMask.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/ApplyMask.php deleted file mode 100644 index 2be2786f9..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/ApplyMask.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; - -/** - * An apply mask filter - */ -class ApplyMask implements FilterInterface -{ - /** - * @var ImageInterface - */ - private $mask; - - /** - * @param ImageInterface $mask - */ - public function __construct(ImageInterface $mask) - { - $this->mask = $mask; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->applyMask($this->mask); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Autorotate.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Autorotate.php deleted file mode 100644 index 3ef7cbec7..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Autorotate.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * Rotates an image automatically based on exif information. - * - * Your attention please: This filter requires the use of the - * ExifMetadataReader to work. - * - * @see https://imagine.readthedocs.org/en/latest/usage/metadata.html - */ -class Autorotate implements FilterInterface -{ - private $color; - - /** - * @param string|array|ColorInterface $color A color - */ - public function __construct($color = '000000') - { - $this->color = $color; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - $metadata = $image->metadata(); - - switch (isset($metadata['ifd0.Orientation']) ? $metadata['ifd0.Orientation'] : null) { - case 1: // top-left - break; - case 2: // top-right - $image->flipHorizontally(); - break; - case 3: // bottom-right - $image->rotate(180, $this->getColor($image)); - break; - case 4: // bottom-left - $image->flipHorizontally(); - $image->rotate(180, $this->getColor($image)); - break; - case 5: // left-top - $image->flipHorizontally(); - $image->rotate(-90, $this->getColor($image)); - break; - case 6: // right-top - $image->rotate(90, $this->getColor($image)); - break; - case 7: // right-bottom - $image->flipHorizontally(); - $image->rotate(90, $this->getColor($image)); - break; - case 8: // left-bottom - $image->rotate(-90, $this->getColor($image)); - break; - default: // Invalid orientation - break; - } - - return $image; - } - - private function getColor(ImageInterface $image) - { - if ($this->color instanceof ColorInterface) { - return $this->color; - } - - return $image->palette()->color($this->color); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Copy.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Copy.php deleted file mode 100644 index 781b23a7e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Copy.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; - -/** - * A copy filter - */ -class Copy implements FilterInterface -{ - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->copy(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Crop.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Crop.php deleted file mode 100644 index 6559e2298..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Crop.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\PointInterface; -use Imagine\Filter\FilterInterface; - -/** - * A crop filter - */ -class Crop implements FilterInterface -{ - /** - * @var PointInterface - */ - private $start; - - /** - * @var BoxInterface - */ - private $size; - - /** - * Constructs a Crop filter with given x, y, coordinates and crop width and - * height values - * - * @param PointInterface $start - * @param BoxInterface $size - */ - public function __construct(PointInterface $start, BoxInterface $size) - { - $this->start = $start; - $this->size = $size; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->crop($this->start, $this->size); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Fill.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Fill.php deleted file mode 100644 index 4be0d0f2b..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Fill.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\Fill\FillInterface; -use Imagine\Image\ImageInterface; - -/** - * A fill filter - */ -class Fill implements FilterInterface -{ - /** - * @var FillInterface - */ - private $fill; - - /** - * @param FillInterface $fill - */ - public function __construct(FillInterface $fill) - { - $this->fill = $fill; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->fill($this->fill); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipHorizontally.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipHorizontally.php deleted file mode 100644 index 50203748d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipHorizontally.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Filter\FilterInterface; - -/** - * A "flip horizontally" filter - */ -class FlipHorizontally implements FilterInterface -{ - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->flipHorizontally(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipVertically.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipVertically.php deleted file mode 100644 index 684c31b3d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/FlipVertically.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Filter\FilterInterface; - -/** - * A "flip vertically" filter - */ -class FlipVertically implements FilterInterface -{ - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->flipVertically(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Paste.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Paste.php deleted file mode 100644 index bd274a179..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Paste.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Image\PointInterface; -use Imagine\Filter\FilterInterface; - -/** - * A paste filter - */ -class Paste implements FilterInterface -{ - /** - * @var ImageInterface - */ - private $image; - - /** - * @var PointInterface - */ - private $start; - - /** - * Constructs a Paste filter with given ImageInterface to paste and x, y - * coordinates of target position - * - * @param ImageInterface $image - * @param PointInterface $start - */ - public function __construct(ImageInterface $image, PointInterface $start) - { - $this->image = $image; - $this->start = $start; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->paste($this->image, $this->start); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Resize.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Resize.php deleted file mode 100644 index 934cfe2fd..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Resize.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\BoxInterface; - -/** - * A resize filter - */ -class Resize implements FilterInterface -{ - /** - * @var BoxInterface - */ - private $size; - private $filter; - - /** - * Constructs Resize filter with given width and height - * - * @param BoxInterface $size - * @param string $filter - */ - public function __construct(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED) - { - $this->size = $size; - $this->filter = $filter; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->resize($this->size, $this->filter); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Rotate.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Rotate.php deleted file mode 100644 index 7b5553ff9..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Rotate.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Filter\FilterInterface; - -/** - * A rotate filter - */ -class Rotate implements FilterInterface -{ - /** - * @var integer - */ - private $angle; - - /** - * @var ColorInterface - */ - private $background; - - /** - * Constructs Rotate filter with given angle and background color - * - * @param integer $angle - * @param ColorInterface $background - */ - public function __construct($angle, ColorInterface $background = null) - { - $this->angle = $angle; - $this->background = $background; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->rotate($this->angle, $this->background); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Save.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Save.php deleted file mode 100644 index 0f76625a1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Save.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Filter\FilterInterface; - -/** - * A save filter - */ -class Save implements FilterInterface -{ - /** - * @var string - */ - private $path; - - /** - * @var array - */ - private $options; - - /** - * Constructs Save filter with given path and options - * - * @param string $path - * @param array $options - */ - public function __construct($path = null, array $options = array()) - { - $this->path = $path; - $this->options = $options; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->save($this->path, $this->options); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Show.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Show.php deleted file mode 100644 index a7d4cbc44..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Show.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Filter\FilterInterface; - -/** - * A show filter - */ -class Show implements FilterInterface -{ - /** - * @var string - */ - private $format; - - /** - * @var array - */ - private $options; - - /** - * Constructs the Show filter with given format and options - * - * @param string $format - * @param array $options - */ - public function __construct($format, array $options = array()) - { - $this->format = $format; - $this->options = $options; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->show($this->format, $this->options); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Strip.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Strip.php deleted file mode 100644 index c096a51bc..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Strip.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Filter\FilterInterface; - -/** - * A strip filter - */ -class Strip implements FilterInterface -{ - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->strip(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Thumbnail.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Thumbnail.php deleted file mode 100644 index 3176f1c39..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/Thumbnail.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Image\BoxInterface; -use Imagine\Filter\FilterInterface; - -/** - * A thumbnail filter - */ -class Thumbnail implements FilterInterface -{ - /** - * @var BoxInterface - */ - private $size; - - /** - * @var string - */ - private $mode; - - /** - * @var string - */ - private $filter; - - /** - * Constructs the Thumbnail filter with given width, height and mode - * - * @param BoxInterface $size - * @param string $mode - * @param string $filter - */ - public function __construct(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED) - { - $this->size = $size; - $this->mode = $mode; - $this->filter = $filter; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return $image->thumbnail($this->size, $this->mode, $this->filter); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/WebOptimization.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/WebOptimization.php deleted file mode 100644 index 37f5f3f1a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Basic/WebOptimization.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Image\ImageInterface; -use Imagine\Image\Palette\RGB; -use Imagine\Filter\FilterInterface; - -/** - * A filter to render web-optimized images - */ -class WebOptimization implements FilterInterface -{ - private $palette; - private $path; - private $options; - - public function __construct($path = null, array $options = array()) - { - $this->path = $path; - $this->options = array_replace(array( - 'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH, - 'resolution-y' => 72, - 'resolution-x' => 72, - ), $options); - $this->palette = new RGB(); - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - $image - ->usePalette($this->palette) - ->strip(); - - if (is_callable($this->path)) { - $path = call_user_func($this->path, $image); - } elseif (null !== $this->path) { - $path = $this->path; - } else { - return $image; - } - - return $image->save($path, $this->options); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/FilterInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/FilterInterface.php deleted file mode 100644 index e23ab70e0..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/FilterInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter; - -use Imagine\Image\ImageInterface; - -/** - * Interface for imagine filters - */ -interface FilterInterface -{ - /** - * Applies scheduled transformation to ImageInterface instance - * Returns processed ImageInterface instance - * - * @param ImageInterface $image - * - * @return ImageInterface - */ - public function apply(ImageInterface $image); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/ImagineAware.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/ImagineAware.php deleted file mode 100644 index 606c5254c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/ImagineAware.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Image\ImagineInterface; - -/** - * ImagineAware base class - */ -abstract class ImagineAware implements FilterInterface -{ - /** - * An ImagineInterface instance. - * - * @var ImagineInterface - */ - private $imagine; - - /** - * Set ImagineInterface instance. - * - * @param ImagineInterface $imagine An ImagineInterface instance - */ - public function setImagine(ImagineInterface $imagine) - { - $this->imagine = $imagine; - } - - /** - * Get ImagineInterface instance. - * - * @return ImagineInterface - * - * @throws InvalidArgumentException - */ - public function getImagine() - { - if (!$this->imagine instanceof ImagineInterface) { - throw new InvalidArgumentException(sprintf('In order to use %s pass an Imagine\Image\ImagineInterface instance to filter constructor', get_class($this))); - } - - return $this->imagine; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Transformation.php b/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Transformation.php deleted file mode 100644 index 26c023a0f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Filter/Transformation.php +++ /dev/null @@ -1,240 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Filter\Basic\ApplyMask; -use Imagine\Filter\Basic\Copy; -use Imagine\Filter\Basic\Crop; -use Imagine\Filter\Basic\Fill; -use Imagine\Filter\Basic\FlipVertically; -use Imagine\Filter\Basic\FlipHorizontally; -use Imagine\Filter\Basic\Paste; -use Imagine\Filter\Basic\Resize; -use Imagine\Filter\Basic\Rotate; -use Imagine\Filter\Basic\Save; -use Imagine\Filter\Basic\Show; -use Imagine\Filter\Basic\Strip; -use Imagine\Filter\Basic\Thumbnail; -use Imagine\Image\ImageInterface; -use Imagine\Image\ImagineInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Fill\FillInterface; -use Imagine\Image\ManipulatorInterface; -use Imagine\Image\PointInterface; - -/** - * A transformation filter - */ -final class Transformation implements FilterInterface, ManipulatorInterface -{ - /** - * @var array - */ - private $filters = array(); - - /** - * @var array - */ - private $sorted; - - /** - * An ImagineInterface instance. - * - * @var ImagineInterface - */ - private $imagine; - - /** - * Class constructor. - * - * @param ImagineInterface $imagine An ImagineInterface instance - */ - public function __construct(ImagineInterface $imagine = null) - { - $this->imagine = $imagine; - } - - /** - * Applies a given FilterInterface onto given ImageInterface and returns - * modified ImageInterface - * - * @param ImageInterface $image - * @param FilterInterface $filter - * - * @return ImageInterface - * @throws InvalidArgumentException - */ - public function applyFilter(ImageInterface $image, FilterInterface $filter) - { - if ($filter instanceof ImagineAware) { - if ($this->imagine === null) { - throw new InvalidArgumentException(sprintf('In order to use %s pass an Imagine\Image\ImagineInterface instance to Transformation constructor', get_class($filter))); - } - $filter->setImagine($this->imagine); - } - - return $filter->apply($image); - } - - /** - * Returns a list of filters sorted by their priority. Filters with same priority will be returned in the order they were added. - * - * @return array - */ - public function getFilters() - { - if (null === $this->sorted) { - if (!empty($this->filters)) { - ksort($this->filters); - $this->sorted = call_user_func_array('array_merge', $this->filters); - } else { - $this->sorted = array(); - } - } - - return $this->sorted; - } - - /** - * {@inheritdoc} - */ - public function apply(ImageInterface $image) - { - return array_reduce( - $this->getFilters(), - array($this, 'applyFilter'), - $image - ); - } - - /** - * {@inheritdoc} - */ - public function copy() - { - return $this->add(new Copy()); - } - - /** - * {@inheritdoc} - */ - public function crop(PointInterface $start, BoxInterface $size) - { - return $this->add(new Crop($start, $size)); - } - - /** - * {@inheritdoc} - */ - public function flipHorizontally() - { - return $this->add(new FlipHorizontally()); - } - - /** - * {@inheritdoc} - */ - public function flipVertically() - { - return $this->add(new FlipVertically()); - } - - /** - * {@inheritdoc} - */ - public function strip() - { - return $this->add(new Strip()); - } - - /** - * {@inheritdoc} - */ - public function paste(ImageInterface $image, PointInterface $start) - { - return $this->add(new Paste($image, $start)); - } - - /** - * {@inheritdoc} - */ - public function applyMask(ImageInterface $mask) - { - return $this->add(new ApplyMask($mask)); - } - - /** - * {@inheritdoc} - */ - public function fill(FillInterface $fill) - { - return $this->add(new Fill($fill)); - } - - /** - * {@inheritdoc} - */ - public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED) - { - return $this->add(new Resize($size, $filter)); - } - - /** - * {@inheritdoc} - */ - public function rotate($angle, ColorInterface $background = null) - { - return $this->add(new Rotate($angle, $background)); - } - - /** - * {@inheritdoc} - */ - public function save($path = null, array $options = array()) - { - return $this->add(new Save($path, $options)); - } - - /** - * {@inheritdoc} - */ - public function show($format, array $options = array()) - { - return $this->add(new Show($format, $options)); - } - - /** - * {@inheritdoc} - */ - public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED) - { - return $this->add(new Thumbnail($size, $mode, $filter)); - } - - /** - * Registers a given FilterInterface in an internal array of filters for - * later application to an instance of ImageInterface - * - * @param FilterInterface $filter - * @param int $priority - * @return Transformation - */ - public function add(FilterInterface $filter, $priority = 0) - { - $this->filters[$priority][] = $filter; - $this->sorted = null; - - return $this; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Drawer.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Drawer.php deleted file mode 100644 index e5d74e0d9..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Drawer.php +++ /dev/null @@ -1,333 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Palette\Color\RGB as RGBColor; -use Imagine\Image\PointInterface; - -/** - * Drawer implementation using the GD library - */ -final class Drawer implements DrawerInterface -{ - /** - * @var resource - */ - private $resource; - - /** - * @var array - */ - private $info; - - /** - * Constructs Drawer with a given gd image resource - * - * @param resource $resource - */ - public function __construct($resource) - { - $this->loadGdInfo(); - $this->resource = $resource; - } - - /** - * {@inheritdoc} - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1) - { - imagesetthickness($this->resource, max(1, (int) $thickness)); - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw arc operation failed'); - } - - if (false === imagearc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color))) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw arc operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw arc operation failed'); - } - - return $this; - } - - /** - * This function does not work properly because of a bug in GD - * - * {@inheritdoc} - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1) - { - imagesetthickness($this->resource, max(1, (int) $thickness)); - - if ($fill) { - $style = IMG_ARC_CHORD; - } else { - $style = IMG_ARC_CHORD | IMG_ARC_NOFILL; - } - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw chord operation failed'); - } - - if (false === imagefilledarc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color), $style)) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw chord operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw chord operation failed'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1) - { - imagesetthickness($this->resource, max(1, (int) $thickness)); - - if ($fill) { - $callback = 'imagefilledellipse'; - } else { - $callback = 'imageellipse'; - } - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw ellipse operation failed'); - } - - if (false === $callback($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $this->getColor($color))) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw ellipse operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw ellipse operation failed'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function line(PointInterface $start, PointInterface $end, ColorInterface $color, $thickness = 1) - { - imagesetthickness($this->resource, max(1, (int) $thickness)); - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw line operation failed'); - } - - if (false === imageline($this->resource, $start->getX(), $start->getY(), $end->getX(), $end->getY(), $this->getColor($color))) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw line operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw line operation failed'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1) - { - imagesetthickness($this->resource, max(1, (int) $thickness)); - - if ($fill) { - $style = IMG_ARC_EDGED; - } else { - $style = IMG_ARC_EDGED | IMG_ARC_NOFILL; - } - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw chord operation failed'); - } - - if (false === imagefilledarc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color), $style)) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw chord operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw chord operation failed'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function dot(PointInterface $position, ColorInterface $color) - { - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw point operation failed'); - } - - if (false === imagesetpixel($this->resource, $position->getX(), $position->getY(), $this->getColor($color))) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw point operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw point operation failed'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1) - { - imagesetthickness($this->resource, max(1, (int) $thickness)); - - if (count($coordinates) < 3) { - throw new InvalidArgumentException(sprintf('A polygon must consist of at least 3 points, %d given', count($coordinates))); - } - - $points = call_user_func_array('array_merge', array_map(function (PointInterface $p) { - return array($p->getX(), $p->getY()); - }, $coordinates)); - - if ($fill) { - $callback = 'imagefilledpolygon'; - } else { - $callback = 'imagepolygon'; - } - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Draw polygon operation failed'); - } - - if (false === $callback($this->resource, $points, count($coordinates), $this->getColor($color))) { - imagealphablending($this->resource, false); - throw new RuntimeException('Draw polygon operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Draw polygon operation failed'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null) - { - if (!$this->info['FreeType Support']) { - throw new RuntimeException('GD is not compiled with FreeType support'); - } - - $angle = -1 * $angle; - $fontsize = $font->getSize(); - $fontfile = $font->getFile(); - $x = $position->getX(); - $y = $position->getY() + $fontsize; - - if ($width !== null) { - $string = $this->wrapText($string, $font, $angle, $width); - } - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Font mask operation failed'); - } - - if (false === imagefttext($this->resource, $fontsize, $angle, $x, $y, $this->getColor($font->getColor()), $fontfile, $string)) { - imagealphablending($this->resource, false); - throw new RuntimeException('Font mask operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Font mask operation failed'); - } - - return $this; - } - - /** - * Internal - * - * Generates a GD color from Color instance - * - * @param ColorInterface $color - * - * @return resource - * - * @throws RuntimeException - * @throws InvalidArgumentException - */ - private function getColor(ColorInterface $color) - { - if (!$color instanceof RGBColor) { - throw new InvalidArgumentException('GD driver only supports RGB colors'); - } - - $gdColor = imagecolorallocatealpha($this->resource, $color->getRed(), $color->getGreen(), $color->getBlue(), (100 - $color->getAlpha()) * 127 / 100); - if (false === $gdColor) { - throw new RuntimeException(sprintf('Unable to allocate color "RGB(%s, %s, %s)" with transparency of %d percent', $color->getRed(), $color->getGreen(), $color->getBlue(), $color->getAlpha())); - } - - return $gdColor; - } - - private function loadGdInfo() - { - if (!function_exists('gd_info')) { - throw new RuntimeException('Gd not installed'); - } - - $this->info = gd_info(); - } - - /** - * Internal - * - * Fits a string into box with given width - */ - private function wrapText($string, AbstractFont $font, $angle, $width) - { - $result = ''; - $words = explode(' ', $string); - foreach ($words as $word) { - $teststring = $result . ' ' . $word; - $testbox = imagettfbbox($font->getSize(), $angle, $font->getFile(), $teststring); - if ($testbox[2] > $width) { - $result .= ($result == '' ? '' : "\n") . $word; - } else { - $result .= ($result == '' ? '' : ' ') . $word; - } - } - - return $result; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Effects.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Effects.php deleted file mode 100644 index d86c4f8a2..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Effects.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Effects\EffectsInterface; -use Imagine\Exception\RuntimeException; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Palette\Color\RGB as RGBColor; - -/** - * Effects implementation using the GD library - */ -class Effects implements EffectsInterface -{ - private $resource; - - public function __construct($resource) - { - $this->resource = $resource; - } - - /** - * {@inheritdoc} - */ - public function gamma($correction) - { - if (false === imagegammacorrect($this->resource, 1.0, $correction)) { - throw new RuntimeException('Failed to apply gamma correction to the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function negative() - { - if (false === imagefilter($this->resource, IMG_FILTER_NEGATE)) { - throw new RuntimeException('Failed to negate the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function grayscale() - { - if (false === imagefilter($this->resource, IMG_FILTER_GRAYSCALE)) { - throw new RuntimeException('Failed to grayscale the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function colorize(ColorInterface $color) - { - if (!$color instanceof RGBColor) { - throw new RuntimeException('Colorize effects only accepts RGB color in GD context'); - } - - if (false === imagefilter($this->resource, IMG_FILTER_COLORIZE, $color->getRed(), $color->getGreen(), $color->getBlue())) { - throw new RuntimeException('Failed to colorize the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function sharpen() - { - $sharpenMatrix = array(array(-1,-1,-1), array(-1,16,-1), array(-1,-1,-1)); - $divisor = array_sum(array_map('array_sum', $sharpenMatrix)); - - if (false === imageconvolution($this->resource, $sharpenMatrix, $divisor, 0)) { - throw new RuntimeException('Failed to sharpen the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function blur($sigma = 1) - { - if (false === imagefilter($this->resource, IMG_FILTER_GAUSSIAN_BLUR)) { - throw new RuntimeException('Failed to blur the image'); - } - - return $this; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Font.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Font.php deleted file mode 100644 index 8bc2b04d4..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Font.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\Box; - -/** - * Font implementation using the GD library - */ -final class Font extends AbstractFont -{ - /** - * {@inheritdoc} - */ - public function box($string, $angle = 0) - { - if (!function_exists('imageftbbox')) { - throw new RuntimeException('GD must have been compiled with `--with-freetype-dir` option to use the Font feature.'); - } - - $angle = -1 * $angle; - $info = imageftbbox($this->size, $angle, $this->file, $string); - $xs = array($info[0], $info[2], $info[4], $info[6]); - $ys = array($info[1], $info[3], $info[5], $info[7]); - $width = abs(max($xs) - min($xs)); - $height = abs(max($ys) - min($ys)); - - return new Box($width, $height); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Image.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Image.php deleted file mode 100644 index 73d7b9506..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Image.php +++ /dev/null @@ -1,735 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Image\AbstractImage; -use Imagine\Image\ImageInterface; -use Imagine\Image\Box; -use Imagine\Image\BoxInterface; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Fill\FillInterface; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; -use Imagine\Image\Palette\PaletteInterface; -use Imagine\Image\Palette\Color\RGB as RGBColor; -use Imagine\Image\ProfileInterface; -use Imagine\Image\Palette\RGB; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\RuntimeException; - -/** - * Image implementation using the GD library - */ -final class Image extends AbstractImage -{ - /** - * @var resource - */ - private $resource; - - /** - * @var Layers|null - */ - private $layers; - - /** - * @var PaletteInterface - */ - private $palette; - - /** - * Constructs a new Image instance - * - * @param resource $resource - * @param PaletteInterface $palette - * @param MetadataBag $metadata - */ - public function __construct($resource, PaletteInterface $palette, MetadataBag $metadata) - { - $this->metadata = $metadata; - $this->palette = $palette; - $this->resource = $resource; - } - - /** - * Makes sure the current image resource is destroyed - */ - public function __destruct() - { - if (is_resource($this->resource) && 'gd' === get_resource_type($this->resource)) { - imagedestroy($this->resource); - } - } - - /** - * Returns Gd resource - * - * @return resource - */ - public function getGdResource() - { - return $this->resource; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function copy() - { - $size = $this->getSize(); - $copy = $this->createImage($size, 'copy'); - - if (false === imagecopy($copy, $this->resource, 0, 0, 0, 0, $size->getWidth(), $size->getHeight())) { - throw new RuntimeException('Image copy operation failed'); - } - - return new Image($copy, $this->palette, $this->metadata); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function crop(PointInterface $start, BoxInterface $size) - { - if (!$start->in($this->getSize())) { - throw new OutOfBoundsException('Crop coordinates must start at minimum 0, 0 position from top left corner, crop height and width must be positive integers and must not exceed the current image borders'); - } - - $width = $size->getWidth(); - $height = $size->getHeight(); - - $dest = $this->createImage($size, 'crop'); - - if (false === imagecopy($dest, $this->resource, 0, 0, $start->getX(), $start->getY(), $width, $height)) { - throw new RuntimeException('Image crop operation failed'); - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function paste(ImageInterface $image, PointInterface $start) - { - if (!$image instanceof self) { - throw new InvalidArgumentException(sprintf('Gd\Image can only paste() Gd\Image instances, %s given', get_class($image))); - } - - $size = $image->getSize(); - if (!$this->getSize()->contains($size, $start)) { - throw new OutOfBoundsException('Cannot paste image of the given size at the specified position, as it moves outside of the current image\'s box'); - } - - imagealphablending($this->resource, true); - imagealphablending($image->resource, true); - - if (false === imagecopy($this->resource, $image->resource, $start->getX(), $start->getY(), 0, 0, $size->getWidth(), $size->getHeight())) { - throw new RuntimeException('Image paste operation failed'); - } - - imagealphablending($this->resource, false); - imagealphablending($image->resource, false); - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED) - { - if (ImageInterface::FILTER_UNDEFINED !== $filter) { - throw new InvalidArgumentException('Unsupported filter type, GD only supports ImageInterface::FILTER_UNDEFINED filter'); - } - - $width = $size->getWidth(); - $height = $size->getHeight(); - - $dest = $this->createImage($size, 'resize'); - - imagealphablending($this->resource, true); - imagealphablending($dest, true); - - if (false === imagecopyresampled($dest, $this->resource, 0, 0, 0, 0, $width, $height, imagesx($this->resource), imagesy($this->resource))) { - throw new RuntimeException('Image resize operation failed'); - } - - imagealphablending($this->resource, false); - imagealphablending($dest, false); - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function rotate($angle, ColorInterface $background = null) - { - $color = $background ? $background : $this->palette->color('fff'); - $resource = imagerotate($this->resource, -1 * $angle, $this->getColor($color)); - - if (false === $resource) { - throw new RuntimeException('Image rotate operation failed'); - } - - imagedestroy($this->resource); - $this->resource = $resource; - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function save($path = null, array $options = array()) - { - $path = null === $path ? (isset($this->metadata['filepath']) ? $this->metadata['filepath'] : $path) : $path; - - if (null === $path) { - throw new RuntimeException('You can omit save path only if image has been open from a file'); - } - - if (isset($options['format'])) { - $format = $options['format']; - } elseif ('' !== $extension = pathinfo($path, \PATHINFO_EXTENSION)) { - $format = $extension; - } else { - $originalPath = isset($this->metadata['filepath']) ? $this->metadata['filepath'] : null; - $format = pathinfo($originalPath, \PATHINFO_EXTENSION); - } - - $this->saveOrOutput($format, $options, $path); - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function show($format, array $options = array()) - { - header('Content-type: '.$this->getMimeType($format)); - - $this->saveOrOutput($format, $options); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function get($format, array $options = array()) - { - ob_start(); - $this->saveOrOutput($format, $options); - - return ob_get_clean(); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return $this->get('png'); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function flipHorizontally() - { - $size = $this->getSize(); - $width = $size->getWidth(); - $height = $size->getHeight(); - $dest = $this->createImage($size, 'flip'); - - for ($i = 0; $i < $width; $i++) { - if (false === imagecopy($dest, $this->resource, $i, 0, ($width - 1) - $i, 0, 1, $height)) { - throw new RuntimeException('Horizontal flip operation failed'); - } - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function flipVertically() - { - $size = $this->getSize(); - $width = $size->getWidth(); - $height = $size->getHeight(); - $dest = $this->createImage($size, 'flip'); - - for ($i = 0; $i < $height; $i++) { - if (false === imagecopy($dest, $this->resource, 0, $i, 0, ($height - 1) - $i, $width, 1)) { - throw new RuntimeException('Vertical flip operation failed'); - } - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - final public function strip() - { - // GD strips profiles and comment, so there's nothing to do here - return $this; - } - - /** - * {@inheritdoc} - */ - public function draw() - { - return new Drawer($this->resource); - } - - /** - * {@inheritdoc} - */ - public function effects() - { - return new Effects($this->resource); - } - - /** - * {@inheritdoc} - */ - public function getSize() - { - return new Box(imagesx($this->resource), imagesy($this->resource)); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function applyMask(ImageInterface $mask) - { - if (!$mask instanceof self) { - throw new InvalidArgumentException('Cannot mask non-gd images'); - } - - $size = $this->getSize(); - $maskSize = $mask->getSize(); - - if ($size != $maskSize) { - throw new InvalidArgumentException(sprintf('The given mask doesn\'t match current image\'s size, Current mask\'s dimensions are %s, while image\'s dimensions are %s', $maskSize, $size)); - } - - for ($x = 0, $width = $size->getWidth(); $x < $width; $x++) { - for ($y = 0, $height = $size->getHeight(); $y < $height; $y++) { - $position = new Point($x, $y); - $color = $this->getColorAt($position); - $maskColor = $mask->getColorAt($position); - $round = (int) round(max($color->getAlpha(), (100 - $color->getAlpha()) * $maskColor->getRed() / 255)); - - if (false === imagesetpixel($this->resource, $x, $y, $this->getColor($color->dissolve($round - $color->getAlpha())))) { - throw new RuntimeException('Apply mask operation failed'); - } - } - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function fill(FillInterface $fill) - { - $size = $this->getSize(); - - for ($x = 0, $width = $size->getWidth(); $x < $width; $x++) { - for ($y = 0, $height = $size->getHeight(); $y < $height; $y++) { - if (false === imagesetpixel($this->resource, $x, $y, $this->getColor($fill->getColor(new Point($x, $y))))) { - throw new RuntimeException('Fill operation failed'); - } - } - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function mask() - { - $mask = $this->copy(); - - if (false === imagefilter($mask->resource, IMG_FILTER_GRAYSCALE)) { - throw new RuntimeException('Mask operation failed'); - } - - return $mask; - } - - /** - * {@inheritdoc} - */ - public function histogram() - { - $size = $this->getSize(); - $colors = array(); - - for ($x = 0, $width = $size->getWidth(); $x < $width; $x++) { - for ($y = 0, $height = $size->getHeight(); $y < $height; $y++) { - $colors[] = $this->getColorAt(new Point($x, $y)); - } - } - - return array_unique($colors); - } - - /** - * {@inheritdoc} - */ - public function getColorAt(PointInterface $point) - { - if (!$point->in($this->getSize())) { - throw new RuntimeException(sprintf('Error getting color at point [%s,%s]. The point must be inside the image of size [%s,%s]', $point->getX(), $point->getY(), $this->getSize()->getWidth(), $this->getSize()->getHeight())); - } - - $index = imagecolorat($this->resource, $point->getX(), $point->getY()); - $info = imagecolorsforindex($this->resource, $index); - - return $this->palette->color(array($info['red'], $info['green'], $info['blue']), max(min(100 - (int) round($info['alpha'] / 127 * 100), 100), 0)); - } - - /** - * {@inheritdoc} - */ - public function layers() - { - if (null === $this->layers) { - $this->layers = new Layers($this, $this->palette, $this->resource); - } - - return $this->layers; - } - - /** - * {@inheritdoc} - **/ - public function interlace($scheme) - { - static $supportedInterlaceSchemes = array( - ImageInterface::INTERLACE_NONE => 0, - ImageInterface::INTERLACE_LINE => 1, - ImageInterface::INTERLACE_PLANE => 1, - ImageInterface::INTERLACE_PARTITION => 1, - ); - - if (!array_key_exists($scheme, $supportedInterlaceSchemes)) { - throw new InvalidArgumentException('Unsupported interlace type'); - } - - imageinterlace($this->resource, $supportedInterlaceSchemes[$scheme]); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function palette() - { - return $this->palette; - } - - /** - * {@inheritdoc} - */ - public function profile(ProfileInterface $profile) - { - throw new RuntimeException('GD driver does not support color profiles'); - } - - /** - * {@inheritdoc} - */ - public function usePalette(PaletteInterface $palette) - { - if (!$palette instanceof RGB) { - throw new RuntimeException('GD driver only supports RGB palette'); - } - - $this->palette = $palette; - - return $this; - } - - /** - * Internal - * - * Performs save or show operation using one of GD's image... functions - * - * @param string $format - * @param array $options - * @param string $filename - * - * @throws InvalidArgumentException - * @throws RuntimeException - */ - private function saveOrOutput($format, array $options, $filename = null) - { - $format = $this->normalizeFormat($format); - - if (!$this->supported($format)) { - throw new InvalidArgumentException(sprintf('Saving image in "%s" format is not supported, please use one of the following extensions: "%s"', $format, implode('", "', $this->supported()))); - } - - $save = 'image'.$format; - $args = array(&$this->resource, $filename); - - // Preserve BC until version 1.0 - if (isset($options['quality']) && !isset($options['png_compression_level'])) { - $options['png_compression_level'] = round((100 - $options['quality']) * 9 / 100); - } - if (isset($options['filters']) && !isset($options['png_compression_filter'])) { - $options['png_compression_filter'] = $options['filters']; - } - - $options = $this->updateSaveOptions($options); - - if ($format === 'jpeg' && isset($options['jpeg_quality'])) { - $args[] = $options['jpeg_quality']; - } - - if ($format === 'png') { - if (isset($options['png_compression_level'])) { - if ($options['png_compression_level'] < 0 || $options['png_compression_level'] > 9) { - throw new InvalidArgumentException('png_compression_level option should be an integer from 0 to 9'); - } - $args[] = $options['png_compression_level']; - } else { - $args[] = -1; // use default level - } - - if (isset($options['png_compression_filter'])) { - if (~PNG_ALL_FILTERS & $options['png_compression_filter']) { - throw new InvalidArgumentException('png_compression_filter option should be a combination of the PNG_FILTER_XXX constants'); - } - $args[] = $options['png_compression_filter']; - } - } - - if (($format === 'wbmp' || $format === 'xbm') && isset($options['foreground'])) { - $args[] = $options['foreground']; - } - - $this->setExceptionHandler(); - - if (false === call_user_func_array($save, $args)) { - throw new RuntimeException('Save operation failed'); - } - - $this->resetExceptionHandler(); - } - - /** - * Internal - * - * Generates a GD image - * - * @param BoxInterface $size - * @param string the operation initiating the creation - * - * @return resource - * - * @throws RuntimeException - * - */ - private function createImage(BoxInterface $size, $operation) - { - $resource = imagecreatetruecolor($size->getWidth(), $size->getHeight()); - - if (false === $resource) { - throw new RuntimeException('Image '.$operation.' failed'); - } - - if (false === imagealphablending($resource, false) || false === imagesavealpha($resource, true)) { - throw new RuntimeException('Image '.$operation.' failed'); - } - - if (function_exists('imageantialias')) { - imageantialias($resource, true); - } - - $transparent = imagecolorallocatealpha($resource, 255, 255, 255, 127); - imagefill($resource, 0, 0, $transparent); - imagecolortransparent($resource, $transparent); - - return $resource; - } - - /** - * Internal - * - * Generates a GD color from Color instance - * - * @param ColorInterface $color - * - * @return integer A color identifier - * - * @throws RuntimeException - * @throws InvalidArgumentException - */ - private function getColor(ColorInterface $color) - { - if (!$color instanceof RGBColor) { - throw new InvalidArgumentException('GD driver only supports RGB colors'); - } - - $index = imagecolorallocatealpha($this->resource, $color->getRed(), $color->getGreen(), $color->getBlue(), round(127 * (100 - $color->getAlpha()) / 100)); - - if (false === $index) { - throw new RuntimeException(sprintf('Unable to allocate color "RGB(%s, %s, %s)" with transparency of %d percent', $color->getRed(), $color->getGreen(), $color->getBlue(), $color->getAlpha())); - } - - return $index; - } - - /** - * Internal - * - * Normalizes a given format name - * - * @param string $format - * - * @return string - */ - private function normalizeFormat($format) - { - $format = strtolower($format); - - if ('jpg' === $format || 'pjpeg' === $format) { - $format = 'jpeg'; - } - - return $format; - } - - /** - * Internal - * - * Checks whether a given format is supported by GD library - * - * @param string $format - * - * @return Boolean - */ - private function supported($format = null) - { - $formats = array('gif', 'jpeg', 'png', 'wbmp', 'xbm'); - - if (null === $format) { - return $formats; - } - - return in_array($format, $formats); - } - - private function setExceptionHandler() - { - set_error_handler(function ($errno, $errstr, $errfile, $errline) { - if (0 === error_reporting()) { - return; - } - - throw new RuntimeException($errstr, $errno, new \ErrorException($errstr, 0, $errno, $errfile, $errline)); - }, E_WARNING | E_NOTICE); - } - - private function resetExceptionHandler() - { - restore_error_handler(); - } - - /** - * Internal - * - * Get the mime type based on format. - * - * @param string $format - * - * @return string mime-type - * - * @throws RuntimeException - */ - private function getMimeType($format) - { - $format = $this->normalizeFormat($format); - - if (!$this->supported($format)) { - throw new RuntimeException('Invalid format'); - } - - static $mimeTypes = array( - 'jpeg' => 'image/jpeg', - 'gif' => 'image/gif', - 'png' => 'image/png', - 'wbmp' => 'image/vnd.wap.wbmp', - 'xbm' => 'image/xbm', - ); - - return $mimeTypes[$format]; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Imagine.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Imagine.php deleted file mode 100644 index f9e950876..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Imagine.php +++ /dev/null @@ -1,195 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Image\AbstractImagine; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Palette\RGB; -use Imagine\Image\Palette\PaletteInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\RGB as RGBColor; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; - -/** - * Imagine implementation using the GD library - */ -final class Imagine extends AbstractImagine -{ - /** - * @var array - */ - private $info; - - /** - * @throws RuntimeException - */ - public function __construct() - { - $this->loadGdInfo(); - $this->requireGdVersion('2.0.1'); - } - - /** - * {@inheritdoc} - */ - public function create(BoxInterface $size, ColorInterface $color = null) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $resource = imagecreatetruecolor($width, $height); - - if (false === $resource) { - throw new RuntimeException('Create operation failed'); - } - - $palette = null !== $color ? $color->getPalette() : new RGB(); - $color = $color ? $color : $palette->color('fff'); - - if (!$color instanceof RGBColor) { - throw new InvalidArgumentException('GD driver only supports RGB colors'); - } - - $index = imagecolorallocatealpha($resource, $color->getRed(), $color->getGreen(), $color->getBlue(), round(127 * (100 - $color->getAlpha()) / 100)); - - if (false === $index) { - throw new RuntimeException('Unable to allocate color'); - } - - if (false === imagefill($resource, 0, 0, $index)) { - throw new RuntimeException('Could not set background color fill'); - } - - if ($color->getAlpha() >= 95) { - imagecolortransparent($resource, $index); - } - - return $this->wrap($resource, $palette, new MetadataBag()); - } - - /** - * {@inheritdoc} - */ - public function open($path) - { - $path = $this->checkPath($path); - $data = @file_get_contents($path); - - if (false === $data) { - throw new RuntimeException(sprintf('Failed to open file %s', $path)); - } - - $resource = @imagecreatefromstring($data); - - if (!is_resource($resource)) { - throw new RuntimeException(sprintf('Unable to open image %s', $path)); - } - - return $this->wrap($resource, new RGB(), $this->getMetadataReader()->readFile($path)); - } - - /** - * {@inheritdoc} - */ - public function load($string) - { - return $this->doLoad($string, $this->getMetadataReader()->readData($string)); - } - - /** - * {@inheritdoc} - */ - public function read($resource) - { - if (!is_resource($resource)) { - throw new InvalidArgumentException('Variable does not contain a stream resource'); - } - - $content = stream_get_contents($resource); - - if (false === $content) { - throw new InvalidArgumentException('Cannot read resource content'); - } - - return $this->doLoad($content, $this->getMetadataReader()->readStream($resource)); - } - - /** - * {@inheritdoc} - */ - public function font($file, $size, ColorInterface $color) - { - if (!$this->info['FreeType Support']) { - throw new RuntimeException('GD is not compiled with FreeType support'); - } - - return new Font($file, $size, $color); - } - - private function wrap($resource, PaletteInterface $palette, MetadataBag $metadata) - { - if (!imageistruecolor($resource)) { - list($width, $height) = array(imagesx($resource), imagesy($resource)); - - // create transparent truecolor canvas - $truecolor = imagecreatetruecolor($width, $height); - $transparent = imagecolorallocatealpha($truecolor, 255, 255, 255, 127); - - imagefill($truecolor, 0, 0, $transparent); - imagecolortransparent($truecolor, $transparent); - - imagecopymerge($truecolor, $resource, 0, 0, 0, 0, $width, $height, 100); - - imagedestroy($resource); - $resource = $truecolor; - } - - if (false === imagealphablending($resource, false) || false === imagesavealpha($resource, true)) { - throw new RuntimeException('Could not set alphablending, savealpha and antialias values'); - } - - if (function_exists('imageantialias')) { - imageantialias($resource, true); - } - - return new Image($resource, $palette, $metadata); - } - - private function loadGdInfo() - { - if (!function_exists('gd_info')) { - throw new RuntimeException('Gd not installed'); - } - - $this->info = gd_info(); - } - - private function requireGdVersion($version) - { - if (version_compare(GD_VERSION, $version, '<')) { - throw new RuntimeException(sprintf('GD2 version %s or higher is required', $version)); - } - } - - private function doLoad($string, MetadataBag $metadata) - { - $resource = @imagecreatefromstring($string); - - if (!is_resource($resource)) { - throw new RuntimeException('An image could not be created from the given input'); - } - - return $this->wrap($resource, new RGB(), $metadata); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Layers.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Layers.php deleted file mode 100644 index 93c7a2695..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gd/Layers.php +++ /dev/null @@ -1,144 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Image\AbstractLayers; -use Imagine\Exception\RuntimeException; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\PaletteInterface; -use Imagine\Exception\NotSupportedException; - -class Layers extends AbstractLayers -{ - private $image; - private $offset; - private $resource; - private $palette; - - public function __construct(Image $image, PaletteInterface $palette, $resource) - { - if (!is_resource($resource)) { - throw new RuntimeException('Invalid Gd resource provided'); - } - - $this->image = $image; - $this->resource = $resource; - $this->offset = 0; - $this->palette = $palette; - } - - /** - * {@inheritdoc} - */ - public function merge() - { - } - - /** - * {@inheritdoc} - */ - public function coalesce() - { - } - - /** - * {@inheritdoc} - */ - public function animate($format, $delay, $loops) - { - return $this; - } - - /** - * {@inheritdoc} - */ - public function current() - { - return new Image($this->resource, $this->palette, new MetadataBag()); - } - - /** - * {@inheritdoc} - */ - public function key() - { - return $this->offset; - } - - /** - * {@inheritdoc} - */ - public function next() - { - ++$this->offset; - } - - /** - * {@inheritdoc} - */ - public function rewind() - { - $this->offset = 0; - } - - /** - * {@inheritdoc} - */ - public function valid() - { - return $this->offset < 1; - } - - /** - * {@inheritdoc} - */ - public function count() - { - return 1; - } - - /** - * {@inheritdoc} - */ - public function offsetExists($offset) - { - return 0 === $offset; - } - - /** - * {@inheritdoc} - */ - public function offsetGet($offset) - { - if (0 === $offset) { - return new Image($this->resource, $this->palette, new MetadataBag()); - } - - throw new RuntimeException('GD only supports one layer at offset 0'); - } - - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value) - { - throw new NotSupportedException('GD does not support layer set'); - } - - /** - * {@inheritdoc} - */ - public function offsetUnset($offset) - { - throw new NotSupportedException('GD does not support layer unset'); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Drawer.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Drawer.php deleted file mode 100644 index de6cc9048..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Drawer.php +++ /dev/null @@ -1,356 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\NotSupportedException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; - -/** - * Drawer implementation using the Gmagick PHP extension - */ -final class Drawer implements DrawerInterface -{ - /** - * @var \Gmagick - */ - private $gmagick; - - /** - * @param \Gmagick $gmagick - */ - public function __construct(\Gmagick $gmagick) - { - $this->gmagick = $gmagick; - } - - /** - * {@inheritdoc} - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $arc = new \GmagickDraw(); - - $arc->setstrokecolor($pixel); - $arc->setstrokewidth(max(1, (int) $thickness)); - $arc->setfillcolor('transparent'); - $arc->arc( - $x - $width / 2, - $y - $height / 2, - $x + $width / 2, - $y + $height / 2, - $start, - $end - ); - - $this->gmagick->drawImage($arc); - - $pixel = null; - - $arc = null; - } catch (\GmagickException $e) { - throw new RuntimeException('Draw arc operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $chord = new \GmagickDraw(); - - $chord->setstrokecolor($pixel); - $chord->setstrokewidth(max(1, (int) $thickness)); - - if ($fill) { - $chord->setfillcolor($pixel); - } else { - $x1 = round($x + $width / 2 * cos(deg2rad($start))); - $y1 = round($y + $height / 2 * sin(deg2rad($start))); - $x2 = round($x + $width / 2 * cos(deg2rad($end))); - $y2 = round($y + $height / 2 * sin(deg2rad($end))); - - $this->line(new Point($x1, $y1), new Point($x2, $y2), $color); - - $chord->setfillcolor('transparent'); - } - - $chord->arc($x - $width / 2, $y - $height / 2, $x + $width / 2, $y + $height / 2, $start, $end); - - $this->gmagick->drawImage($chord); - - $pixel = null; - - $chord = null; - } catch (\GmagickException $e) { - throw new RuntimeException('Draw chord operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $ellipse = new \GmagickDraw(); - - $ellipse->setstrokecolor($pixel); - $ellipse->setstrokewidth(max(1, (int) $thickness)); - - if ($fill) { - $ellipse->setfillcolor($pixel); - } else { - $ellipse->setfillcolor('transparent'); - } - - $ellipse->ellipse( - $center->getX(), - $center->getY(), - $width / 2, - $height / 2, - 0, 360 - ); - - $this->gmagick->drawImage($ellipse); - - $pixel = null; - - $ellipse = null; - } catch (\GmagickException $e) { - throw new RuntimeException('Draw ellipse operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function line(PointInterface $start, PointInterface $end, ColorInterface $color, $thickness = 1) - { - try { - $pixel = $this->getColor($color); - $line = new \GmagickDraw(); - - $line->setstrokecolor($pixel); - $line->setstrokewidth(max(1, (int) $thickness)); - $line->setfillcolor($pixel); - $line->line( - $start->getX(), - $start->getY(), - $end->getX(), - $end->getY() - ); - - $this->gmagick->drawImage($line); - - $pixel = null; - - $line = null; - } catch (\GmagickException $e) { - throw new RuntimeException('Draw line operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $x1 = round($center->getX() + $width / 2 * cos(deg2rad($start))); - $y1 = round($center->getY() + $height / 2 * sin(deg2rad($start))); - $x2 = round($center->getX() + $width / 2 * cos(deg2rad($end))); - $y2 = round($center->getY() + $height / 2 * sin(deg2rad($end))); - - if ($fill) { - $this->chord($center, $size, $start, $end, $color, true, $thickness); - $this->polygon( - array( - $center, - new Point($x1, $y1), - new Point($x2, $y2), - ), - $color, - true, - $thickness - ); - } else { - $this->arc($center, $size, $start, $end, $color, $thickness); - $this->line($center, new Point($x1, $y1), $color, $thickness); - $this->line($center, new Point($x2, $y2), $color, $thickness); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function dot(PointInterface $position, ColorInterface $color) - { - $x = $position->getX(); - $y = $position->getY(); - - try { - $pixel = $this->getColor($color); - $point = new \GmagickDraw(); - - $point->setfillcolor($pixel); - $point->point($x, $y); - - $this->gmagick->drawimage($point); - - $pixel = null; - $point = null; - } catch (\GmagickException $e) { - throw new RuntimeException('Draw point operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1) - { - if (count($coordinates) < 3) { - throw new InvalidArgumentException(sprintf('Polygon must consist of at least 3 coordinates, %d given', count($coordinates))); - } - - $points = array_map(function (PointInterface $p) { - return array('x' => $p->getX(), 'y' => $p->getY()); - }, $coordinates); - - try { - $pixel = $this->getColor($color); - $polygon = new \GmagickDraw(); - - $polygon->setstrokecolor($pixel); - $polygon->setstrokewidth(max(1, (int) $thickness)); - - if ($fill) { - $polygon->setfillcolor($pixel); - } else { - $polygon->setfillcolor('transparent'); - } - - $polygon->polygon($points); - - $this->gmagick->drawImage($polygon); - - unset($pixel, $polygon); - } catch (\GmagickException $e) { - throw new RuntimeException('Draw polygon operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null) - { - try { - $pixel = $this->getColor($font->getColor()); - $text = new \GmagickDraw(); - - $text->setfont($font->getFile()); - /** - * @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027 - * - * ensure font resolution is the same as GD's hard-coded 96 - */ - $text->setfontsize((int) ($font->getSize() * (96 / 72))); - $text->setfillcolor($pixel); - - $info = $this->gmagick->queryfontmetrics($text, $string); - $rad = deg2rad($angle); - $cos = cos($rad); - $sin = sin($rad); - - $x1 = round(0 * $cos - 0 * $sin); - $x2 = round($info['textWidth'] * $cos - $info['textHeight'] * $sin); - $y1 = round(0 * $sin + 0 * $cos); - $y2 = round($info['textWidth'] * $sin + $info['textHeight'] * $cos); - - $xdiff = 0 - min($x1, $x2); - $ydiff = 0 - min($y1, $y2); - - if ($width !== null) { - throw new NotSupportedException('Gmagick doesn\'t support queryfontmetrics function for multiline text', 1); - } - - $this->gmagick->annotateimage($text, $position->getX() + $x1 + $xdiff, $position->getY() + $y2 + $ydiff, $angle, $string); - - unset($pixel, $text); - } catch (\GmagickException $e) { - throw new RuntimeException('Draw text operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param ColorInterface $color - * - * @return \GmagickPixel - * - * @throws InvalidArgumentException In case a non-opaque color is passed - */ - private function getColor(ColorInterface $color) - { - if (!$color->isOpaque()) { - throw new InvalidArgumentException('Gmagick doesn\'t support transparency'); - } - - return new \GmagickPixel((string) $color); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Effects.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Effects.php deleted file mode 100644 index 3f9c5cd08..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Effects.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Effects\EffectsInterface; -use Imagine\Exception\RuntimeException; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Exception\NotSupportedException; - -/** - * Effects implementation using the Gmagick PHP extension - */ -class Effects implements EffectsInterface -{ - private $gmagick; - - public function __construct(\Gmagick $gmagick) - { - $this->gmagick = $gmagick; - } - - /** - * {@inheritdoc} - */ - public function gamma($correction) - { - try { - $this->gmagick->gammaimage($correction); - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to apply gamma correction to the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function negative() - { - if (!method_exists($this->gmagick, 'negateimage')) { - throw new NotSupportedException('Gmagick version 1.1.0 RC3 is required for negative effect'); - } - - try { - $this->gmagick->negateimage(false, \Gmagick::CHANNEL_ALL); - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to negate the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function grayscale() - { - try { - $this->gmagick->setImageType(2); - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to grayscale the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function colorize(ColorInterface $color) - { - throw new NotSupportedException('Gmagick does not support colorize'); - } - - /** - * {@inheritdoc} - */ - public function sharpen() - { - throw new NotSupportedException('Gmagick does not support sharpen yet'); - } - - /** - * {@inheritdoc} - */ - public function blur($sigma = 1) - { - try { - $this->gmagick->blurImage(0, $sigma); - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to blur the image', $e->getCode(), $e); - } - - return $this; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Font.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Font.php deleted file mode 100644 index ad67c5647..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Font.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Image\AbstractFont; -use Imagine\Image\Box; -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * Font implementation using the Gmagick PHP extension - */ -final class Font extends AbstractFont -{ - /** - * @var \Gmagick - */ - private $gmagick; - - /** - * @param \Gmagick $gmagick - * @param string $file - * @param integer $size - * @param ColorInterface $color - */ - public function __construct(\Gmagick $gmagick, $file, $size, ColorInterface $color) - { - $this->gmagick = $gmagick; - - parent::__construct($file, $size, $color); - } - - /** - * {@inheritdoc} - */ - public function box($string, $angle = 0) - { - $text = new \GmagickDraw(); - - $text->setfont($this->file); - /** - * @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027 - * - * ensure font resolution is the same as GD's hard-coded 96 - */ - $text->setfontsize((int) ($this->size * (96 / 72))); - $text->setfontstyle(\Gmagick::STYLE_OBLIQUE); - - $info = $this->gmagick->queryfontmetrics($text, $string); - - $box = new Box($info['textWidth'], $info['textHeight']); - - return $box; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Image.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Image.php deleted file mode 100644 index 61ef858c1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Image.php +++ /dev/null @@ -1,786 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractImage; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\PaletteInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\Box; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Fill\FillInterface; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; -use Imagine\Image\ProfileInterface; - -/** - * Image implementation using the Gmagick PHP extension - */ -final class Image extends AbstractImage -{ - /** - * @var \Gmagick - */ - private $gmagick; - /** - * @var Layers - */ - private $layers; - - /** - * @var PaletteInterface - */ - private $palette; - - private static $colorspaceMapping = array( - PaletteInterface::PALETTE_CMYK => \Gmagick::COLORSPACE_CMYK, - PaletteInterface::PALETTE_RGB => \Gmagick::COLORSPACE_RGB, - ); - - /** - * Constructs a new Image instance - * - * @param \Gmagick $gmagick - * @param PaletteInterface $palette - * @param MetadataBag $metadata - */ - public function __construct(\Gmagick $gmagick, PaletteInterface $palette, MetadataBag $metadata) - { - $this->metadata = $metadata; - $this->gmagick = $gmagick; - $this->setColorspace($palette); - $this->layers = new Layers($this, $this->palette, $this->gmagick); - } - - /** - * Destroys allocated gmagick resources - */ - public function __destruct() - { - if ($this->gmagick instanceof \Gmagick) { - $this->gmagick->clear(); - $this->gmagick->destroy(); - } - } - - /** - * Returns gmagick instance - * - * @return Gmagick - */ - public function getGmagick() - { - return $this->gmagick; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function copy() - { - return new self(clone $this->gmagick, $this->palette, clone $this->metadata); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function crop(PointInterface $start, BoxInterface $size) - { - if (!$start->in($this->getSize())) { - throw new OutOfBoundsException('Crop coordinates must start at minimum 0, 0 position from top left corner, crop height and width must be positive integers and must not exceed the current image borders'); - } - - try { - $this->gmagick->cropimage($size->getWidth(), $size->getHeight(), $start->getX(), $start->getY()); - } catch (\GmagickException $e) { - throw new RuntimeException('Crop operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function flipHorizontally() - { - try { - $this->gmagick->flopimage(); - } catch (\GmagickException $e) { - throw new RuntimeException('Horizontal flip operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function flipVertically() - { - try { - $this->gmagick->flipimage(); - } catch (\GmagickException $e) { - throw new RuntimeException('Vertical flip operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function strip() - { - try { - try { - $this->profile($this->palette->profile()); - } catch (\Exception $e) { - // here we discard setting the profile as the previous incorporated profile - // is corrupted, let's now strip the image - } - $this->gmagick->stripimage(); - } catch (\GmagickException $e) { - throw new RuntimeException('Strip operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function paste(ImageInterface $image, PointInterface $start) - { - if (!$image instanceof self) { - throw new InvalidArgumentException(sprintf('Gmagick\Image can only paste() Gmagick\Image instances, %s given', get_class($image))); - } - - if (!$this->getSize()->contains($image->getSize(), $start)) { - throw new OutOfBoundsException('Cannot paste image of the given size at the specified position, as it moves outside of the current image\'s box'); - } - - try { - $this->gmagick->compositeimage($image->gmagick, \Gmagick::COMPOSITE_DEFAULT, $start->getX(), $start->getY()); - } catch (\GmagickException $e) { - throw new RuntimeException('Paste operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED) - { - static $supportedFilters = array( - ImageInterface::FILTER_UNDEFINED => \Gmagick::FILTER_UNDEFINED, - ImageInterface::FILTER_BESSEL => \Gmagick::FILTER_BESSEL, - ImageInterface::FILTER_BLACKMAN => \Gmagick::FILTER_BLACKMAN, - ImageInterface::FILTER_BOX => \Gmagick::FILTER_BOX, - ImageInterface::FILTER_CATROM => \Gmagick::FILTER_CATROM, - ImageInterface::FILTER_CUBIC => \Gmagick::FILTER_CUBIC, - ImageInterface::FILTER_GAUSSIAN => \Gmagick::FILTER_GAUSSIAN, - ImageInterface::FILTER_HANNING => \Gmagick::FILTER_HANNING, - ImageInterface::FILTER_HAMMING => \Gmagick::FILTER_HAMMING, - ImageInterface::FILTER_HERMITE => \Gmagick::FILTER_HERMITE, - ImageInterface::FILTER_LANCZOS => \Gmagick::FILTER_LANCZOS, - ImageInterface::FILTER_MITCHELL => \Gmagick::FILTER_MITCHELL, - ImageInterface::FILTER_POINT => \Gmagick::FILTER_POINT, - ImageInterface::FILTER_QUADRATIC => \Gmagick::FILTER_QUADRATIC, - ImageInterface::FILTER_SINC => \Gmagick::FILTER_SINC, - ImageInterface::FILTER_TRIANGLE => \Gmagick::FILTER_TRIANGLE - ); - - if (!array_key_exists($filter, $supportedFilters)) { - throw new InvalidArgumentException('Unsupported filter type'); - } - - try { - $this->gmagick->resizeimage($size->getWidth(), $size->getHeight(), $supportedFilters[$filter], 1); - } catch (\GmagickException $e) { - throw new RuntimeException('Resize operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function rotate($angle, ColorInterface $background = null) - { - try { - $background = $background ?: $this->palette->color('fff'); - $pixel = $this->getColor($background); - - $this->gmagick->rotateimage($pixel, $angle); - - unset($pixel); - } catch (\GmagickException $e) { - throw new RuntimeException('Rotate operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * Internal - * - * Applies options before save or output - * - * @param \Gmagick $image - * @param array $options - * @param string $path - * - * @throws InvalidArgumentException - */ - private function applyImageOptions(\Gmagick $image, array $options, $path) - { - if (isset($options['format'])) { - $format = $options['format']; - } elseif ('' !== $extension = pathinfo($path, \PATHINFO_EXTENSION)) { - $format = $extension; - } else { - $format = pathinfo($image->getImageFilename(), \PATHINFO_EXTENSION); - } - - $format = strtolower($format); - - $options = $this->updateSaveOptions($options); - - if (isset($options['jpeg_quality']) && in_array($format, array('jpeg', 'jpg', 'pjpeg'))) { - $image->setCompressionQuality($options['jpeg_quality']); - } - - if ((isset($options['png_compression_level']) || isset($options['png_compression_filter'])) && $format === 'png') { - // first digit: compression level (default: 7) - if (isset($options['png_compression_level'])) { - if ($options['png_compression_level'] < 0 || $options['png_compression_level'] > 9) { - throw new InvalidArgumentException('png_compression_level option should be an integer from 0 to 9'); - } - $compression = $options['png_compression_level'] * 10; - } else { - $compression = 70; - } - - // second digit: compression filter (default: 5) - if (isset($options['png_compression_filter'])) { - if ($options['png_compression_filter'] < 0 || $options['png_compression_filter'] > 9) { - throw new InvalidArgumentException('png_compression_filter option should be an integer from 0 to 9'); - } - $compression += $options['png_compression_filter']; - } else { - $compression += 5; - } - - $image->setCompressionQuality($compression); - } - - if (isset($options['resolution-units']) && isset($options['resolution-x']) && isset($options['resolution-y'])) { - if ($options['resolution-units'] == ImageInterface::RESOLUTION_PIXELSPERCENTIMETER) { - $image->setimageunits(\Gmagick::RESOLUTION_PIXELSPERCENTIMETER); - } elseif ($options['resolution-units'] == ImageInterface::RESOLUTION_PIXELSPERINCH) { - $image->setimageunits(\Gmagick::RESOLUTION_PIXELSPERINCH); - } else { - throw new InvalidArgumentException('Unsupported image unit format'); - } - - $image->setimageresolution($options['resolution-x'], $options['resolution-y']); - } - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function save($path = null, array $options = array()) - { - $path = null === $path ? $this->gmagick->getImageFilename() : $path; - - if ('' === trim($path)) { - throw new RuntimeException('You can omit save path only if image has been open from a file'); - } - - try { - $this->prepareOutput($options, $path); - $allFrames = !isset($options['animated']) || false === $options['animated']; - $this->gmagick->writeimage($path, $allFrames); - } catch (\GmagickException $e) { - throw new RuntimeException('Save operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function show($format, array $options = array()) - { - header('Content-type: '.$this->getMimeType($format)); - echo $this->get($format, $options); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function get($format, array $options = array()) - { - try { - $options['format'] = $format; - $this->prepareOutput($options); - } catch (\GmagickException $e) { - throw new RuntimeException('Get operation failed', $e->getCode(), $e); - } - - return $this->gmagick->getimagesblob(); - } - - /** - * @param array $options - * @param string $path - */ - private function prepareOutput(array $options, $path = null) - { - if (isset($options['format'])) { - $this->gmagick->setimageformat($options['format']); - } - - if (isset($options['animated']) && true === $options['animated']) { - $format = isset($options['format']) ? $options['format'] : 'gif'; - $delay = isset($options['animated.delay']) ? $options['animated.delay'] : null; - $loops = isset($options['animated.loops']) ? $options['animated.loops'] : 0; - - $options['flatten'] = false; - - $this->layers->animate($format, $delay, $loops); - } else { - $this->layers->merge(); - } - $this->applyImageOptions($this->gmagick, $options, $path); - - // flatten only if image has multiple layers - if ((!isset($options['flatten']) || $options['flatten'] === true) && count($this->layers) > 1) { - $this->flatten(); - } - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return $this->get('png'); - } - - /** - * {@inheritdoc} - */ - public function draw() - { - return new Drawer($this->gmagick); - } - - /** - * {@inheritdoc} - */ - public function effects() - { - return new Effects($this->gmagick); - } - - /** - * {@inheritdoc} - */ - public function getSize() - { - try { - $i = $this->gmagick->getimageindex(); - $this->gmagick->setimageindex(0); //rewind - $width = $this->gmagick->getimagewidth(); - $height = $this->gmagick->getimageheight(); - $this->gmagick->setimageindex($i); - } catch (\GmagickException $e) { - throw new RuntimeException('Get size operation failed', $e->getCode(), $e); - } - - return new Box($width, $height); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function applyMask(ImageInterface $mask) - { - if (!$mask instanceof self) { - throw new InvalidArgumentException('Can only apply instances of Imagine\Gmagick\Image as masks'); - } - - $size = $this->getSize(); - $maskSize = $mask->getSize(); - - if ($size != $maskSize) { - throw new InvalidArgumentException(sprintf('The given mask doesn\'t match current image\'s size, current mask\'s dimensions are %s, while image\'s dimensions are %s', $maskSize, $size)); - } - - try { - $mask = $mask->copy(); - $this->gmagick->compositeimage($mask->gmagick, \Gmagick::COMPOSITE_DEFAULT, 0, 0); - } catch (\GmagickException $e) { - throw new RuntimeException('Apply mask operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function mask() - { - $mask = $this->copy(); - - try { - $mask->gmagick->modulateimage(100, 0, 100); - } catch (\GmagickException $e) { - throw new RuntimeException('Mask operation failed', $e->getCode(), $e); - } - - return $mask; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function fill(FillInterface $fill) - { - try { - $draw = new \GmagickDraw(); - $size = $this->getSize(); - - $w = $size->getWidth(); - $h = $size->getHeight(); - - for ($x = 0; $x <= $w; $x++) { - for ($y = 0; $y <= $h; $y++) { - $pixel = $this->getColor($fill->getColor(new Point($x, $y))); - - $draw->setfillcolor($pixel); - $draw->point($x, $y); - - $pixel = null; - } - } - - $this->gmagick->drawimage($draw); - - $draw = null; - } catch (\GmagickException $e) { - throw new RuntimeException('Fill operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function histogram() - { - try { - $pixels = $this->gmagick->getimagehistogram(); - } catch (\GmagickException $e) { - throw new RuntimeException('Error while fetching histogram', $e->getCode(), $e); - } - - $image = $this; - - return array_map(function (\GmagickPixel $pixel) use ($image) { - return $image->pixelToColor($pixel); - }, $pixels); - } - - /** - * {@inheritdoc} - */ - public function getColorAt(PointInterface $point) - { - if (!$point->in($this->getSize())) { - throw new InvalidArgumentException(sprintf('Error getting color at point [%s,%s]. The point must be inside the image of size [%s,%s]', $point->getX(), $point->getY(), $this->getSize()->getWidth(), $this->getSize()->getHeight())); - } - - try { - $cropped = clone $this->gmagick; - $histogram = $cropped - ->cropImage(1, 1, $point->getX(), $point->getY()) - ->getImageHistogram(); - } catch (\GmagickException $e) { - throw new RuntimeException('Unable to get the pixel'); - } - - $pixel = array_shift($histogram); - - unset($histogram, $cropped); - - return $this->pixelToColor($pixel); - } - - /** - * Returns a color given a pixel, depending the Palette context - * - * Note : this method is public for PHP 5.3 compatibility - * - * @param \GmagickPixel $pixel - * - * @return ColorInterface - * - * @throws InvalidArgumentException In case a unknown color is requested - */ - public function pixelToColor(\GmagickPixel $pixel) - { - static $colorMapping = array( - ColorInterface::COLOR_RED => \Gmagick::COLOR_RED, - ColorInterface::COLOR_GREEN => \Gmagick::COLOR_GREEN, - ColorInterface::COLOR_BLUE => \Gmagick::COLOR_BLUE, - ColorInterface::COLOR_CYAN => \Gmagick::COLOR_CYAN, - ColorInterface::COLOR_MAGENTA => \Gmagick::COLOR_MAGENTA, - ColorInterface::COLOR_YELLOW => \Gmagick::COLOR_YELLOW, - ColorInterface::COLOR_KEYLINE => \Gmagick::COLOR_BLACK, - // There is no gray component in \Gmagick, let's use one of the RGB comp - ColorInterface::COLOR_GRAY => \Gmagick::COLOR_RED, - ); - - if ($this->palette->supportsAlpha()) { - try { - $alpha = (int) round($pixel->getcolorvalue(\Gmagick::COLOR_ALPHA) * 100); - } catch (\GmagickPixelException $e) { - $alpha = null; - } - } else { - $alpha = null; - } - - $palette = $this->palette(); - - return $this->palette->color(array_map(function ($color) use ($palette, $pixel, $colorMapping) { - if (!isset($colorMapping[$color])) { - throw new InvalidArgumentException(sprintf('Color %s is not mapped in Gmagick', $color)); - } - $multiplier = 255; - if ($palette->name() === PaletteInterface::PALETTE_CMYK) { - $multiplier = 100; - } - - return $pixel->getcolorvalue($colorMapping[$color]) * $multiplier; - }, $this->palette->pixelDefinition()), $alpha); - } - - /** - * {@inheritdoc} - */ - public function layers() - { - return $this->layers; - } - - /** - * {@inheritdoc} - */ - public function interlace($scheme) - { - static $supportedInterlaceSchemes = array( - ImageInterface::INTERLACE_NONE => \Gmagick::INTERLACE_NO, - ImageInterface::INTERLACE_LINE => \Gmagick::INTERLACE_LINE, - ImageInterface::INTERLACE_PLANE => \Gmagick::INTERLACE_PLANE, - ImageInterface::INTERLACE_PARTITION => \Gmagick::INTERLACE_PARTITION, - ); - - if (!array_key_exists($scheme, $supportedInterlaceSchemes)) { - throw new InvalidArgumentException('Unsupported interlace type'); - } - - $this->gmagick->setInterlaceScheme($supportedInterlaceSchemes[$scheme]); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function usePalette(PaletteInterface $palette) - { - if (!isset(static::$colorspaceMapping[$palette->name()])) { - throw new InvalidArgumentException(sprintf('The palette %s is not supported by Gmagick driver',$palette->name())); - } - - if ($this->palette->name() === $palette->name()) { - return $this; - } - - try { - try { - $hasICCProfile = (Boolean) $this->gmagick->getimageprofile('ICM'); - } catch (\GmagickException $e) { - $hasICCProfile = false; - } - - if (!$hasICCProfile) { - $this->profile($this->palette->profile()); - } - - $this->profile($palette->profile()); - - $this->setColorspace($palette); - $this->palette = $palette; - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to set colorspace', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function palette() - { - return $this->palette; - } - - /** - * {@inheritdoc} - */ - public function profile(ProfileInterface $profile) - { - try { - $this->gmagick->profileimage('ICM', $profile->data()); - } catch (\GmagickException $e) { - throw new RuntimeException(sprintf('Unable to add profile %s to image', $profile->name()), $e->getCode(), $e); - } - - return $this; - } - - /** - * Internal - * - * Flatten the image. - */ - private function flatten() - { - /** - * @see http://pecl.php.net/bugs/bug.php?id=22435 - */ - if (method_exists($this->gmagick, 'flattenImages')) { - try { - $this->gmagick = $this->gmagick->flattenImages(); - } catch (\GmagickException $e) { - throw new RuntimeException('Flatten operation failed', $e->getCode(), $e); - } - } - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param ColorInterface $color - * - * @return \GmagickPixel - * - * @throws InvalidArgumentException - */ - private function getColor(ColorInterface $color) - { - if (!$color->isOpaque()) { - throw new InvalidArgumentException('Gmagick doesn\'t support transparency'); - } - - return new \GmagickPixel((string) $color); - } - - /** - * Internal - * - * Get the mime type based on format. - * - * @param string $format - * - * @return string mime-type - * - * @throws InvalidArgumentException - */ - private function getMimeType($format) - { - static $mimeTypes = array( - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'png' => 'image/png', - 'wbmp' => 'image/vnd.wap.wbmp', - 'xbm' => 'image/xbm', - ); - - if (!isset($mimeTypes[$format])) { - throw new InvalidArgumentException(sprintf('Unsupported format given. Only %s are supported, %s given', implode(", ", array_keys($mimeTypes)), $format)); - } - - return $mimeTypes[$format]; - } - - /** - * Sets colorspace and image type, assigns the palette. - * - * @param PaletteInterface $palette - * - * @throws InvalidArgumentException - */ - private function setColorspace(PaletteInterface $palette) - { - if (!isset(static::$colorspaceMapping[$palette->name()])) { - throw new InvalidArgumentException(sprintf('The palette %s is not supported by Gmagick driver', $palette->name())); - } - - $this->gmagick->setimagecolorspace(static::$colorspaceMapping[$palette->name()]); - $this->palette = $palette; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Imagine.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Imagine.php deleted file mode 100644 index ce4d4ed6c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Imagine.php +++ /dev/null @@ -1,167 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Image\AbstractImagine; -use Imagine\Exception\NotSupportedException; -use Imagine\Image\BoxInterface; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Palette\Grayscale; -use Imagine\Image\Palette\CMYK; -use Imagine\Image\Palette\RGB; -use Imagine\Image\Palette\Color\CMYK as CMYKColor; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; - -/** - * Imagine implementation using the Gmagick PHP extension - */ -class Imagine extends AbstractImagine -{ - /** - * @throws RuntimeException - */ - public function __construct() - { - if (!class_exists('Gmagick')) { - throw new RuntimeException('Gmagick not installed'); - } - } - - /** - * {@inheritdoc} - */ - public function open($path) - { - $path = $this->checkPath($path); - - try { - $gmagick = new \Gmagick($path); - $image = new Image($gmagick, $this->createPalette($gmagick), $this->getMetadataReader()->readFile($path)); - } catch (\GmagickException $e) { - throw new RuntimeException(sprintf('Unable to open image %s', $path), $e->getCode(), $e); - } - - return $image; - } - - /** - * {@inheritdoc} - */ - public function create(BoxInterface $size, ColorInterface $color = null) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $palette = null !== $color ? $color->getPalette() : new RGB(); - $color = null !== $color ? $color : $palette->color('fff'); - - try { - $gmagick = new \Gmagick(); - // Gmagick does not support creation of CMYK GmagickPixel - // see https://bugs.php.net/bug.php?id=64466 - if ($color instanceof CMYKColor) { - $switchPalette = $palette; - $palette = new RGB(); - $pixel = new \GmagickPixel($palette->color((string) $color)); - } else { - $switchPalette = null; - $pixel = new \GmagickPixel((string) $color); - } - - if ($color->getPalette()->supportsAlpha() && $color->getAlpha() < 100) { - throw new NotSupportedException('alpha transparency is not supported'); - } - - $gmagick->newimage($width, $height, $pixel->getcolor(false)); - $gmagick->setimagecolorspace(\Gmagick::COLORSPACE_TRANSPARENT); - $gmagick->setimagebackgroundcolor($pixel); - - $image = new Image($gmagick, $palette, new MetadataBag()); - - if ($switchPalette) { - $image->usePalette($switchPalette); - } - - return $image; - } catch (\GmagickException $e) { - throw new RuntimeException('Could not create empty image', $e->getCode(), $e); - } - } - - /** - * {@inheritdoc} - */ - public function load($string) - { - return $this->doLoad($string, $this->getMetadataReader()->readData($string)); - } - - /** - * {@inheritdoc} - */ - public function read($resource) - { - if (!is_resource($resource)) { - throw new InvalidArgumentException('Variable does not contain a stream resource'); - } - - $content = stream_get_contents($resource); - - if (false === $content) { - throw new InvalidArgumentException('Couldn\'t read given resource'); - } - - return $this->doLoad($content, $this->getMetadataReader()->readStream($resource)); - } - - /** - * {@inheritdoc} - */ - public function font($file, $size, ColorInterface $color) - { - $gmagick = new \Gmagick(); - $gmagick->newimage(1, 1, 'transparent'); - - return new Font($gmagick, $file, $size, $color); - } - - private function createPalette(\Gmagick $gmagick) - { - switch ($gmagick->getimagecolorspace()) { - case \Gmagick::COLORSPACE_SRGB: - case \Gmagick::COLORSPACE_RGB: - return new RGB(); - case \Gmagick::COLORSPACE_CMYK: - return new CMYK(); - case \Gmagick::COLORSPACE_GRAY: - return new Grayscale(); - default: - throw new NotSupportedException('Only RGB and CMYK colorspace are currently supported'); - } - } - - private function doLoad($content, MetadataBag $metadata) - { - try { - $gmagick = new \Gmagick(); - $gmagick->readimageblob($content); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Could not load image from string', $e->getCode(), $e - ); - } - - return new Image($gmagick, $this->createPalette($gmagick), $metadata); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Layers.php b/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Layers.php deleted file mode 100644 index d708812a1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Gmagick/Layers.php +++ /dev/null @@ -1,272 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Image\AbstractLayers; -use Imagine\Exception\RuntimeException; -use Imagine\Exception\NotSupportedException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\PaletteInterface; - -class Layers extends AbstractLayers -{ - /** - * @var Image - */ - private $image; - - /** - * @var \Gmagick - */ - private $resource; - - /** - * @var integer - */ - private $offset = 0; - - /** - * @var array - */ - private $layers = array(); - - /** - * @var PaletteInterface - */ - private $palette; - - public function __construct(Image $image, PaletteInterface $palette, \Gmagick $resource) - { - $this->image = $image; - $this->resource = $resource; - $this->palette = $palette; - } - - /** - * {@inheritdoc} - */ - public function merge() - { - foreach ($this->layers as $offset => $image) { - try { - $this->resource->setimageindex($offset); - $this->resource->setimage($image->getGmagick()); - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to substitute layer', $e->getCode(), $e); - } - } - } - - /** - * {@inheritdoc} - */ - public function coalesce() - { - throw new NotSupportedException('Gmagick does not support coalescing'); - } - - /** - * {@inheritdoc} - */ - public function animate($format, $delay, $loops) - { - if ('gif' !== strtolower($format)) { - throw new NotSupportedException('Animated picture is currently only supported on gif'); - } - - if (!is_int($loops) || $loops < 0) { - throw new InvalidArgumentException('Loops must be a positive integer.'); - } - - if (null !== $delay && (!is_int($delay) || $delay < 0)) { - throw new InvalidArgumentException('Delay must be either null or a positive integer.'); - } - - try { - foreach ($this as $offset => $layer) { - $this->resource->setimageindex($offset); - $this->resource->setimageformat($format); - - if (null !== $delay) { - $this->resource->setimagedelay($delay / 10); - } - - $this->resource->setimageiterations($loops); - } - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to animate layers', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function current() - { - return $this->extractAt($this->offset); - } - - /** - * Tries to extract layer at given offset - * - * @param integer $offset - * @return Image - * @throws RuntimeException - */ - private function extractAt($offset) - { - if (!isset($this->layers[$offset])) { - try { - $this->resource->setimageindex($offset); - $this->layers[$offset] = new Image($this->resource->getimage(), $this->palette, new MetadataBag()); - } catch (\GmagickException $e) { - throw new RuntimeException(sprintf('Failed to extract layer %d', $offset), $e->getCode(), $e); - } - } - - return $this->layers[$offset]; - } - - /** - * {@inheritdoc} - */ - public function key() - { - return $this->offset; - } - - /** - * {@inheritdoc} - */ - public function next() - { - ++$this->offset; - } - - /** - * {@inheritdoc} - */ - public function rewind() - { - $this->offset = 0; - } - - /** - * {@inheritdoc} - */ - public function valid() - { - return $this->offset < count($this); - } - - /** - * {@inheritdoc} - */ - public function count() - { - try { - return $this->resource->getnumberimages(); - } catch (\GmagickException $e) { - throw new RuntimeException('Failed to count the number of layers', $e->getCode(), $e); - } - } - - /** - * {@inheritdoc} - */ - public function offsetExists($offset) - { - return is_int($offset) && $offset >= 0 && $offset < count($this); - } - - /** - * {@inheritdoc} - */ - public function offsetGet($offset) - { - return $this->extractAt($offset); - } - - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $image) - { - if (!$image instanceof Image) { - throw new InvalidArgumentException('Only a Gmagick Image can be used as layer'); - } - - if (null === $offset) { - $offset = count($this) - 1; - } else { - if (!is_int($offset)) { - throw new InvalidArgumentException('Invalid offset for layer, it must be an integer'); - } - - if (count($this) < $offset || 0 > $offset) { - throw new OutOfBoundsException(sprintf('Invalid offset for layer, it must be a value between 0 and %d, %d given', count($this), $offset)); - } - - if (isset($this[$offset])) { - unset($this[$offset]); - $offset = $offset - 1; - } - } - - $frame = $image->getGmagick(); - - try { - if (count($this) > 0) { - $this->resource->setimageindex($offset); - $this->resource->nextimage(); - } - $this->resource->addimage($frame); - - /** - * ugly hack to bypass issue https://bugs.php.net/bug.php?id=64623 - */ - if (count($this) == 2) { - $this->resource->setimageindex($offset+1); - $this->resource->nextimage(); - $this->resource->addimage($frame); - unset($this[0]); - } - } catch (\GmagickException $e) { - throw new RuntimeException('Unable to set the layer', $e->getCode(), $e); - } - - $this->layers = array(); - } - - /** - * {@inheritdoc} - */ - public function offsetUnset($offset) - { - try { - $this->extractAt($offset); - } catch (RuntimeException $e) { - return; - } - - try { - $this->resource->setimageindex($offset); - $this->resource->removeimage(); - } catch (\GmagickException $e) { - throw new RuntimeException('Unable to remove layer', $e->getCode(), $e); - } - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractFont.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractFont.php deleted file mode 100644 index 59e9a45ba..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractFont.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * Abstract font base class - */ -abstract class AbstractFont implements FontInterface -{ - /** - * @var string - */ - protected $file; - - /** - * @var integer - */ - protected $size; - - /** - * @var ColorInterface - */ - protected $color; - - /** - * Constructs a font with specified $file, $size and $color - * - * The font size is to be specified in points (e.g. 10pt means 10) - * - * @param string $file - * @param integer $size - * @param ColorInterface $color - */ - public function __construct($file, $size, ColorInterface $color) - { - $this->file = $file; - $this->size = $size; - $this->color = $color; - } - - /** - * {@inheritdoc} - */ - final public function getFile() - { - return $this->file; - } - - /** - * {@inheritdoc} - */ - final public function getSize() - { - return $this->size; - } - - /** - * {@inheritdoc} - */ - final public function getColor() - { - return $this->color; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImage.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImage.php deleted file mode 100644 index 5c85769fd..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImage.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Image\Metadata\MetadataBag; - -abstract class AbstractImage implements ImageInterface -{ - /** - * @var MetadataBag - */ - protected $metadata; - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED) - { - if ($mode !== ImageInterface::THUMBNAIL_INSET && - $mode !== ImageInterface::THUMBNAIL_OUTBOUND) { - throw new InvalidArgumentException('Invalid mode specified'); - } - - $imageSize = $this->getSize(); - $ratios = array( - $size->getWidth() / $imageSize->getWidth(), - $size->getHeight() / $imageSize->getHeight() - ); - - $thumbnail = $this->copy(); - - $thumbnail->usePalette($this->palette()); - $thumbnail->strip(); - // if target width is larger than image width - // AND target height is longer than image height - if ($size->contains($imageSize)) { - return $thumbnail; - } - - if ($mode === ImageInterface::THUMBNAIL_INSET) { - $ratio = min($ratios); - } else { - $ratio = max($ratios); - } - - if ($mode === ImageInterface::THUMBNAIL_OUTBOUND) { - if (!$imageSize->contains($size)) { - $size = new Box( - min($imageSize->getWidth(), $size->getWidth()), - min($imageSize->getHeight(), $size->getHeight()) - ); - } else { - $imageSize = $thumbnail->getSize()->scale($ratio); - $thumbnail->resize($imageSize, $filter); - } - $thumbnail->crop(new Point( - max(0, round(($imageSize->getWidth() - $size->getWidth()) / 2)), - max(0, round(($imageSize->getHeight() - $size->getHeight()) / 2)) - ), $size); - } else { - if (!$imageSize->contains($size)) { - $imageSize = $imageSize->scale($ratio); - $thumbnail->resize($imageSize, $filter); - } else { - $imageSize = $thumbnail->getSize()->scale($ratio); - $thumbnail->resize($imageSize, $filter); - } - } - - return $thumbnail; - } - - /** - * Updates a given array of save options for backward compatibility with legacy names - * - * @param array $options - * - * @return array - */ - protected function updateSaveOptions(array $options) - { - // Preserve BC until version 1.0 - if (isset($options['quality']) && !isset($options['jpeg_quality'])) { - $options['jpeg_quality'] = $options['quality']; - } - - return $options; - } - - /** - * {@inheritdoc} - */ - public function metadata() - { - return $this->metadata; - } - - /** - * Assures the metadata instance will be cloned, too - */ - public function __clone() - { - if ($this->metadata !== null) { - $this->metadata = clone $this->metadata; - } - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImagine.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImagine.php deleted file mode 100644 index fcb1a3d67..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractImagine.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Image\Metadata\DefaultMetadataReader; -use Imagine\Image\Metadata\ExifMetadataReader; -use Imagine\Image\Metadata\MetadataReaderInterface; -use Imagine\Exception\InvalidArgumentException; - -abstract class AbstractImagine implements ImagineInterface -{ - /** @var MetadataReaderInterface */ - private $metadataReader; - - /** - * @param MetadataReaderInterface $metadataReader - * - * @return ImagineInterface - */ - public function setMetadataReader(MetadataReaderInterface $metadataReader) - { - $this->metadataReader = $metadataReader; - - return $this; - } - - /** - * @return MetadataReaderInterface - */ - public function getMetadataReader() - { - if (null === $this->metadataReader) { - if (ExifMetadataReader::isSupported()) { - $this->metadataReader = new ExifMetadataReader(); - } else { - $this->metadataReader = new DefaultMetadataReader(); - } - } - - return $this->metadataReader; - } - - /** - * Checks a path that could be used with ImagineInterface::open and returns - * a proper string. - * - * @param string|object $path - * - * @return string - * - * @throws InvalidArgumentException In case the given path is invalid. - */ - protected function checkPath($path) - { - // provide compatibility with objects such as \SplFileInfo - if (is_object($path) && method_exists($path, '__toString')) { - $path = (string) $path; - } - - $handle = @fopen($path, 'r'); - - if (false === $handle) { - throw new InvalidArgumentException(sprintf('File %s does not exist.', $path)); - } - - fclose($handle); - - return $path; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractLayers.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractLayers.php deleted file mode 100644 index 486a72228..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/AbstractLayers.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -abstract class AbstractLayers implements LayersInterface -{ - /** - * {@inheritdoc} - */ - public function add(ImageInterface $image) - { - $this[] = $image; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function set($offset, ImageInterface $image) - { - $this[$offset] = $image; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function remove($offset) - { - unset($this[$offset]); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function get($offset) - { - return $this[$offset]; - } - - /** - * {@inheritdoc} - */ - public function has($offset) - { - return isset($this[$offset]); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Box.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Box.php deleted file mode 100644 index 37b85135a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Box.php +++ /dev/null @@ -1,122 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; - -/** - * A box implementation - */ -final class Box implements BoxInterface -{ - /** - * @var integer - */ - private $width; - - /** - * @var integer - */ - private $height; - - /** - * Constructs the Size with given width and height - * - * @param integer $width - * @param integer $height - * - * @throws InvalidArgumentException - */ - public function __construct($width, $height) - { - if ($height < 1 || $width < 1) { - throw new InvalidArgumentException(sprintf('Length of either side cannot be 0 or negative, current size is %sx%s', $width, $height)); - } - - $this->width = (int) $width; - $this->height = (int) $height; - } - - /** - * {@inheritdoc} - */ - public function getWidth() - { - return $this->width; - } - - /** - * {@inheritdoc} - */ - public function getHeight() - { - return $this->height; - } - - /** - * {@inheritdoc} - */ - public function scale($ratio) - { - return new Box(round($ratio * $this->width), round($ratio * $this->height)); - } - - /** - * {@inheritdoc} - */ - public function increase($size) - { - return new Box((int) $size + $this->width, (int) $size + $this->height); - } - - /** - * {@inheritdoc} - */ - public function contains(BoxInterface $box, PointInterface $start = null) - { - $start = $start ? $start : new Point(0, 0); - - return $start->in($this) && $this->width >= $box->getWidth() + $start->getX() && $this->height >= $box->getHeight() + $start->getY(); - } - - /** - * {@inheritdoc} - */ - public function square() - { - return $this->width * $this->height; - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return sprintf('%dx%d px', $this->width, $this->height); - } - - /** - * {@inheritdoc} - */ - public function widen($width) - { - return $this->scale($width / $this->width); - } - - /** - * {@inheritdoc} - */ - public function heighten($height) - { - return $this->scale($height / $this->height); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/BoxInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/BoxInterface.php deleted file mode 100644 index 4a086c39f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/BoxInterface.php +++ /dev/null @@ -1,94 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -/** - * Interface for a box - */ -interface BoxInterface -{ - /** - * Gets current image height - * - * @return integer - */ - public function getHeight(); - - /** - * Gets current image width - * - * @return integer - */ - public function getWidth(); - - /** - * Creates new BoxInterface instance with ratios applied to both sides - * - * @param float $ratio - * - * @return BoxInterface - */ - public function scale($ratio); - - /** - * Creates new BoxInterface, adding given size to both sides - * - * @param integer $size - * - * @return BoxInterface - */ - public function increase($size); - - /** - * Checks whether current box can fit given box at a given start position, - * start position defaults to top left corner xy(0,0) - * - * @param BoxInterface $box - * @param PointInterface $start - * - * @return Boolean - */ - public function contains(BoxInterface $box, PointInterface $start = null); - - /** - * Gets current box square, useful for getting total number of pixels in a - * given box - * - * @return integer - */ - public function square(); - - /** - * Returns a string representation of the current box - * - * @return string - */ - public function __toString(); - - /** - * Resizes box to given width, constraining proportions and returns the new box - * - * @param integer $width - * - * @return BoxInterface - */ - public function widen($width); - - /** - * Resizes box to given height, constraining proportions and returns the new box - * - * @param integer $height - * - * @return BoxInterface - */ - public function heighten($height); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/FillInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/FillInterface.php deleted file mode 100644 index c8d69e5d4..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/FillInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Fill; - -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\PointInterface; - -/** - * Interface for the fill - */ -interface FillInterface -{ - /** - * Gets color of the fill for the given position - * - * @param PointInterface $position - * - * @return ColorInterface - */ - public function getColor(PointInterface $position); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Horizontal.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Horizontal.php deleted file mode 100644 index 4a4230671..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Horizontal.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Fill\Gradient; - -use Imagine\Image\PointInterface; - -/** - * Horizontal gradient fill - */ -final class Horizontal extends Linear -{ - /** - * {@inheritdoc} - */ - public function getDistance(PointInterface $position) - { - return $position->getX(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Linear.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Linear.php deleted file mode 100644 index 9bf2cf6cc..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Linear.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Fill\Gradient; - -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Fill\FillInterface; -use Imagine\Image\PointInterface; - -/** - * Linear gradient fill - */ -abstract class Linear implements FillInterface -{ - /** - * @var integer - */ - private $length; - - /** - * @var ColorInterface - */ - private $start; - - /** - * @var ColorInterface - */ - private $end; - - /** - * Constructs a linear gradient with overall gradient length, and start and - * end shades, which default to 0 and 255 accordingly - * - * @param integer $length - * @param ColorInterface $start - * @param ColorInterface $end - */ - final public function __construct($length, ColorInterface $start, ColorInterface $end) - { - $this->length = $length; - $this->start = $start; - $this->end = $end; - } - - /** - * {@inheritdoc} - */ - final public function getColor(PointInterface $position) - { - $l = $this->getDistance($position); - - if ($l >= $this->length) { - return $this->end; - } - - if ($l < 0) { - return $this->start; - } - - return $this->start->getPalette()->blend($this->start, $this->end, $l / $this->length); - } - - /** - * @return ColorInterface - */ - final public function getStart() - { - return $this->start; - } - - /** - * @return ColorInterface - */ - final public function getEnd() - { - return $this->end; - } - - /** - * Get the distance of the position relative to the beginning of the gradient - * - * @param PointInterface $position - * - * @return integer - */ - abstract protected function getDistance(PointInterface $position); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Vertical.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Vertical.php deleted file mode 100644 index 9a26b7789..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Fill/Gradient/Vertical.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Fill\Gradient; - -use Imagine\Image\PointInterface; - -/** - * Vertical gradient fill - */ -final class Vertical extends Linear -{ - /** - * {@inheritdoc} - */ - public function getDistance(PointInterface $position) - { - return $position->getY(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/FontInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/FontInterface.php deleted file mode 100644 index 59d4a265a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/FontInterface.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * The font interface - */ -interface FontInterface -{ - /** - * Gets the fontfile for current font - * - * @return string - */ - public function getFile(); - - /** - * Gets font's integer point size - * - * @return integer - */ - public function getSize(); - - /** - * Gets font's color - * - * @return ColorInterface - */ - public function getColor(); - - /** - * Gets BoxInterface of font size on the image based on string and angle - * - * @param string $string - * @param integer $angle - * - * @return BoxInterface - */ - public function box($string, $angle = 0); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Bucket.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Bucket.php deleted file mode 100644 index 34dad5eed..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Bucket.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Histogram; - -/** - * Bucket histogram - */ -final class Bucket implements \Countable -{ - /** - * @var Range - */ - private $range; - - /** - * @var integer - */ - private $count; - - /** - * @param Range $range - * @param integer $count - */ - public function __construct(Range $range, $count = 0) - { - $this->range = $range; - $this->count = $count; - } - - /** - * @param integer $value - */ - public function add($value) - { - if ($this->range->contains($value)) { - $this->count++; - } - } - - /** - * @return integer The number of elements in the bucket. - */ - public function count() - { - return $this->count; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Range.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Range.php deleted file mode 100644 index 9dc9f9928..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Histogram/Range.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Histogram; - -use Imagine\Exception\OutOfBoundsException; - -/** - * Range histogram - */ -final class Range -{ - /** - * @var integer - */ - private $start; - - /** - * @var integer - */ - private $end; - - /** - * @param integer $start - * @param integer $end - * - * @throws OutOfBoundsException - */ - public function __construct($start, $end) - { - if ($end <= $start) { - throw new OutOfBoundsException(sprintf('Range end cannot be bigger than start, %d %d given accordingly', $this->start, $this->end)); - } - - $this->start = $start; - $this->end = $end; - } - - /** - * @param integer $value - * - * @return Boolean - */ - public function contains($value) - { - return $value >= $this->start && $value < $this->end; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ImageInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/ImageInterface.php deleted file mode 100644 index 6cf1dd87d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ImageInterface.php +++ /dev/null @@ -1,173 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Draw\DrawerInterface; -use Imagine\Effects\EffectsInterface; -use Imagine\Image\Palette\PaletteInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Exception\RuntimeException; -use Imagine\Exception\OutOfBoundsException; - -/** - * The image interface - */ -interface ImageInterface extends ManipulatorInterface -{ - const RESOLUTION_PIXELSPERINCH = 'ppi'; - const RESOLUTION_PIXELSPERCENTIMETER = 'ppc'; - - const INTERLACE_NONE = 'none'; - const INTERLACE_LINE = 'line'; - const INTERLACE_PLANE = 'plane'; - const INTERLACE_PARTITION = 'partition'; - - const FILTER_UNDEFINED = 'undefined'; - const FILTER_POINT = 'point'; - const FILTER_BOX = 'box'; - const FILTER_TRIANGLE = 'triangle'; - const FILTER_HERMITE = 'hermite'; - const FILTER_HANNING = 'hanning'; - const FILTER_HAMMING = 'hamming'; - const FILTER_BLACKMAN = 'blackman'; - const FILTER_GAUSSIAN = 'gaussian'; - const FILTER_QUADRATIC = 'quadratic'; - const FILTER_CUBIC = 'cubic'; - const FILTER_CATROM = 'catrom'; - const FILTER_MITCHELL = 'mitchell'; - const FILTER_LANCZOS = 'lanczos'; - const FILTER_BESSEL = 'bessel'; - const FILTER_SINC = 'sinc'; - - /** - * Returns the image content as a binary string - * - * @param string $format - * @param array $options - * - * @throws RuntimeException - * - * @return string binary - */ - public function get($format, array $options = array()); - - /** - * Returns the image content as a PNG binary string - * - * @throws RuntimeException - * - * @return string binary - */ - public function __toString(); - - /** - * Instantiates and returns a DrawerInterface instance for image drawing - * - * @return DrawerInterface - */ - public function draw(); - - /** - * @return EffectsInterface - */ - public function effects(); - - /** - * Returns current image size - * - * @return BoxInterface - */ - public function getSize(); - - /** - * Transforms creates a grayscale mask from current image, returns a new - * image, while keeping the existing image unmodified - * - * @return ImageInterface - */ - public function mask(); - - /** - * Returns array of image colors as Imagine\Image\Palette\Color\ColorInterface instances - * - * @return array - */ - public function histogram(); - - /** - * Returns color at specified positions of current image - * - * @param PointInterface $point - * - * @throws RuntimeException - * - * @return ColorInterface - */ - public function getColorAt(PointInterface $point); - - /** - * Returns the image layers when applicable. - * - * @throws RuntimeException In case the layer can not be returned - * @throws OutOfBoundsException In case the index is not a valid value - * - * @return LayersInterface - */ - public function layers(); - - /** - * Enables or disables interlacing - * - * @param string $scheme - * - * @throws InvalidArgumentException When an unsupported Interface type is supplied - * - * @return ImageInterface - */ - public function interlace($scheme); - - /** - * Return the current color palette - * - * @return PaletteInterface - */ - public function palette(); - - /** - * Set a palette for the image. Useful to change colorspace. - * - * @param PaletteInterface $palette - * - * @return ImageInterface - * - * @throws RuntimeException - */ - public function usePalette(PaletteInterface $palette); - - /** - * Applies a color profile on the Image - * - * @param ProfileInterface $profile - * - * @return ImageInterface - * - * @throws RuntimeException - */ - public function profile(ProfileInterface $profile); - - /** - * Returns the Image's meta data - * - * @return Metadata\MetadataInterface - */ - public function metadata(); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ImagineInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/ImagineInterface.php deleted file mode 100644 index b5c942c73..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ImagineInterface.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; - -/** - * The imagine interface - */ -interface ImagineInterface -{ - const VERSION = '0.7-dev'; - - /** - * Creates a new empty image with an optional background color - * - * @param BoxInterface $size - * @param ColorInterface $color - * - * @throws InvalidArgumentException - * @throws RuntimeException - * - * @return ImageInterface - */ - public function create(BoxInterface $size, ColorInterface $color = null); - - /** - * Opens an existing image from $path - * - * @param string $path - * - * @throws RuntimeException - * - * @return ImageInterface - */ - public function open($path); - - /** - * Loads an image from a binary $string - * - * @param string $string - * - * @throws RuntimeException - * - * @return ImageInterface - */ - public function load($string); - - /** - * Loads an image from a resource $resource - * - * @param resource $resource - * - * @throws RuntimeException - * - * @return ImageInterface - */ - public function read($resource); - - /** - * Constructs a font with specified $file, $size and $color - * - * The font size is to be specified in points (e.g. 10pt means 10) - * - * @param string $file - * @param integer $size - * @param ColorInterface $color - * - * @return FontInterface - */ - public function font($file, $size, ColorInterface $color); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/LayersInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/LayersInterface.php deleted file mode 100644 index 44df42369..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/LayersInterface.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\RuntimeException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\OutOfBoundsException; - -/** - * The layers interface - */ -interface LayersInterface extends \Iterator, \Countable, \ArrayAccess -{ - /** - * Merge layers into the original objects - * - * @throws RuntimeException - */ - public function merge(); - - /** - * Animates layers - * - * @param string $format The output output format - * @param integer $delay The delay in milliseconds between two frames - * @param integer $loops The number of loops, 0 means infinite - * - * @return LayersInterface - * - * @throws InvalidArgumentException In case an invalid argument is provided - * @throws RuntimeException In case the driver fails to animate - */ - public function animate($format, $delay, $loops); - - /** - * Coalesce layers. Each layer in the sequence is the same size as the first and composited with the next layer in - * the sequence. - */ - public function coalesce(); - - /** - * Adds an image at the end of the layers stack - * - * @param ImageInterface $image - * - * @return LayersInterface - * - * @throws RuntimeException - */ - public function add(ImageInterface $image); - - /** - * Set an image at offset - * - * @param integer $offset - * @param ImageInterface $image - * - * @return LayersInterface - * - * @throws RuntimeException - * @throws InvalidArgumentException - * @throws OutOfBoundsException - */ - public function set($offset, ImageInterface $image); - - /** - * Removes the image at offset - * - * @param integer $offset - * - * @return LayersInterface - * - * @throws RuntimeException - * @throws InvalidArgumentException - */ - public function remove($offset); - - /** - * Returns the image at offset - * - * @param integer $offset - * - * @return ImageInterface - * - * @throws RuntimeException - * @throws InvalidArgumentException - */ - public function get($offset); - - /** - * Returns true if a layer at offset is preset - * - * @param integer $offset - * - * @return Boolean - */ - public function has($offset); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ManipulatorInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/ManipulatorInterface.php deleted file mode 100644 index 392b90828..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ManipulatorInterface.php +++ /dev/null @@ -1,181 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Fill\FillInterface; - -/** - * The manipulator interface - */ -interface ManipulatorInterface -{ - const THUMBNAIL_INSET = 'inset'; - const THUMBNAIL_OUTBOUND = 'outbound'; - - /** - * Copies current source image into a new ImageInterface instance - * - * @throws RuntimeException - * - * @return static - */ - public function copy(); - - /** - * Crops a specified box out of the source image (modifies the source image) - * Returns cropped self - * - * @param PointInterface $start - * @param BoxInterface $size - * - * @throws OutOfBoundsException - * @throws RuntimeException - * - * @return static - */ - public function crop(PointInterface $start, BoxInterface $size); - - /** - * Resizes current image and returns self - * - * @param BoxInterface $size - * @param string $filter - * - * @throws RuntimeException - * - * @return static - */ - public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED); - - /** - * Rotates an image at the given angle. - * Optional $background can be used to specify the fill color of the empty - * area of rotated image. - * - * @param integer $angle - * @param ColorInterface $background - * - * @throws RuntimeException - * - * @return static - */ - public function rotate($angle, ColorInterface $background = null); - - /** - * Pastes an image into a parent image - * Throws exceptions if image exceeds parent image borders or if paste - * operation fails - * - * Returns source image - * - * @param ImageInterface $image - * @param PointInterface $start - * - * @throws InvalidArgumentException - * @throws OutOfBoundsException - * @throws RuntimeException - * - * @return static - */ - public function paste(ImageInterface $image, PointInterface $start); - - /** - * Saves the image at a specified path, the target file extension is used - * to determine file format, only jpg, jpeg, gif, png, wbmp and xbm are - * supported - * - * @param string $path - * @param array $options - * - * @throws RuntimeException - * - * @return static - */ - public function save($path = null, array $options = array()); - - /** - * Outputs the image content - * - * @param string $format - * @param array $options - * - * @throws RuntimeException - * - * @return static - */ - public function show($format, array $options = array()); - - /** - * Flips current image using horizontal axis - * - * @throws RuntimeException - * - * @return static - */ - public function flipHorizontally(); - - /** - * Flips current image using vertical axis - * - * @throws RuntimeException - * - * @return static - */ - public function flipVertically(); - - /** - * Remove all profiles and comments - * - * @throws RuntimeException - * - * @return static - */ - public function strip(); - - /** - * Generates a thumbnail from a current image - * Returns it as a new image, doesn't modify the current image - * - * @param BoxInterface $size - * @param string $mode - * @param string $filter The filter to use for resizing, one of ImageInterface::FILTER_* - * - * @throws RuntimeException - * - * @return static - */ - public function thumbnail(BoxInterface $size, $mode = self::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED); - - /** - * Applies a given mask to current image's alpha channel - * - * @param ImageInterface $mask - * - * @return static - */ - public function applyMask(ImageInterface $mask); - - /** - * Fills image with provided filling, by replacing each pixel's color in - * the current image with corresponding color from FillInterface, and - * returns modified image - * - * @param FillInterface $fill - * - * @return static - */ - public function fill(FillInterface $fill); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/AbstractMetadataReader.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/AbstractMetadataReader.php deleted file mode 100644 index a709655be..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/AbstractMetadataReader.php +++ /dev/null @@ -1,101 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Metadata; - -use Imagine\Exception\InvalidArgumentException; - -abstract class AbstractMetadataReader implements MetadataReaderInterface -{ - /** - * {@inheritdoc} - */ - public function readFile($file) - { - if (stream_is_local($file)) { - if (!is_file($file)) { - throw new InvalidArgumentException(sprintf('File %s does not exist.', $file)); - } - - return new MetadataBag(array_merge(array('filepath' => realpath($file), 'uri' => $file), $this->extractFromFile($file))); - } - - return new MetadataBag(array_merge(array('uri' => $file), $this->extractFromFile($file))); - } - - /** - * {@inheritdoc} - */ - public function readData($data) - { - return new MetadataBag($this->extractFromData($data)); - } - - /** - * {@inheritdoc} - */ - public function readStream($resource) - { - if (!is_resource($resource)) { - throw new InvalidArgumentException('Invalid resource provided.'); - } - - return new MetadataBag(array_merge($this->getStreamMetadata($resource), $this->extractFromStream($resource))); - } - - /** - * Gets the URI from a stream resource - * - * @param resource $resource - * - * @return string|null The URI f ava - */ - private function getStreamMetadata($resource) - { - $metadata = array(); - - if (false !== $data = @stream_get_meta_data($resource)) { - $metadata['uri'] = $data['uri']; - if (stream_is_local($resource)) { - $metadata['filepath'] = realpath($data['uri']); - } - } - - return $metadata; - } - - /** - * Extracts metadata from a file - * - * @param $file - * - * @return array An associative array of metadata - */ - abstract protected function extractFromFile($file); - - /** - * Extracts metadata from raw data - * - * @param $data - * - * @return array An associative array of metadata - */ - abstract protected function extractFromData($data); - - /** - * Extracts metadata from a stream - * - * @param $resource - * - * @return array An associative array of metadata - */ - abstract protected function extractFromStream($resource); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/DefaultMetadataReader.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/DefaultMetadataReader.php deleted file mode 100644 index 349366e06..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/DefaultMetadataReader.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Metadata; - -/** - * Default metadata reader - */ -class DefaultMetadataReader extends AbstractMetadataReader -{ - /** - * {@inheritdoc} - */ - protected function extractFromFile($file) - { - return array(); - } - - /** - * {@inheritdoc} - */ - protected function extractFromData($data) - { - return array(); - } - - /** - * {@inheritdoc} - */ - protected function extractFromStream($resource) - { - return array(); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/ExifMetadataReader.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/ExifMetadataReader.php deleted file mode 100644 index dd8e7d093..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/ExifMetadataReader.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Metadata; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\NotSupportedException; - -/** - * Metadata driven by Exif information - */ -class ExifMetadataReader extends AbstractMetadataReader -{ - public function __construct() - { - if (!self::isSupported()) { - throw new NotSupportedException('PHP exif extension is required to use the ExifMetadataReader'); - } - } - - public static function isSupported() - { - return function_exists('exif_read_data'); - } - - /** - * {@inheritdoc} - */ - protected function extractFromFile($file) - { - if (false === $data = @file_get_contents($file)) { - throw new InvalidArgumentException(sprintf('File %s is not readable.', $file)); - } - - return $this->doReadData($data); - } - - /** - * {@inheritdoc} - */ - protected function extractFromData($data) - { - return $this->doReadData($data); - } - - /** - * {@inheritdoc} - */ - protected function extractFromStream($resource) - { - if (0 < ftell($resource)) { - $metadata = stream_get_meta_data($resource); - if ($metadata['seekable']) { - rewind($resource); - } - } - - return $this->doReadData(stream_get_contents($resource)); - } - - /** - * Extracts metadata from raw data, merges with existing metadata - * - * @param string $data - * - * @return MetadataBag - */ - private function doReadData($data) - { - if (substr($data, 0, 2) === 'II') { - $mime = 'image/tiff'; - } else { - $mime = 'image/jpeg'; - } - - return $this->extract('data://' . $mime . ';base64,' . base64_encode($data)); - } - - /** - * Performs the exif data extraction given a path or data-URI representation. - * - * @param string $path The path to the file or the data-URI representation. - * - * @return MetadataBag - */ - private function extract($path) - { - if (false === $exifData = @exif_read_data($path, null, true, false)) { - return array(); - } - - $metadata = array(); - $sources = array('EXIF' => 'exif', 'IFD0' => 'ifd0'); - - foreach ($sources as $name => $prefix) { - if (!isset($exifData[$name])) { - continue; - } - foreach ($exifData[$name] as $prop => $value) { - $metadata[$prefix.'.'.$prop] = $value; - } - } - - return $metadata; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataBag.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataBag.php deleted file mode 100644 index b7e917a38..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataBag.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Metadata; - -/** - * An interface for Image Metadata - */ -class MetadataBag implements \ArrayAccess, \IteratorAggregate, \Countable -{ - /** @var array */ - private $data; - - public function __construct(array $data = array()) - { - $this->data = $data; - } - - /** - * Returns the metadata key, default value if it does not exist - * - * @param string $key - * @param mixed|null $default - * - * @return mixed - */ - public function get($key, $default = null) - { - return array_key_exists($key, $this->data) ? $this->data[$key] : $default; - } - - /** - * {@inheritdoc} - */ - public function count() - { - return count($this->data); - } - - /** - * {@inheritdoc} - */ - public function getIterator() - { - return new \ArrayIterator($this->data); - } - - /** - * {@inheritdoc} - */ - public function offsetExists($offset) - { - return array_key_exists($offset, $this->data); - } - - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value) - { - $this->data[$offset] = $value; - } - - /** - * {@inheritdoc} - */ - public function offsetUnset($offset) - { - unset($this->data[$offset]); - } - - /** - * {@inheritdoc} - */ - public function offsetGet($offset) - { - return $this->get($offset); - } - - /** - * Returns metadata as an array - * - * @return array An associative array - */ - public function toArray() - { - return $this->data; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataReaderInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataReaderInterface.php deleted file mode 100644 index 62fcc8817..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Metadata/MetadataReaderInterface.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Metadata; - -use Imagine\Exception\InvalidArgumentException; - -interface MetadataReaderInterface -{ - /** - * Reads metadata from a file. - * - * @param $file The path to the file where to read metadata. - * - * @throws InvalidArgumentException In case the file does not exist. - * - * @return MetadataBag - */ - public function readFile($file); - - /** - * Reads metadata from a binary string. - * - * @param $data The binary string to read. - * - * @return MetadataBag - */ - public function readData($data); - - /** - * Reads metadata from a stream. - * - * @param $resource The stream to read. - * - * @throws InvalidArgumentException In case the resource is not valid. - * - * @return MetadataBag - */ - public function readStream($resource); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/CMYK.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/CMYK.php deleted file mode 100644 index 2beecf28a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/CMYK.php +++ /dev/null @@ -1,118 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette; - -use Imagine\Image\Palette\Color\CMYK as CMYKColor; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Exception\RuntimeException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Image\Profile; -use Imagine\Image\ProfileInterface; - -class CMYK implements PaletteInterface -{ - private $parser; - private $profile; - private static $colors = array(); - - public function __construct() - { - $this->parser = new ColorParser(); - } - - /** - * {@inheritdoc} - */ - public function name() - { - return PaletteInterface::PALETTE_CMYK; - } - - /** - * {@inheritdoc} - */ - public function pixelDefinition() - { - return array( - ColorInterface::COLOR_CYAN, - ColorInterface::COLOR_MAGENTA, - ColorInterface::COLOR_YELLOW, - ColorInterface::COLOR_KEYLINE, - ); - } - - /** - * {@inheritdoc} - */ - public function supportsAlpha() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function color($color, $alpha = null) - { - if (null !== $alpha) { - throw new InvalidArgumentException('CMYK palette does not support alpha'); - } - - $color = $this->parser->parseToCMYK($color); - $index = sprintf('cmyk(%d, %d, %d, %d)', $color[0], $color[1], $color[2], $color[3]); - - if (false === array_key_exists($index, self::$colors)) { - self::$colors[$index] = new CMYKColor($this, $color); - } - - return self::$colors[$index]; - } - - /** - * {@inheritdoc} - */ - public function blend(ColorInterface $color1, ColorInterface $color2, $amount) - { - if (!$color1 instanceof CMYKColor || ! $color2 instanceof CMYKColor) { - throw new RuntimeException('CMYK palette can only blend CMYK colors'); - } - - return $this->color(array( - min(100, $color1->getCyan() + $color2->getCyan() * $amount), - min(100, $color1->getMagenta() + $color2->getMagenta() * $amount), - min(100, $color1->getYellow() + $color2->getYellow() * $amount), - min(100, $color1->getKeyline() + $color2->getKeyline() * $amount), - )); - } - - /** - * {@inheritdoc} - */ - public function useProfile(ProfileInterface $profile) - { - $this->profile = $profile; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function profile() - { - if (!$this->profile) { - $this->profile = Profile::fromPath(__DIR__ . '/../../resources/Adobe/CMYK/USWebUncoated.icc'); - } - - return $this->profile; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/CMYK.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/CMYK.php deleted file mode 100644 index 316543355..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/CMYK.php +++ /dev/null @@ -1,219 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette\Color; - -use Imagine\Image\Palette\CMYK as CMYKPalette; -use Imagine\Exception\RuntimeException; -use Imagine\Exception\InvalidArgumentException; - -final class CMYK implements ColorInterface -{ - /** - * @var integer - */ - private $c; - - /** - * @var integer - */ - private $m; - - /** - * @var integer - */ - private $y; - - /** - * @var integer - */ - private $k; - - /** - * - * @var CMYK - */ - private $palette; - - public function __construct(CMYKPalette $palette, array $color) - { - $this->palette = $palette; - $this->setColor($color); - } - - /** - * {@inheritdoc} - */ - public function getValue($component) - { - switch ($component) { - case ColorInterface::COLOR_CYAN: - return $this->getCyan(); - case ColorInterface::COLOR_MAGENTA: - return $this->getMagenta(); - case ColorInterface::COLOR_YELLOW: - return $this->getYellow(); - case ColorInterface::COLOR_KEYLINE: - return $this->getKeyline(); - default: - throw new InvalidArgumentException(sprintf('Color component %s is not valid', $component)); - } - } - - /** - * Returns Cyan value of the color - * - * @return integer - */ - public function getCyan() - { - return $this->c; - } - - /** - * Returns Magenta value of the color - * - * @return integer - */ - public function getMagenta() - { - return $this->m; - } - - /** - * Returns Yellow value of the color - * - * @return integer - */ - public function getYellow() - { - return $this->y; - } - - /** - * Returns Key value of the color - * - * @return integer - */ - public function getKeyline() - { - return $this->k; - } - - /** - * {@inheritdoc} - */ - public function getPalette() - { - return $this->palette; - } - - /** - * {@inheritdoc} - */ - public function getAlpha() - { - return null; - } - - /** - * {@inheritdoc} - */ - public function dissolve($alpha) - { - throw new RuntimeException('CMYK does not support dissolution'); - } - - /** - * {@inheritdoc} - */ - public function lighten($shade) - { - return $this->palette->color( - array( - $this->c, - $this->m, - $this->y, - max(0, $this->k - $shade), - ) - ); - } - - /** - * {@inheritdoc} - */ - public function darken($shade) - { - return $this->palette->color( - array( - $this->c, - $this->m, - $this->y, - min(100, $this->k + $shade), - ) - ); - } - - /** - * {@inheritdoc} - */ - public function grayscale() - { - $color = array( - $this->c * (1 - $this->k / 100) + $this->k, - $this->m * (1 - $this->k / 100) + $this->k, - $this->y * (1 - $this->k / 100) + $this->k, - ); - - $gray = min(100, round(0.299 * $color[0] + 0.587 * $color[1] + 0.114 * $color[2])); - - return $this->palette->color(array($gray, $gray, $gray, $this->k)); - } - - /** - * {@inheritdoc} - */ - public function isOpaque() - { - return true; - } - - /** - * Returns hex representation of the color - * - * @return string - */ - public function __toString() - { - return sprintf('cmyk(%d%%, %d%%, %d%%, %d%%)', $this->c, $this->m, $this->y, $this->k); - } - - /** - * Internal, Performs checks for color validity (an of array(C, M, Y, K)) - * - * @param array $color - * - * @throws InvalidArgumentException - */ - private function setColor(array $color) - { - if (count($color) !== 4) { - throw new InvalidArgumentException('Color argument must look like array(C, M, Y, K), where C, M, Y, K are the integer values between 0 and 255 for cyan, magenta, yellow and black color indexes accordingly'); - } - - $colors = array_values($color); - array_walk($colors, function ($color) { - return max(0, min(100, $color)); - }); - - list($this->c, $this->m, $this->y, $this->k) = $colors; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/ColorInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/ColorInterface.php deleted file mode 100644 index 8784c4ed3..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/ColorInterface.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette\Color; - -use Imagine\Image\Palette\PaletteInterface; - -interface ColorInterface -{ - const COLOR_RED = 'red'; - const COLOR_GREEN = 'green'; - const COLOR_BLUE = 'blue'; - - const COLOR_CYAN = 'cyan'; - const COLOR_MAGENTA = 'magenta'; - const COLOR_YELLOW = 'yellow'; - const COLOR_KEYLINE = 'keyline'; - - const COLOR_GRAY = 'gray'; - - /** - * Return the value of one of the component. - * - * @param string $component One of the ColorInterface::COLOR_* component - * - * @return Integer - */ - public function getValue($component); - - /** - * Returns percentage of transparency of the color - * - * @return integer - */ - public function getAlpha(); - - /** - * Returns the palette attached to the current color - * - * @return PaletteInterface - */ - public function getPalette(); - - /** - * Returns a copy of current color, incrementing the alpha channel by the - * given amount - * - * @param integer $alpha - * - * @return ColorInterface - */ - public function dissolve($alpha); - - /** - * Returns a copy of the current color, lightened by the specified number - * of shades - * - * @param integer $shade - * - * @return ColorInterface - */ - public function lighten($shade); - - /** - * Returns a copy of the current color, darkened by the specified number of - * shades - * - * @param integer $shade - * - * @return ColorInterface - */ - public function darken($shade); - - /** - * Returns a gray related to the current color - * - * @return ColorInterface - */ - public function grayscale(); - - /** - * Checks if the current color is opaque - * - * @return Boolean - */ - public function isOpaque(); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/Gray.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/Gray.php deleted file mode 100644 index c8096450f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/Gray.php +++ /dev/null @@ -1,164 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette\Color; - -use Imagine\Image\Palette\Grayscale; -use Imagine\Exception\InvalidArgumentException; - -final class Gray implements ColorInterface -{ - /** - * @var integer - */ - private $gray; - - /** - * @var integer - */ - private $alpha; - - /** - * - * @var Grayscale - */ - private $palette; - - public function __construct(Grayscale $palette, array $color, $alpha) - { - $this->palette = $palette; - $this->setColor($color); - $this->setAlpha($alpha); - } - - /** - * {@inheritdoc} - */ - public function getValue($component) - { - switch ($component) { - case ColorInterface::COLOR_GRAY: - return $this->getGray(); - default: - throw new InvalidArgumentException(sprintf('Color component %s is not valid', $component)); - } - } - - /** - * Returns Gray value of the color - * - * @return integer - */ - public function getGray() - { - return $this->gray; - } - - /** - * {@inheritdoc} - */ - public function getPalette() - { - return $this->palette; - } - - /** - * {@inheritdoc} - */ - public function getAlpha() - { - return $this->alpha; - } - - /** - * {@inheritdoc} - */ - public function dissolve($alpha) - { - return $this->palette->color( - array($this->gray), $this->alpha + $alpha - ); - } - - /** - * {@inheritdoc} - */ - public function lighten($shade) - { - return $this->palette->color(array(min(255, $this->gray + $shade)), $this->alpha); - } - - /** - * {@inheritdoc} - */ - public function darken($shade) - { - return $this->palette->color(array(max(0, $this->gray - $shade)), $this->alpha); - } - - /** - * {@inheritdoc} - */ - public function grayscale() - { - return $this; - } - - /** - * {@inheritdoc} - */ - public function isOpaque() - { - return 100 === $this->alpha; - } - - /** - * Returns hex representation of the color - * - * @return string - */ - public function __toString() - { - return sprintf('#%02x%02x%02x', $this->gray, $this->gray, $this->gray); - } - - /** - * Performs checks for validity of given alpha value and sets it - * - * @param integer $alpha - * - * @throws InvalidArgumentException - */ - private function setAlpha($alpha) - { - if (!is_int($alpha) || $alpha < 0 || $alpha > 100) { - throw new InvalidArgumentException(sprintf('Alpha must be an integer between 0 and 100, %s given', $alpha)); - } - - $this->alpha = $alpha; - } - - /** - * Performs checks for color validity (array of array(gray)) - * - * @param array $color - * - * @throws InvalidArgumentException - */ - private function setColor(array $color) - { - if (count($color) !== 1) { - throw new InvalidArgumentException('Color argument must look like array(gray), where gray is the integer value between 0 and 255 for the grayscale'); - } - - list($this->gray) = array_values($color); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/RGB.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/RGB.php deleted file mode 100644 index a0b4f0d01..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Color/RGB.php +++ /dev/null @@ -1,214 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette\Color; - -use Imagine\Image\Palette\RGB as RGBPalette; -use Imagine\Exception\InvalidArgumentException; - -final class RGB implements ColorInterface -{ - /** - * @var integer - */ - private $r; - - /** - * @var integer - */ - private $g; - - /** - * @var integer - */ - private $b; - - /** - * @var integer - */ - private $alpha; - - /** - * - * @var RGBPalette - */ - private $palette; - - public function __construct(RGBPalette $palette, array $color, $alpha) - { - $this->palette = $palette; - $this->setColor($color); - $this->setAlpha($alpha); - } - - /** - * {@inheritdoc} - */ - public function getValue($component) - { - switch ($component) { - case ColorInterface::COLOR_RED: - return $this->getRed(); - case ColorInterface::COLOR_GREEN: - return $this->getGreen(); - case ColorInterface::COLOR_BLUE: - return $this->getBlue(); - default: - throw new InvalidArgumentException(sprintf('Color component %s is not valid', $component)); - } - } - - /** - * Returns RED value of the color - * - * @return integer - */ - public function getRed() - { - return $this->r; - } - - /** - * Returns GREEN value of the color - * - * @return integer - */ - public function getGreen() - { - return $this->g; - } - - /** - * Returns BLUE value of the color - * - * @return integer - */ - public function getBlue() - { - return $this->b; - } - - /** - * {@inheritdoc} - */ - public function getPalette() - { - return $this->palette; - } - - /** - * {@inheritdoc} - */ - public function getAlpha() - { - return $this->alpha; - } - - /** - * {@inheritdoc} - */ - public function dissolve($alpha) - { - return $this->palette->color(array($this->r, $this->g, $this->b), $this->alpha + $alpha); - } - - /** - * {@inheritdoc} - */ - public function lighten($shade) - { - return $this->palette->color( - array( - min(255, $this->r + $shade), - min(255, $this->g + $shade), - min(255, $this->b + $shade), - ), $this->alpha - ); - } - - /** - * {@inheritdoc} - */ - public function darken($shade) - { - return $this->palette->color( - array( - max(0, $this->r - $shade), - max(0, $this->g - $shade), - max(0, $this->b - $shade), - ), $this->alpha - ); - } - - /** - * {@inheritdoc} - */ - public function grayscale() - { - $gray = min(255, round(0.299 * $this->getRed() + 0.114 * $this->getBlue() + 0.587 * $this->getGreen())); - - return $this->palette->color(array($gray, $gray, $gray), $this->alpha); - } - - /** - * {@inheritdoc} - */ - public function isOpaque() - { - return 100 === $this->alpha; - } - - /** - * Returns hex representation of the color - * - * @return string - */ - public function __toString() - { - return sprintf('#%02x%02x%02x', $this->r, $this->g, $this->b); - } - - /** - * Internal - * - * Performs checks for validity of given alpha value and sets it - * - * @param integer $alpha - * - * @throws InvalidArgumentException - */ - private function setAlpha($alpha) - { - if (!is_int($alpha) || $alpha < 0 || $alpha > 100) { - throw new InvalidArgumentException(sprintf('Alpha must be an integer between 0 and 100, %s given', $alpha)); - } - - $this->alpha = $alpha; - } - - /** - * Internal - * - * Performs checks for color validity (array of array(R, G, B)) - * - * @param array $color - * - * @throws InvalidArgumentException - */ - private function setColor(array $color) - { - if (count($color) !== 3) { - throw new InvalidArgumentException('Color argument must look like array(R, G, B), where R, G, B are the integer values between 0 and 255 for red, green and blue color indexes accordingly'); - } - - list($this->r, $this->g, $this->b) = array_values($color); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/ColorParser.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/ColorParser.php deleted file mode 100644 index 35cf4e952..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/ColorParser.php +++ /dev/null @@ -1,153 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette; - -use Imagine\Exception\InvalidArgumentException; - -class ColorParser -{ - /** - * Parses a color to a RGB tuple - * - * @param string|array|integer $color - * - * @return array - * - * @throws InvalidArgumentException - */ - public function parseToRGB($color) - { - $color = $this->parse($color); - - if (4 === count($color)) { - $color = array( - 255 * (1 - $color[0] / 100) * (1 - $color[3] / 100), - 255 * (1 - $color[1] / 100) * (1 - $color[3] / 100), - 255 * (1 - $color[2] / 100) * (1 - $color[3] / 100), - ); - } - - return $color; - } - - /** - * Parses a color to a CMYK tuple - * - * @param string|array|integer $color - * - * @return array - * - * @throws InvalidArgumentException - */ - public function parseToCMYK($color) - { - $color = $this->parse($color); - - if (3 === count($color)) { - $r = $color[0] / 255; - $g = $color[1] / 255; - $b = $color[2] / 255; - - $k = 1 - max($r, $g, $b); - - $color = array( - 1 === $k ? 0 : round((1 - $r - $k) / (1- $k) * 100), - 1 === $k ? 0 : round((1 - $g - $k) / (1- $k) * 100), - 1 === $k ? 0 : round((1 - $b - $k) / (1- $k) * 100), - round($k * 100) - ); - } - - return $color; - } - - /** - * Parses a color to a grayscale value - * - * @param string|array|integer $color - * - * @return array - * - * @throws InvalidArgumentException - */ - public function parseToGrayscale($color) - { - if (is_array($color) && 1 === count($color)) { - return array_values($color); - } - - $color = array_unique($this->parse($color)); - - if (1 !== count($color)) { - throw new InvalidArgumentException('The provided color has different values of red, green and blue components. Grayscale colors must have the same values for these.'); - } - - return $color; - } - - /** - * Parses a color - * - * @param string|array|integer $color - * - * @return array - * - * @throws InvalidArgumentException - */ - private function parse($color) - { - if (!is_string($color) && !is_array($color) && !is_int($color)) { - throw new InvalidArgumentException(sprintf('Color must be specified as a hexadecimal string, array or integer, %s given', gettype($color))); - } - - if (is_array($color)) { - if (3 === count($color) || 4 === count($color)) { - return array_values($color); - } - throw new InvalidArgumentException('Color argument if array, must look like array(R, G, B), or array(C, M, Y, K) where R, G, B are the integer values between 0 and 255 for red, green and blue or cyan, magenta, yellow and black color indexes accordingly'); - } - - if (is_string($color)) { - if (0 === strpos($color, 'cmyk(')) { - $substrColor = substr($color, 5, strlen($color) - 6); - - $components = array_map(function ($component) { - return round(trim($component, ' %')); - }, explode(',', $substrColor)); - - if (count($components) !== 4) { - throw new InvalidArgumentException(sprintf('Unable to parse color %s', $color)); - } - - return $components; - } else { - $color = ltrim($color, '#'); - - if (strlen($color) !== 3 && strlen($color) !== 6) { - throw new InvalidArgumentException(sprintf('Color must be a hex value in regular (6 characters) or short (3 characters) notation, "%s" given', $color)); - } - - if (strlen($color) === 3) { - $color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2]; - } - - $color = array_map('hexdec', str_split($color, 2)); - } - } - - if (is_int($color)) { - $color = array(255 & ($color >> 16), 255 & ($color >> 8), 255 & $color); - } - - return $color; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Grayscale.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Grayscale.php deleted file mode 100644 index 088b7909a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/Grayscale.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette; - -use Imagine\Image\Palette\Color\Gray as GrayColor; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\ProfileInterface; -use Imagine\Image\Profile; -use Imagine\Exception\RuntimeException; - -class Grayscale implements PaletteInterface -{ - /** - * @var ColorParser - */ - private $parser; - - /** - * @var ProfileInterface - */ - private $profile; - - /** - * @var array - */ - protected static $colors = array(); - - public function __construct() - { - $this->parser = new ColorParser(); - } - - /** - * {@inheritdoc} - */ - public function name() - { - return PaletteInterface::PALETTE_GRAYSCALE; - } - - /** - * {@inheritdoc} - */ - public function pixelDefinition() - { - return array(ColorInterface::COLOR_GRAY); - } - - /** - * {@inheritdoc} - */ - public function supportsAlpha() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function useProfile(ProfileInterface $profile) - { - $this->profile = $profile; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function profile() - { - if (!$this->profile) { - $this->profile = Profile::fromPath(__DIR__ . '/../../resources/colormanagement.org/ISOcoated_v2_grey1c_bas.ICC'); - } - - return $this->profile; - } - - /** - * {@inheritdoc} - */ - public function color($color, $alpha = null) - { - if (null === $alpha) { - $alpha = 0; - } - - $color = $this->parser->parseToGrayscale($color); - $index = sprintf('#%02x%02x%02x-%d', $color[0], $color[0], $color[0], $alpha); - - if (false === array_key_exists($index, static::$colors)) { - static::$colors[$index] = new GrayColor($this, $color, $alpha); - } - - return static::$colors[$index]; - } - - /** - * {@inheritdoc} - */ - public function blend(ColorInterface $color1, ColorInterface $color2, $amount) - { - if (!$color1 instanceof GrayColor || ! $color2 instanceof GrayColor) { - throw new RuntimeException('Grayscale palette can only blend Grayscale colors'); - } - - return $this->color( - array( - (int) min(255, min($color1->getGray(), $color2->getGray()) + round(abs($color2->getGray() - $color1->getGray()) * $amount)), - ), - (int) min(100, min($color1->getAlpha(), $color2->getAlpha()) + round(abs($color2->getAlpha() - $color1->getAlpha()) * $amount)) - ); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/PaletteInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/PaletteInterface.php deleted file mode 100644 index 855c244c4..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/PaletteInterface.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette; - -use Imagine\Image\ProfileInterface; -use Imagine\Image\Palette\Color\ColorInterface; - -interface PaletteInterface -{ - const PALETTE_GRAYSCALE = 'gray'; - const PALETTE_RGB = 'rgb'; - const PALETTE_CMYK = 'cmyk'; - - /** - * Returns a color given some values - * - * @param string|array|integer $color A color - * @param integer|null $alpha Set alpha to null to disable it - * - * @return ColorInterface - * - * @throws InvalidArgumentException In case you pass an alpha value to a - * Palette that does not support alpha - */ - public function color($color, $alpha = null); - - /** - * Blend two colors given an amount - * - * @param ColorInterface $color1 - * @param ColorInterface $color2 - * @param float $amount The amount of color2 in color1 - * - * @return ColorInterface - */ - public function blend(ColorInterface $color1, ColorInterface $color2, $amount); - - /** - * Attachs an ICC profile to this Palette. - * - * (A default profile is provided by default) - * - * @param ProfileInterface $profile - * - * @return PaletteInterface - */ - public function useProfile(ProfileInterface $profile); - - /** - * Returns the ICC profile attached to this Palette. - * - * @return ProfileInterface - */ - public function profile(); - - /** - * Returns the name of this Palette, one of PaletteInterface::PALETTE_* - * constants - * - * @return String - */ - public function name(); - - /** - * Returns an array containing ColorInterface::COLOR_* constants that - * define the structure of colors for a pixel. - * - * @return array - */ - public function pixelDefinition(); - - /** - * Tells if alpha channel is supported in this palette - * - * @return Boolean - */ - public function supportsAlpha(); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/RGB.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/RGB.php deleted file mode 100644 index 0462ca4da..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Palette/RGB.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Palette; - -use Imagine\Image\Palette\Color\RGB as RGBColor; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\ProfileInterface; -use Imagine\Image\Profile; -use Imagine\Exception\RuntimeException; - -class RGB implements PaletteInterface -{ - /** - * @var ColorParser - */ - private $parser; - - /** - * @var ProfileInterface - */ - private $profile; - - /** - * @var array - */ - protected static $colors = array(); - - public function __construct() - { - $this->parser = new ColorParser(); - } - - /** - * {@inheritdoc} - */ - public function name() - { - return PaletteInterface::PALETTE_RGB; - } - - /** - * {@inheritdoc} - */ - public function pixelDefinition() - { - return array( - ColorInterface::COLOR_RED, - ColorInterface::COLOR_GREEN, - ColorInterface::COLOR_BLUE, - ); - } - - /** - * {@inheritdoc} - */ - public function supportsAlpha() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function useProfile(ProfileInterface $profile) - { - $this->profile = $profile; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function profile() - { - if (!$this->profile) { - $this->profile = Profile::fromPath(__DIR__ . '/../../resources/color.org/sRGB_IEC61966-2-1_black_scaled.icc'); - } - - return $this->profile; - } - - /** - * {@inheritdoc} - */ - public function color($color, $alpha = null) - { - if (null === $alpha) { - $alpha = 100; - } - - $color = $this->parser->parseToRGB($color); - $index = sprintf('#%02x%02x%02x-%d', $color[0], $color[1], $color[2], $alpha); - - if (false === array_key_exists($index, static::$colors)) { - static::$colors[$index] = new RGBColor($this, $color, $alpha); - } - - return static::$colors[$index]; - } - - /** - * {@inheritdoc} - */ - public function blend(ColorInterface $color1, ColorInterface $color2, $amount) - { - if (!$color1 instanceof RGBColor || ! $color2 instanceof RGBColor) { - throw new RuntimeException('RGB palette can only blend RGB colors'); - } - - return $this->color( - array( - (int) min(255, min($color1->getRed(), $color2->getRed()) + round(abs($color2->getRed() - $color1->getRed()) * $amount)), - (int) min(255, min($color1->getGreen(), $color2->getGreen()) + round(abs($color2->getGreen() - $color1->getGreen()) * $amount)), - (int) min(255, min($color1->getBlue(), $color2->getBlue()) + round(abs($color2->getBlue() - $color1->getBlue()) * $amount)), - ), - (int) min(100, min($color1->getAlpha(), $color2->getAlpha()) + round(abs($color2->getAlpha() - $color1->getAlpha()) * $amount)) - ); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Point.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Point.php deleted file mode 100644 index abfc7c3a8..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Point.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; - -/** - * The point class - */ -final class Point implements PointInterface -{ - /** - * @var integer - */ - private $x; - - /** - * @var integer - */ - private $y; - - /** - * Constructs a point of coordinates - * - * @param integer $x - * @param integer $y - * - * @throws InvalidArgumentException - */ - public function __construct($x, $y) - { - if ($x < 0 || $y < 0) { - throw new InvalidArgumentException(sprintf('A coordinate cannot be positioned outside of a bounding box (x: %s, y: %s given)', $x, $y)); - } - - $this->x = $x; - $this->y = $y; - } - - /** - * {@inheritdoc} - */ - public function getX() - { - return $this->x; - } - - /** - * {@inheritdoc} - */ - public function getY() - { - return $this->y; - } - - /** - * {@inheritdoc} - */ - public function in(BoxInterface $box) - { - return $this->x < $box->getWidth() && $this->y < $box->getHeight(); - } - - /** - * {@inheritdoc} - */ - public function move($amount) - { - return new Point($this->x + $amount, $this->y + $amount); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return sprintf('(%d, %d)', $this->x, $this->y); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Point/Center.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Point/Center.php deleted file mode 100644 index 0e60349e1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Point/Center.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Point; - -use Imagine\Image\BoxInterface; -use Imagine\Image\Point as OriginalPoint; -use Imagine\Image\PointInterface; - -/** - * Point center - */ -final class Center implements PointInterface -{ - /** - * @var BoxInterface - */ - private $box; - - /** - * Constructs coordinate with size instance, it needs to be relative to - * - * @param BoxInterface $box - */ - public function __construct(BoxInterface $box) - { - $this->box = $box; - } - - /** - * {@inheritdoc} - */ - public function getX() - { - return ceil($this->box->getWidth() / 2); - } - - /** - * {@inheritdoc} - */ - public function getY() - { - return ceil($this->box->getHeight() / 2); - } - - /** - * {@inheritdoc} - */ - public function in(BoxInterface $box) - { - return $this->getX() < $box->getWidth() && $this->getY() < $box->getHeight(); - } - - /** - * {@inheritdoc} - */ - public function move($amount) - { - return new OriginalPoint($this->getX() + $amount, $this->getY() + $amount); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return sprintf('(%d, %d)', $this->getX(), $this->getY()); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/PointInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/PointInterface.php deleted file mode 100644 index f35fa80da..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/PointInterface.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -/** - * The point interface - */ -interface PointInterface -{ - /** - * Gets points x coordinate - * - * @return integer - */ - public function getX(); - - /** - * Gets points y coordinate - * - * @return integer - */ - public function getY(); - - /** - * Checks if current coordinate is inside a given bo - * - * @param BoxInterface $box - * - * @return Boolean - */ - public function in(BoxInterface $box); - - /** - * Returns another point, moved by a given amount from current coordinates - * - * @param integer $amount - * @return ImageInterface - */ - public function move($amount); - - /** - * Gets a string representation for the current point - * - * @return string - */ - public function __toString(); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Profile.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/Profile.php deleted file mode 100644 index fda5415a6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/Profile.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; - -class Profile implements ProfileInterface -{ - private $data; - private $name; - - public function __construct($name, $data) - { - $this->name = $name; - $this->data = $data; - } - - /** - * {@inheritdoc} - */ - public function name() - { - return $this->name; - } - - /** - * {@inheritdoc} - */ - public function data() - { - return $this->data; - } - - /** - * Creates a profile from a path to a file - * - * @param String $path - * - * @return Profile - * - * @throws InvalidArgumentException In case the provided path is not valid - */ - public static function fromPath($path) - { - if (!file_exists($path) || !is_file($path) || !is_readable($path)) { - throw new InvalidArgumentException(sprintf('Path %s is an invalid profile file or is not readable', $path)); - } - - return new static(basename($path), file_get_contents($path)); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ProfileInterface.php b/snappymail/v/0.0.0/app/libraries/Imagine/Image/ProfileInterface.php deleted file mode 100644 index b2caa9c9a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Image/ProfileInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -interface ProfileInterface -{ - /** - * Returns the name of the profile - * - * @return String - */ - public function name(); - - /** - * Returns the profile data - * - * @return String - */ - public function data(); -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Drawer.php b/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Drawer.php deleted file mode 100644 index 2f8636491..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Drawer.php +++ /dev/null @@ -1,404 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; - -/** - * Drawer implementation using the Imagick PHP extension - */ -final class Drawer implements DrawerInterface -{ - /** - * @var Imagick - */ - private $imagick; - - /** - * @param \Imagick $imagick - */ - public function __construct(\Imagick $imagick) - { - $this->imagick = $imagick; - } - - /** - * {@inheritdoc} - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $arc = new \ImagickDraw(); - - $arc->setStrokeColor($pixel); - $arc->setStrokeWidth(max(1, (int) $thickness)); - $arc->setFillColor('transparent'); - $arc->arc($x - $width / 2, $y - $height / 2, $x + $width / 2, $y + $height / 2, $start, $end); - - $this->imagick->drawImage($arc); - - $pixel->clear(); - $pixel->destroy(); - - $arc->clear(); - $arc->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw arc operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $chord = new \ImagickDraw(); - - $chord->setStrokeColor($pixel); - $chord->setStrokeWidth(max(1, (int) $thickness)); - - if ($fill) { - $chord->setFillColor($pixel); - } else { - $this->line( - new Point(round($x + $width / 2 * cos(deg2rad($start))), round($y + $height / 2 * sin(deg2rad($start)))), - new Point(round($x + $width / 2 * cos(deg2rad($end))), round($y + $height / 2 * sin(deg2rad($end)))), - $color - ); - - $chord->setFillColor('transparent'); - } - - $chord->arc( - $x - $width / 2, - $y - $height / 2, - $x + $width / 2, - $y + $height / 2, - $start, - $end - ); - - $this->imagick->drawImage($chord); - - $pixel->clear(); - $pixel->destroy(); - - $chord->clear(); - $chord->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw chord operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $ellipse = new \ImagickDraw(); - - $ellipse->setStrokeColor($pixel); - $ellipse->setStrokeWidth(max(1, (int) $thickness)); - - if ($fill) { - $ellipse->setFillColor($pixel); - } else { - $ellipse->setFillColor('transparent'); - } - - $ellipse->ellipse( - $center->getX(), - $center->getY(), - $width / 2, - $height / 2, - 0, 360 - ); - - if (false === $this->imagick->drawImage($ellipse)) { - throw new RuntimeException('Ellipse operation failed'); - } - - $pixel->clear(); - $pixel->destroy(); - - $ellipse->clear(); - $ellipse->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw ellipse operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function line(PointInterface $start, PointInterface $end, ColorInterface $color, $thickness = 1) - { - try { - $pixel = $this->getColor($color); - $line = new \ImagickDraw(); - - $line->setStrokeColor($pixel); - $line->setStrokeWidth(max(1, (int) $thickness)); - $line->setFillColor($pixel); - $line->line( - $start->getX(), - $start->getY(), - $end->getX(), - $end->getY() - ); - - $this->imagick->drawImage($line); - - $pixel->clear(); - $pixel->destroy(); - - $line->clear(); - $line->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw line operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $x1 = round($center->getX() + $width / 2 * cos(deg2rad($start))); - $y1 = round($center->getY() + $height / 2 * sin(deg2rad($start))); - $x2 = round($center->getX() + $width / 2 * cos(deg2rad($end))); - $y2 = round($center->getY() + $height / 2 * sin(deg2rad($end))); - - if ($fill) { - $this->chord($center, $size, $start, $end, $color, true, $thickness); - $this->polygon( - array( - $center, - new Point($x1, $y1), - new Point($x2, $y2), - ), - $color, - true, - $thickness - ); - } else { - $this->arc($center, $size, $start, $end, $color, $thickness); - $this->line($center, new Point($x1, $y1), $color, $thickness); - $this->line($center, new Point($x2, $y2), $color, $thickness); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function dot(PointInterface $position, ColorInterface $color) - { - $x = $position->getX(); - $y = $position->getY(); - - try { - $pixel = $this->getColor($color); - $point = new \ImagickDraw(); - - $point->setFillColor($pixel); - $point->point($x, $y); - - $this->imagick->drawimage($point); - - $pixel->clear(); - $pixel->destroy(); - - $point->clear(); - $point->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw point operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1) - { - if (count($coordinates) < 3) { - throw new InvalidArgumentException(sprintf('Polygon must consist of at least 3 coordinates, %d given', count($coordinates))); - } - - $points = array_map(function (PointInterface $p) { - return array('x' => $p->getX(), 'y' => $p->getY()); - }, $coordinates); - - try { - $pixel = $this->getColor($color); - $polygon = new \ImagickDraw(); - - $polygon->setStrokeColor($pixel); - $polygon->setStrokeWidth(max(1, (int) $thickness)); - - if ($fill) { - $polygon->setFillColor($pixel); - } else { - $polygon->setFillColor('transparent'); - } - - $polygon->polygon($points); - $this->imagick->drawImage($polygon); - - $pixel->clear(); - $pixel->destroy(); - - $polygon->clear(); - $polygon->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw polygon operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null) - { - try { - $pixel = $this->getColor($font->getColor()); - $text = new \ImagickDraw(); - - $text->setFont($font->getFile()); - /** - * @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027 - * - * ensure font resolution is the same as GD's hard-coded 96 - */ - if (version_compare(phpversion("imagick"), "3.0.2", ">=")) { - $text->setResolution(96, 96); - $text->setFontSize($font->getSize()); - } else { - $text->setFontSize((int) ($font->getSize() * (96 / 72))); - } - $text->setFillColor($pixel); - $text->setTextAntialias(true); - - $info = $this->imagick->queryFontMetrics($text, $string); - $rad = deg2rad($angle); - $cos = cos($rad); - $sin = sin($rad); - - // round(0 * $cos - 0 * $sin) - $x1 = 0; - $x2 = round($info['characterWidth'] * $cos - $info['characterHeight'] * $sin); - // round(0 * $sin + 0 * $cos) - $y1 = 0; - $y2 = round($info['characterWidth'] * $sin + $info['characterHeight'] * $cos); - - $xdiff = 0 - min($x1, $x2); - $ydiff = 0 - min($y1, $y2); - - if ($width !== null) { - $string = $this->wrapText($string, $text, $angle, $width); - } - - $this->imagick->annotateImage( - $text, $position->getX() + $x1 + $xdiff, - $position->getY() + $y2 + $ydiff, $angle, $string - ); - - $pixel->clear(); - $pixel->destroy(); - - $text->clear(); - $text->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Draw text operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * Gets specifically formatted color string from ColorInterface instance - * - * @param ColorInterface $color - * - * @return string - */ - private function getColor(ColorInterface $color) - { - $pixel = new \ImagickPixel((string) $color); - $pixel->setColorValue(\Imagick::COLOR_ALPHA, $color->getAlpha() / 100); - - return $pixel; - } - - /** - * Internal - * - * Fits a string into box with given width - */ - private function wrapText($string, $text, $angle, $width) - { - $result = ''; - $words = explode(' ', $string); - foreach ($words as $word) { - $teststring = $result . ' ' . $word; - $testbox = $this->imagick->queryFontMetrics($text, $teststring, true); - if ($testbox['textWidth'] > $width) { - $result .= ($result == '' ? '' : "\n") . $word; - } else { - $result .= ($result == '' ? '' : ' ') . $word; - } - } - - return $result; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Effects.php b/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Effects.php deleted file mode 100644 index debe32a60..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Effects.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Effects\EffectsInterface; -use Imagine\Exception\RuntimeException; -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * Effects implementation using the Imagick PHP extension - */ -class Effects implements EffectsInterface -{ - private $imagick; - - public function __construct(\Imagick $imagick) - { - $this->imagick = $imagick; - } - - /** - * {@inheritdoc} - */ - public function gamma($correction) - { - try { - $this->imagick->gammaImage($correction, \Imagick::CHANNEL_ALL); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to apply gamma correction to the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function negative() - { - try { - $this->imagick->negateImage(false, \Imagick::CHANNEL_ALL); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to negate the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function grayscale() - { - try { - $this->imagick->setImageType(\Imagick::IMGTYPE_GRAYSCALE); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to grayscale the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function colorize(ColorInterface $color) - { - try { - $this->imagick->colorizeImage((string) $color, 1); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to colorize the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function sharpen() - { - try { - $this->imagick->sharpenImage(2, 1); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to sharpen the image'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function blur($sigma = 1) - { - try { - $this->imagick->gaussianBlurImage(0, $sigma); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to blur the image', $e->getCode(), $e); - } - - return $this; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Font.php b/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Font.php deleted file mode 100644 index 3fc41dd2f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Font.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Image\AbstractFont; -use Imagine\Image\Box; -use Imagine\Image\Palette\Color\ColorInterface; - -/** - * Font implementation using the Imagick PHP extension - */ -final class Font extends AbstractFont -{ - /** - * @var \Imagick - */ - private $imagick; - - /** - * @param \Imagick $imagick - * @param string $file - * @param integer $size - * @param ColorInterface $color - */ - public function __construct(\Imagick $imagick, $file, $size, ColorInterface $color) - { - $this->imagick = $imagick; - - parent::__construct($file, $size, $color); - } - - /** - * {@inheritdoc} - */ - public function box($string, $angle = 0) - { - $text = new \ImagickDraw(); - - $text->setFont($this->file); - - /** - * @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027 - * - * ensure font resolution is the same as GD's hard-coded 96 - */ - if (version_compare(phpversion("imagick"), "3.0.2", ">=")) { - $text->setResolution(96, 96); - $text->setFontSize($this->size); - } else { - $text->setFontSize((int) ($this->size * (96 / 72))); - } - - $info = $this->imagick->queryFontMetrics($text, $string); - - $box = new Box($info['textWidth'], $info['textHeight']); - - return $box; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Image.php b/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Image.php deleted file mode 100644 index c94f605d5..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Image.php +++ /dev/null @@ -1,880 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractImage; -use Imagine\Image\Box; -use Imagine\Image\BoxInterface; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Image\Fill\FillInterface; -use Imagine\Image\Fill\Gradient\Horizontal; -use Imagine\Image\Fill\Gradient\Linear; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; -use Imagine\Image\ProfileInterface; -use Imagine\Image\ImageInterface; -use Imagine\Image\Palette\PaletteInterface; - -/** - * Image implementation using the Imagick PHP extension - */ -final class Image extends AbstractImage -{ - /** - * @var \Imagick - */ - private $imagick; - /** - * @var Layers - */ - private $layers; - /** - * @var PaletteInterface - */ - private $palette; - - /** - * @var Boolean - */ - private static $supportsColorspaceConversion; - - private static $colorspaceMapping = array( - PaletteInterface::PALETTE_CMYK => \Imagick::COLORSPACE_CMYK, - PaletteInterface::PALETTE_RGB => \Imagick::COLORSPACE_RGB, - PaletteInterface::PALETTE_GRAYSCALE => \Imagick::COLORSPACE_GRAY, - ); - - /** - * Constructs a new Image instance - * - * @param \Imagick $imagick - * @param PaletteInterface $palette - * @param MetadataBag $metadata - */ - public function __construct(\Imagick $imagick, PaletteInterface $palette, MetadataBag $metadata) - { - $this->metadata = $metadata; - $this->detectColorspaceConversionSupport(); - $this->imagick = $imagick; - if (static::$supportsColorspaceConversion) { - $this->setColorspace($palette); - } - $this->palette = $palette; - $this->layers = new Layers($this, $this->palette, $this->imagick); - } - - /** - * Destroys allocated imagick resources - */ - public function __destruct() - { - if ($this->imagick instanceof \Imagick) { - $this->imagick->clear(); - $this->imagick->destroy(); - } - } - - /** - * Returns the underlying \Imagick instance - * - * @return \Imagick - */ - public function getImagick() - { - return $this->imagick; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function copy() - { - try { - if (version_compare(phpversion("imagick"), "3.1.0b1", ">=") || defined("HHVM_VERSION")) { - $clone = clone $this->imagick; - } else { - $clone = $this->imagick->clone(); - } - } catch (\ImagickException $e) { - throw new RuntimeException('Copy operation failed', $e->getCode(), $e); - } - - return new self($clone, $this->palette, clone $this->metadata); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function crop(PointInterface $start, BoxInterface $size) - { - if (!$start->in($this->getSize())) { - throw new OutOfBoundsException('Crop coordinates must start at minimum 0, 0 position from top left corner, crop height and width must be positive integers and must not exceed the current image borders'); - } - - try { - $this->imagick->cropImage($size->getWidth(), $size->getHeight(), $start->getX(), $start->getY()); - // Reset canvas for gif format - $this->imagick->setImagePage(0, 0, 0, 0); - } catch (\ImagickException $e) { - throw new RuntimeException('Crop operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function flipHorizontally() - { - try { - $this->imagick->flopImage(); - } catch (\ImagickException $e) { - throw new RuntimeException('Horizontal Flip operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function flipVertically() - { - try { - $this->imagick->flipImage(); - } catch (\ImagickException $e) { - throw new RuntimeException('Vertical flip operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function strip() - { - try { - try { - $this->profile($this->palette->profile()); - } catch (\Exception $e) { - // here we discard setting the profile as the previous incorporated profile - // is corrupted, let's now strip the image - } - $this->imagick->stripImage(); - } catch (\ImagickException $e) { - throw new RuntimeException('Strip operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function paste(ImageInterface $image, PointInterface $start) - { - if (!$image instanceof self) { - throw new InvalidArgumentException(sprintf('Imagick\Image can only paste() Imagick\Image instances, %s given', get_class($image))); - } - - if (!$this->getSize()->contains($image->getSize(), $start)) { - throw new OutOfBoundsException('Cannot paste image of the given size at the specified position, as it moves outside of the current image\'s box'); - } - - try { - $this->imagick->compositeImage($image->imagick, \Imagick::COMPOSITE_DEFAULT, $start->getX(), $start->getY()); - } catch (\ImagickException $e) { - throw new RuntimeException('Paste operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED) - { - try { - $this->imagick->resizeImage($size->getWidth(), $size->getHeight(), $this->getFilter($filter), 1); - } catch (\ImagickException $e) { - throw new RuntimeException('Resize operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function rotate($angle, ColorInterface $background = null) - { - $color = $background ? $background : $this->palette->color('fff'); - - try { - $pixel = $this->getColor($color); - - $this->imagick->rotateimage($pixel, $angle); - - $pixel->clear(); - $pixel->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Rotate operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function save($path = null, array $options = array()) - { - $path = null === $path ? $this->imagick->getImageFilename() : $path; - if (null === $path) { - throw new RuntimeException('You can omit save path only if image has been open from a file'); - } - - try { - $this->prepareOutput($options, $path); - $this->imagick->writeImages($path, true); - } catch (\ImagickException $e) { - throw new RuntimeException('Save operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function show($format, array $options = array()) - { - header('Content-type: '.$this->getMimeType($format)); - echo $this->get($format, $options); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function get($format, array $options = array()) - { - try { - $options['format'] = $format; - $this->prepareOutput($options); - } catch (\ImagickException $e) { - throw new RuntimeException('Get operation failed', $e->getCode(), $e); - } - - return $this->imagick->getImagesBlob(); - } - - /** - * {@inheritdoc} - */ - public function interlace($scheme) - { - static $supportedInterlaceSchemes = array( - ImageInterface::INTERLACE_NONE => \Imagick::INTERLACE_NO, - ImageInterface::INTERLACE_LINE => \Imagick::INTERLACE_LINE, - ImageInterface::INTERLACE_PLANE => \Imagick::INTERLACE_PLANE, - ImageInterface::INTERLACE_PARTITION => \Imagick::INTERLACE_PARTITION, - ); - - if (!array_key_exists($scheme, $supportedInterlaceSchemes)) { - throw new InvalidArgumentException('Unsupported interlace type'); - } - - $this->imagick->setInterlaceScheme($supportedInterlaceSchemes[$scheme]); - - return $this; - } - - /** - * @param array $options - * @param string $path - */ - private function prepareOutput(array $options, $path = null) - { - if (isset($options['format'])) { - $this->imagick->setImageFormat($options['format']); - } - - if (isset($options['animated']) && true === $options['animated']) { - $format = isset($options['format']) ? $options['format'] : 'gif'; - $delay = isset($options['animated.delay']) ? $options['animated.delay'] : null; - $loops = isset($options['animated.loops']) ? $options['animated.loops'] : 0; - - $options['flatten'] = false; - - $this->layers->animate($format, $delay, $loops); - } else { - $this->layers->merge(); - } - $this->applyImageOptions($this->imagick, $options, $path); - - // flatten only if image has multiple layers - if ((!isset($options['flatten']) || $options['flatten'] === true) && count($this->layers) > 1) { - $this->flatten(); - } - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return $this->get('png'); - } - - /** - * {@inheritdoc} - */ - public function draw() - { - return new Drawer($this->imagick); - } - - /** - * {@inheritdoc} - */ - public function effects() - { - return new Effects($this->imagick); - } - - /** - * {@inheritdoc} - */ - public function getSize() - { - try { - $i = $this->imagick->getIteratorIndex(); - $this->imagick->rewind(); - $width = $this->imagick->getImageWidth(); - $height = $this->imagick->getImageHeight(); - $this->imagick->setIteratorIndex($i); - } catch (\ImagickException $e) { - throw new RuntimeException('Could not get size', $e->getCode(), $e); - } - - return new Box($width, $height); - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function applyMask(ImageInterface $mask) - { - if (!$mask instanceof self) { - throw new InvalidArgumentException('Can only apply instances of Imagine\Imagick\Image as masks'); - } - - $size = $this->getSize(); - $maskSize = $mask->getSize(); - - if ($size != $maskSize) { - throw new InvalidArgumentException(sprintf('The given mask doesn\'t match current image\'s size, Current mask\'s dimensions are %s, while image\'s dimensions are %s', $maskSize, $size)); - } - - $mask = $mask->mask(); - $mask->imagick->negateImage(true); - - try { - // remove transparent areas of the original from the mask - $mask->imagick->compositeImage($this->imagick, \Imagick::COMPOSITE_DSTIN, 0, 0); - $this->imagick->compositeImage($mask->imagick, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); - - $mask->imagick->clear(); - $mask->imagick->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException('Apply mask operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function mask() - { - $mask = $this->copy(); - - try { - $mask->imagick->modulateImage(100, 0, 100); - $mask->imagick->setImageMatte(false); - } catch (\ImagickException $e) { - throw new RuntimeException('Mask operation failed', $e->getCode(), $e); - } - - return $mask; - } - - /** - * {@inheritdoc} - * - * @return ImageInterface - */ - public function fill(FillInterface $fill) - { - try { - if ($this->isLinearOpaque($fill)) { - $this->applyFastLinear($fill); - } else { - $iterator = $this->imagick->getPixelIterator(); - - foreach ($iterator as $y => $pixels) { - foreach ($pixels as $x => $pixel) { - $color = $fill->getColor(new Point($x, $y)); - - $pixel->setColor((string) $color); - $pixel->setColorValue(\Imagick::COLOR_ALPHA, $color->getAlpha() / 100); - } - - $iterator->syncIterator(); - } - } - } catch (\ImagickException $e) { - throw new RuntimeException('Fill operation failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function histogram() - { - try { - $pixels = $this->imagick->getImageHistogram(); - } catch (\ImagickException $e) { - throw new RuntimeException('Error while fetching histogram', $e->getCode(), $e); - } - - $image = $this; - - return array_map(function (\ImagickPixel $pixel) use ($image) { - return $image->pixelToColor($pixel); - },$pixels); - } - - /** - * {@inheritdoc} - */ - public function getColorAt(PointInterface $point) - { - if (!$point->in($this->getSize())) { - throw new RuntimeException(sprintf('Error getting color at point [%s,%s]. The point must be inside the image of size [%s,%s]', $point->getX(), $point->getY(), $this->getSize()->getWidth(), $this->getSize()->getHeight())); - } - - try { - $pixel = $this->imagick->getImagePixelColor($point->getX(), $point->getY()); - } catch (\ImagickException $e) { - throw new RuntimeException('Error while getting image pixel color', $e->getCode(), $e); - } - - return $this->pixelToColor($pixel); - } - - /** - * Returns a color given a pixel, depending the Palette context - * - * Note : this method is public for PHP 5.3 compatibility - * - * @param \ImagickPixel $pixel - * - * @return ColorInterface - * - * @throws InvalidArgumentException In case a unknown color is requested - */ - public function pixelToColor(\ImagickPixel $pixel) - { - static $colorMapping = array( - ColorInterface::COLOR_RED => \Imagick::COLOR_RED, - ColorInterface::COLOR_GREEN => \Imagick::COLOR_GREEN, - ColorInterface::COLOR_BLUE => \Imagick::COLOR_BLUE, - ColorInterface::COLOR_CYAN => \Imagick::COLOR_CYAN, - ColorInterface::COLOR_MAGENTA => \Imagick::COLOR_MAGENTA, - ColorInterface::COLOR_YELLOW => \Imagick::COLOR_YELLOW, - ColorInterface::COLOR_KEYLINE => \Imagick::COLOR_BLACK, - // There is no gray component in \Imagick, let's use one of the RGB comp - ColorInterface::COLOR_GRAY => \Imagick::COLOR_RED, - ); - - $alpha = $this->palette->supportsAlpha() ? (int) round($pixel->getColorValue(\Imagick::COLOR_ALPHA) * 100) : null; - $palette = $this->palette(); - - return $this->palette->color(array_map(function ($color) use ($palette, $pixel, $colorMapping) { - if (!isset($colorMapping[$color])) { - throw new InvalidArgumentException(sprintf('Color %s is not mapped in Imagick', $color)); - } - $multiplier = 255; - if ($palette->name() === PaletteInterface::PALETTE_CMYK) { - $multiplier = 100; - } - - return $pixel->getColorValue($colorMapping[$color]) * $multiplier; - }, $this->palette->pixelDefinition()), $alpha); - } - - /** - * {@inheritdoc} - */ - public function layers() - { - return $this->layers; - } - - /** - * {@inheritdoc} - */ - public function usePalette(PaletteInterface $palette) - { - if (!isset(static::$colorspaceMapping[$palette->name()])) { - throw new InvalidArgumentException(sprintf('The palette %s is not supported by Imagick driver', $palette->name())); - } - - if ($this->palette->name() === $palette->name()) { - return $this; - } - - if (!static::$supportsColorspaceConversion) { - throw new RuntimeException('Your version of Imagick does not support colorspace conversions.'); - } - - try { - try { - $hasICCProfile = (Boolean) $this->imagick->getImageProfile('icc'); - } catch (\ImagickException $e) { - $hasICCProfile = false; - } - - if (!$hasICCProfile) { - $this->profile($this->palette->profile()); - } - - $this->profile($palette->profile()); - $this->setColorspace($palette); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to set colorspace', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function palette() - { - return $this->palette; - } - - /** - * {@inheritdoc} - */ - public function profile(ProfileInterface $profile) - { - try { - $this->imagick->profileImage('icc', $profile->data()); - } catch (\ImagickException $e) { - throw new RuntimeException(sprintf('Unable to add profile %s to image', $profile->name()), $e->getCode(), $e); - } - - return $this; - } - - /** - * Internal - * - * Flatten the image. - */ - private function flatten() - { - /** - * @see https://github.com/mkoppanen/imagick/issues/45 - */ - try { - if (method_exists($this->imagick, 'mergeImageLayers') && defined('Imagick::LAYERMETHOD_UNDEFINED')) { - $this->imagick = $this->imagick->mergeImageLayers(\Imagick::LAYERMETHOD_UNDEFINED); - } elseif (method_exists($this->imagick, 'flattenImages')) { - $this->imagick = $this->imagick->flattenImages(); - } - } catch (\ImagickException $e) { - throw new RuntimeException('Flatten operation failed', $e->getCode(), $e); - } - } - - /** - * Internal - * - * Applies options before save or output - * - * @param \Imagick $image - * @param array $options - * @param string $path - * - * @throws InvalidArgumentException - * @throws RuntimeException - */ - private function applyImageOptions(\Imagick $image, array $options, $path) - { - if (isset($options['format'])) { - $format = $options['format']; - } elseif ('' !== $extension = pathinfo($path, \PATHINFO_EXTENSION)) { - $format = $extension; - } else { - $format = pathinfo($image->getImageFilename(), \PATHINFO_EXTENSION); - } - - $format = strtolower($format); - - $options = $this->updateSaveOptions($options); - - if (isset($options['jpeg_quality']) && in_array($format, array('jpeg', 'jpg', 'pjpeg'))) { - $image->setImageCompressionQuality($options['jpeg_quality']); - } - - if ((isset($options['png_compression_level']) || isset($options['png_compression_filter'])) && $format === 'png') { - // first digit: compression level (default: 7) - if (isset($options['png_compression_level'])) { - if ($options['png_compression_level'] < 0 || $options['png_compression_level'] > 9) { - throw new InvalidArgumentException('png_compression_level option should be an integer from 0 to 9'); - } - $compression = $options['png_compression_level'] * 10; - } else { - $compression = 70; - } - - // second digit: compression filter (default: 5) - if (isset($options['png_compression_filter'])) { - if ($options['png_compression_filter'] < 0 || $options['png_compression_filter'] > 9) { - throw new InvalidArgumentException('png_compression_filter option should be an integer from 0 to 9'); - } - $compression += $options['png_compression_filter']; - } else { - $compression += 5; - } - - $image->setImageCompressionQuality($compression); - } - - if (isset($options['resolution-units']) && isset($options['resolution-x']) && isset($options['resolution-y'])) { - if ($options['resolution-units'] == ImageInterface::RESOLUTION_PIXELSPERCENTIMETER) { - $image->setImageUnits(\Imagick::RESOLUTION_PIXELSPERCENTIMETER); - } elseif ($options['resolution-units'] == ImageInterface::RESOLUTION_PIXELSPERINCH) { - $image->setImageUnits(\Imagick::RESOLUTION_PIXELSPERINCH); - } else { - throw new RuntimeException('Unsupported image unit format'); - } - - $filter = ImageInterface::FILTER_UNDEFINED; - if (!empty($options['resampling-filter'])) { - $filter = $options['resampling-filter']; - } - - $image->setImageResolution($options['resolution-x'], $options['resolution-y']); - $image->resampleImage($options['resolution-x'], $options['resolution-y'], $this->getFilter($filter), 0); - } - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param ColorInterface $color - * - * @return \ImagickPixel - */ - private function getColor(ColorInterface $color) - { - $pixel = new \ImagickPixel((string) $color); - $pixel->setColorValue(\Imagick::COLOR_ALPHA, $color->getAlpha() / 100); - - return $pixel; - } - - /** - * Checks whether given $fill is linear and opaque - * - * @param FillInterface $fill - * - * @return Boolean - */ - private function isLinearOpaque(FillInterface $fill) - { - return $fill instanceof Linear && $fill->getStart()->isOpaque() && $fill->getEnd()->isOpaque(); - } - - /** - * Performs optimized gradient fill for non-opaque linear gradients - * - * @param Linear $fill - */ - private function applyFastLinear(Linear $fill) - { - $gradient = new \Imagick(); - $size = $this->getSize(); - $color = sprintf('gradient:%s-%s', (string) $fill->getStart(), (string) $fill->getEnd()); - - if ($fill instanceof Horizontal) { - $gradient->newPseudoImage($size->getHeight(), $size->getWidth(), $color); - $gradient->rotateImage(new \ImagickPixel(), 90); - } else { - $gradient->newPseudoImage($size->getWidth(), $size->getHeight(), $color); - } - - $this->imagick->compositeImage($gradient, \Imagick::COMPOSITE_OVER, 0, 0); - $gradient->clear(); - $gradient->destroy(); - } - - /** - * Internal - * - * Get the mime type based on format. - * - * @param string $format - * - * @return string mime-type - * - * @throws RuntimeException - */ - private function getMimeType($format) - { - static $mimeTypes = array( - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'png' => 'image/png', - 'wbmp' => 'image/vnd.wap.wbmp', - 'xbm' => 'image/xbm', - ); - - if (!isset($mimeTypes[$format])) { - throw new RuntimeException(sprintf('Unsupported format given. Only %s are supported, %s given', implode(", ", array_keys($mimeTypes)), $format)); - } - - return $mimeTypes[$format]; - } - - /** - * Sets colorspace and image type, assigns the palette. - * - * @param PaletteInterface $palette - * - * @throws InvalidArgumentException - */ - private function setColorspace(PaletteInterface $palette) - { - static $typeMapping = array( - // We use Matte variants to preserve alpha - PaletteInterface::PALETTE_CMYK => \Imagick::IMGTYPE_TRUECOLORMATTE, - PaletteInterface::PALETTE_RGB => \Imagick::IMGTYPE_TRUECOLORMATTE, - PaletteInterface::PALETTE_GRAYSCALE => \Imagick::IMGTYPE_GRAYSCALEMATTE, - ); - - if (!isset(static::$colorspaceMapping[$palette->name()])) { - throw new InvalidArgumentException(sprintf('The palette %s is not supported by Imagick driver', $palette->name())); - } - - $this->imagick->setType($typeMapping[$palette->name()]); - $this->imagick->setColorspace(static::$colorspaceMapping[$palette->name()]); - $this->palette = $palette; - } - - /** - * Older imagemagick versions does not support colorspace conversions. - * Let's detect if it is supported. - * - * @return Boolean - */ - private function detectColorspaceConversionSupport() - { - if (null !== static::$supportsColorspaceConversion) { - return static::$supportsColorspaceConversion; - } - - return static::$supportsColorspaceConversion = method_exists('Imagick', 'setColorspace'); - } - - /** - * Returns the filter if it's supported. - * - * @param string $filter - * - * @return string - * - * @throws InvalidArgumentException If the filter is unsupported. - */ - private function getFilter($filter = ImageInterface::FILTER_UNDEFINED) - { - static $supportedFilters = array( - ImageInterface::FILTER_UNDEFINED => \Imagick::FILTER_UNDEFINED, - ImageInterface::FILTER_BESSEL => \Imagick::FILTER_BESSEL, - ImageInterface::FILTER_BLACKMAN => \Imagick::FILTER_BLACKMAN, - ImageInterface::FILTER_BOX => \Imagick::FILTER_BOX, - ImageInterface::FILTER_CATROM => \Imagick::FILTER_CATROM, - ImageInterface::FILTER_CUBIC => \Imagick::FILTER_CUBIC, - ImageInterface::FILTER_GAUSSIAN => \Imagick::FILTER_GAUSSIAN, - ImageInterface::FILTER_HANNING => \Imagick::FILTER_HANNING, - ImageInterface::FILTER_HAMMING => \Imagick::FILTER_HAMMING, - ImageInterface::FILTER_HERMITE => \Imagick::FILTER_HERMITE, - ImageInterface::FILTER_LANCZOS => \Imagick::FILTER_LANCZOS, - ImageInterface::FILTER_MITCHELL => \Imagick::FILTER_MITCHELL, - ImageInterface::FILTER_POINT => \Imagick::FILTER_POINT, - ImageInterface::FILTER_QUADRATIC => \Imagick::FILTER_QUADRATIC, - ImageInterface::FILTER_SINC => \Imagick::FILTER_SINC, - ImageInterface::FILTER_TRIANGLE => \Imagick::FILTER_TRIANGLE - ); - - if (!array_key_exists($filter, $supportedFilters)) { - throw new InvalidArgumentException(sprintf( - 'The resampling filter "%s" is not supported by Imagick driver.', - $filter - )); - } - - return $supportedFilters[$filter]; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Imagine.php b/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Imagine.php deleted file mode 100644 index aa6e92646..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Imagine.php +++ /dev/null @@ -1,176 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Exception\NotSupportedException; -use Imagine\Image\AbstractImagine; -use Imagine\Image\BoxInterface; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Image\Palette\Color\ColorInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\Palette\CMYK; -use Imagine\Image\Palette\RGB; -use Imagine\Image\Palette\Grayscale; - -/** - * Imagine implementation using the Imagick PHP extension - */ -final class Imagine extends AbstractImagine -{ - /** - * @throws RuntimeException - */ - public function __construct() - { - if (!class_exists('Imagick')) { - throw new RuntimeException('Imagick not installed'); - } - - if (version_compare('6.2.9', $this->getVersion(new \Imagick())) > 0) { - throw new RuntimeException('ImageMagick version 6.2.9 or higher is required'); - } - } - - /** - * {@inheritdoc} - */ - public function open($path) - { - $path = $this->checkPath($path); - - try { - $imagick = new \Imagick($path); - $image = new Image($imagick, $this->createPalette($imagick), $this->getMetadataReader()->readFile($path)); - } catch (\Exception $e) { - throw new RuntimeException(sprintf('Unable to open image %s', $path), $e->getCode(), $e); - } - - return $image; - } - - /** - * {@inheritdoc} - */ - public function create(BoxInterface $size, ColorInterface $color = null) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $palette = null !== $color ? $color->getPalette() : new RGB(); - $color = null !== $color ? $color : $palette->color('fff'); - - try { - $pixel = new \ImagickPixel((string) $color); - $pixel->setColorValue(\Imagick::COLOR_ALPHA, $color->getAlpha() / 100); - - $imagick = new \Imagick(); - $imagick->newImage($width, $height, $pixel); - $imagick->setImageMatte(true); - $imagick->setImageBackgroundColor($pixel); - - if (version_compare('6.3.1', $this->getVersion($imagick)) < 0) { - $imagick->setImageOpacity($pixel->getColorValue(\Imagick::COLOR_ALPHA)); - } - - $pixel->clear(); - $pixel->destroy(); - - return new Image($imagick, $palette, new MetadataBag()); - } catch (\ImagickException $e) { - throw new RuntimeException('Could not create empty image', $e->getCode(), $e); - } - } - - /** - * {@inheritdoc} - */ - public function load($string) - { - try { - $imagick = new \Imagick(); - - $imagick->readImageBlob($string); - $imagick->setImageMatte(true); - - return new Image($imagick, $this->createPalette($imagick), $this->getMetadataReader()->readData($string)); - } catch (\ImagickException $e) { - throw new RuntimeException('Could not load image from string', $e->getCode(), $e); - } - } - - /** - * {@inheritdoc} - */ - public function read($resource) - { - if (!is_resource($resource)) { - throw new InvalidArgumentException('Variable does not contain a stream resource'); - } - - try { - $imagick = new \Imagick(); - $imagick->readImageFile($resource); - } catch (\ImagickException $e) { - throw new RuntimeException('Could not read image from resource', $e->getCode(), $e); - } - - return new Image($imagick, $this->createPalette($imagick), $this->getMetadataReader()->readStream($resource)); - } - - /** - * {@inheritdoc} - */ - public function font($file, $size, ColorInterface $color) - { - return new Font(new \Imagick(), $file, $size, $color); - } - - /** - * Returns the palette corresponding to an \Imagick resource colorspace - * - * @param \Imagick $imagick - * - * @return CMYK|Grayscale|RGB - * - * @throws NotSupportedException - */ - private function createPalette(\Imagick $imagick) - { - switch ($imagick->getImageColorspace()) { - case \Imagick::COLORSPACE_RGB: - case \Imagick::COLORSPACE_SRGB: - return new RGB(); - case \Imagick::COLORSPACE_CMYK: - return new CMYK(); - case \Imagick::COLORSPACE_GRAY: - return new Grayscale(); - default: - throw new NotSupportedException('Only RGB and CMYK colorspace are currently supported'); - } - } - - /** - * Returns ImageMagick version - * - * @param \Imagick $imagick - * - * @return string - */ - private function getVersion(\Imagick $imagick) - { - $v = $imagick->getVersion(); - list($version) = sscanf($v['versionString'], 'ImageMagick %s %04d-%02d-%02d %s %s'); - - return $version; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Layers.php b/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Layers.php deleted file mode 100644 index 7809447ad..000000000 --- a/snappymail/v/0.0.0/app/libraries/Imagine/Imagick/Layers.php +++ /dev/null @@ -1,271 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Image\AbstractLayers; -use Imagine\Image\Metadata\MetadataBag; -use Imagine\Exception\RuntimeException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Image\Palette\PaletteInterface; - -class Layers extends AbstractLayers -{ - /** - * @var Image - */ - private $image; - /** - * @var \Imagick - */ - private $resource; - /** - * @var integer - */ - private $offset = 0; - /** - * @var array - */ - private $layers = array(); - - private $palette; - - public function __construct(Image $image, PaletteInterface $palette, \Imagick $resource) - { - $this->image = $image; - $this->resource = $resource; - $this->palette = $palette; - } - - /** - * {@inheritdoc} - */ - public function merge() - { - foreach ($this->layers as $offset => $image) { - try { - $this->resource->setIteratorIndex($offset); - $this->resource->setImage($image->getImagick()); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to substitute layer', $e->getCode(), $e); - } - } - } - - /** - * {@inheritdoc} - */ - public function animate($format, $delay, $loops) - { - if ('gif' !== strtolower($format)) { - throw new InvalidArgumentException('Animated picture is currently only supported on gif'); - } - - if (!is_int($loops) || $loops < 0) { - throw new InvalidArgumentException('Loops must be a positive integer.'); - } - - if (null !== $delay && (!is_int($delay) || $delay < 0)) { - throw new InvalidArgumentException('Delay must be either null or a positive integer.'); - } - - try { - foreach ($this as $offset => $layer) { - $this->resource->setIteratorIndex($offset); - $this->resource->setFormat($format); - - if (null !== $delay) { - $layer->getImagick()->setImageDelay($delay / 10); - $layer->getImagick()->setImageTicksPerSecond(100); - } - $layer->getImagick()->setImageIterations($loops); - - $this->resource->setImage($layer->getImagick()); - } - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to animate layers', $e->getCode(), $e); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function coalesce() - { - try { - $coalescedResource = $this->resource->coalesceImages(); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to coalesce layers', $e->getCode(), $e); - } - - $count = $coalescedResource->getNumberImages(); - for ($offset = 0; $offset < $count; $offset++) { - try { - $coalescedResource->setIteratorIndex($offset); - $this->layers[$offset] = new Image($coalescedResource->getImage(), $this->palette, new MetadataBag()); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to retrieve layer', $e->getCode(), $e); - } - } - } - - /** - * {@inheritdoc} - */ - public function current() - { - return $this->extractAt($this->offset); - } - - /** - * Tries to extract layer at given offset - * - * @param integer $offset - * - * @return Image - * @throws RuntimeException - */ - private function extractAt($offset) - { - if (!isset($this->layers[$offset])) { - try { - $this->resource->setIteratorIndex($offset); - $this->layers[$offset] = new Image($this->resource->getImage(), $this->palette, new MetadataBag()); - } catch (\ImagickException $e) { - throw new RuntimeException(sprintf('Failed to extract layer %d', $offset), $e->getCode(), $e); - } - } - - return $this->layers[$offset]; - } - - /** - * {@inheritdoc} - */ - public function key() - { - return $this->offset; - } - - /** - * {@inheritdoc} - */ - public function next() - { - ++$this->offset; - } - - /** - * {@inheritdoc} - */ - public function rewind() - { - $this->offset = 0; - } - - /** - * {@inheritdoc} - */ - public function valid() - { - return $this->offset < count($this); - } - - /** - * {@inheritdoc} - */ - public function count() - { - try { - return $this->resource->getNumberImages(); - } catch (\ImagickException $e) { - throw new RuntimeException('Failed to count the number of layers', $e->getCode(), $e); - } - } - - /** - * {@inheritdoc} - */ - public function offsetExists($offset) - { - return is_int($offset) && $offset >= 0 && $offset < count($this); - } - - /** - * {@inheritdoc} - */ - public function offsetGet($offset) - { - return $this->extractAt($offset); - } - - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $image) - { - if (!$image instanceof Image) { - throw new InvalidArgumentException('Only an Imagick Image can be used as layer'); - } - - if (null === $offset) { - $offset = count($this) - 1; - } else { - if (!is_int($offset)) { - throw new InvalidArgumentException('Invalid offset for layer, it must be an integer'); - } - - if (count($this) < $offset || 0 > $offset) { - throw new OutOfBoundsException(sprintf('Invalid offset for layer, it must be a value between 0 and %d, %d given', count($this), $offset)); - } - - if (isset($this[$offset])) { - unset($this[$offset]); - $offset = $offset - 1; - } - } - - $frame = $image->getImagick(); - - try { - if (count($this) > 0) { - $this->resource->setIteratorIndex($offset); - } - $this->resource->addImage($frame); - } catch (\ImagickException $e) { - throw new RuntimeException('Unable to set the layer', $e->getCode(), $e); - } - - $this->layers = array(); - } - - /** - * {@inheritdoc} - */ - public function offsetUnset($offset) - { - try { - $this->extractAt($offset); - } catch (RuntimeException $e) { - return; - } - - try { - $this->resource->setIteratorIndex($offset); - $this->resource->removeImage(); - } catch (\ImagickException $e) { - throw new RuntimeException('Unable to remove layer', $e->getCode(), $e); - } - } -} diff --git a/snappymail/v/0.0.0/app/libraries/PHPThumb/GD.php b/snappymail/v/0.0.0/app/libraries/PHPThumb/GD.php deleted file mode 100644 index 733fe476c..000000000 --- a/snappymail/v/0.0.0/app/libraries/PHPThumb/GD.php +++ /dev/null @@ -1,1417 +0,0 @@ - - * Copyright (c) 2009, Ian Selby/Gen X Design - * - * Author(s): Ian Selby - * - * Licensed under the MIT License - * Redistributions of files must retain the above copyright notice. - * - * @author Ian Selby - * @copyright Copyright (c) 2009 Gen X Design - * @link http://phpthumb.gxdlabs.com - * @license http://www.opensource.org/licenses/mit-license.php The MIT License - */ - -class GD extends PHPThumb -{ - /** - * The prior image (before manipulation) - * - * @var resource - */ - protected $oldImage; - - /** - * The working image (used during manipulation) - * - * @var resource - */ - protected $workingImage; - - /** - * The current dimensions of the image - * - * @var array - */ - protected $currentDimensions; - - /** - * The new, calculated dimensions of the image - * - * @var array - */ - protected $newDimensions; - - /** - * The options for this class - * - * This array contains various options that determine the behavior in - * various functions throughout the class. Functions note which specific - * option key / values are used in their documentation - * - * @var array - */ - protected $options; - - /** - * The maximum width an image can be after resizing (in pixels) - * - * @var int - */ - protected $maxWidth; - - /** - * The maximum height an image can be after resizing (in pixels) - * - * @var int - */ - protected $maxHeight; - - /** - * The percentage to resize the image by - * - * @var int - */ - protected $percent; - - /** - * @param string $fileName - * @param array $options - * @param array $plugins - */ - public function __construct($fileName, $options = array(), array $plugins = array()) - { - parent::__construct($fileName, $options, $plugins); - - $this->determineFormat(); - $this->verifyFormatCompatiblity(); - - switch ($this->format) { - case 'GIF': - $this->oldImage = @imagecreatefromgif($this->fileName); - break; - case 'JPG': - $this->oldImage = @imagecreatefromjpeg($this->fileName); - break; - case 'PNG': - $this->oldImage = @imagecreatefrompng($this->fileName); - break; - case 'STRING': - $this->oldImage = @imagecreatefromstring($this->fileName); - break; - } - - if (!is_resource($this->oldImage)) - { - throw new \Exception('Invalid image file'); - } - else - { - $this->currentDimensions = array ( - 'width' => imagesx($this->oldImage), - 'height' => imagesy($this->oldImage) - ); - } - } - - public function __destruct() - { - if (is_resource($this->oldImage)) { - imagedestroy($this->oldImage); - } - - if (is_resource($this->workingImage)) { - imagedestroy($this->workingImage); - } - } - - /** - * Pad an image to desired dimensions. Moves the image into the center and fills the rest with $color. - * @param $width - * @param $height - * @param array $color - * @return GD - */ - public function pad($width, $height, $color = array(255, 255, 255)) - { - // no resize - woohoo! - if ($width == $this->currentDimensions['width'] && $height == $this->currentDimensions['height']) { - return $this; - } - - // create the working image - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($width, $height); - } else { - $this->workingImage = imagecreate($width, $height); - } - - // create the fill color - $fillColor = imagecolorallocate( - $this->workingImage, - $color[0], - $color[1], - $color[2] - ); - - // fill our working image with the fill color - imagefill( - $this->workingImage, - 0, - 0, - $fillColor - ); - - // copy the image into the center of our working image - imagecopyresampled( - $this->workingImage, - $this->oldImage, - intval(($width-$this->currentDimensions['width']) / 2), - intval(($height-$this->currentDimensions['height']) / 2), - 0, - 0, - $this->currentDimensions['width'], - $this->currentDimensions['height'], - $this->currentDimensions['width'], - $this->currentDimensions['height'] - ); - - // update all the variables and resources to be correct - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $width; - $this->currentDimensions['height'] = $height; - - return $this; - } - - /** - * Resizes an image to be no larger than $maxWidth or $maxHeight - * - * If either param is set to zero, then that dimension will not be considered as a part of the resize. - * Additionally, if $this->options['resizeUp'] is set to true (false by default), then this function will - * also scale the image up to the maximum dimensions provided. - * - * @param int $maxWidth The maximum width of the image in pixels - * @param int $maxHeight The maximum height of the image in pixels - * @return \PHPThumb\GD - */ - public function resize($maxWidth = 0, $maxHeight = 0) - { - // make sure our arguments are valid - if (!is_numeric($maxWidth)) { - throw new \InvalidArgumentException('$maxWidth must be numeric'); - } - - if (!is_numeric($maxHeight)) { - throw new \InvalidArgumentException('$maxHeight must be numeric'); - } - - // make sure we're not exceeding our image size if we're not supposed to - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($maxHeight) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $maxHeight; - $this->maxWidth = (intval($maxWidth) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $maxWidth; - } else { - $this->maxHeight = intval($maxHeight); - $this->maxWidth = intval($maxWidth); - } - - // get the new dimensions... - $this->calcImageSize($this->currentDimensions['width'], $this->currentDimensions['height']); - - // create the working image - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - } else { - $this->workingImage = imagecreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - } - - $this->preserveAlpha(); - - // and create the newly sized image - imagecopyresampled( - $this->workingImage, - $this->oldImage, - 0, - 0, - 0, - 0, - $this->newDimensions['newWidth'], - $this->newDimensions['newHeight'], - $this->currentDimensions['width'], - $this->currentDimensions['height'] - ); - - // update all the variables and resources to be correct - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $this->newDimensions['newWidth']; - $this->currentDimensions['height'] = $this->newDimensions['newHeight']; - - return $this; - } - - /** - * Adaptively Resizes the Image - * - * This function attempts to get the image to as close to the provided dimensions as possible, and then crops the - * remaining overflow (from the center) to get the image to be the size specified - * - * @param int $maxWidth - * @param int $maxHeight - * @return \PHPThumb\GD - */ - public function adaptiveResize($width, $height) - { - // make sure our arguments are valid - if ((!is_numeric($width) || $width == 0) && (!is_numeric($height) || $height == 0)) { - throw new \InvalidArgumentException('$width and $height must be numeric and greater than zero'); - } - - if (!is_numeric($width) || $width == 0) { - $width = ($height * $this->currentDimensions['width']) / $this->currentDimensions['height']; - } - - if (!is_numeric($height) || $height == 0) { - $height = ($width * $this->currentDimensions['height']) / $this->currentDimensions['width']; - } - - // make sure we're not exceeding our image size if we're not supposed to - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height; - $this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width; - } else { - $this->maxHeight = intval($height); - $this->maxWidth = intval($width); - } - - $this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']); - - // resize the image to be close to our desired dimensions - $this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - - // reset the max dimensions... - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height; - $this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width; - } else { - $this->maxHeight = intval($height); - $this->maxWidth = intval($width); - } - - // create the working image - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight); - } else { - $this->workingImage = imagecreate($this->maxWidth, $this->maxHeight); - } - - $this->preserveAlpha(); - - $cropWidth = $this->maxWidth; - $cropHeight = $this->maxHeight; - $cropX = 0; - $cropY = 0; - - // now, figure out how to crop the rest of the image... - if ($this->currentDimensions['width'] > $this->maxWidth) { - $cropX = intval(($this->currentDimensions['width'] - $this->maxWidth) / 2); - } elseif ($this->currentDimensions['height'] > $this->maxHeight) { - $cropY = intval(($this->currentDimensions['height'] - $this->maxHeight) / 2); - } - - imagecopyresampled( - $this->workingImage, - $this->oldImage, - 0, - 0, - $cropX, - $cropY, - $cropWidth, - $cropHeight, - $cropWidth, - $cropHeight - ); - - // update all the variables and resources to be correct - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $this->maxWidth; - $this->currentDimensions['height'] = $this->maxHeight; - - return $this; - } - - /** - * Adaptively Resizes the Image and Crops Using a Percentage - * - * This function attempts to get the image to as close to the provided dimensions as possible, and then crops the - * remaining overflow using a provided percentage to get the image to be the size specified. - * - * The percentage mean different things depending on the orientation of the original image. - * - * For Landscape images: - * --------------------- - * - * A percentage of 1 would crop the image all the way to the left, which would be the same as - * using adaptiveResizeQuadrant() with $quadrant = 'L' - * - * A percentage of 50 would crop the image to the center which would be the same as using - * adaptiveResizeQuadrant() with $quadrant = 'C', or even the original adaptiveResize() - * - * A percentage of 100 would crop the image to the image all the way to the right, etc, etc. - * Note that you can use any percentage between 1 and 100. - * - * For Portrait images: - * -------------------- - * - * This works the same as for Landscape images except that a percentage of 1 means top and 100 means bottom - * - * @param int $maxWidth - * @param int $maxHeight - * @param int $percent - * @return \PHPThumb\GD - */ - public function adaptiveResizePercent($width, $height, $percent = 50) - { - // make sure our arguments are valid - if (!is_numeric($width) || $width == 0) { - throw new \InvalidArgumentException('$width must be numeric and greater than zero'); - } - - if (!is_numeric($height) || $height == 0) { - throw new \InvalidArgumentException('$height must be numeric and greater than zero'); - } - - // make sure we're not exceeding our image size if we're not supposed to - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height; - $this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width; - } else { - $this->maxHeight = intval($height); - $this->maxWidth = intval($width); - } - - $this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']); - - // resize the image to be close to our desired dimensions - $this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - - // reset the max dimensions... - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height; - $this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width; - } else { - $this->maxHeight = intval($height); - $this->maxWidth = intval($width); - } - - // create the working image - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight); - } else { - $this->workingImage = imagecreate($this->maxWidth, $this->maxHeight); - } - - $this->preserveAlpha(); - - $cropWidth = $this->maxWidth; - $cropHeight = $this->maxHeight; - $cropX = 0; - $cropY = 0; - - // Crop the rest of the image using the quadrant - - if ($percent > 100) { - $percent = 100; - } elseif ($percent < 1) { - $percent = 1; - } - - if ($this->currentDimensions['width'] > $this->maxWidth) { - // Image is landscape - $maxCropX = $this->currentDimensions['width'] - $this->maxWidth; - $cropX = intval(($percent / 100) * $maxCropX); - - } elseif ($this->currentDimensions['height'] > $this->maxHeight) { - // Image is portrait - $maxCropY = $this->currentDimensions['height'] - $this->maxHeight; - $cropY = intval(($percent / 100) * $maxCropY); - } - - imagecopyresampled( - $this->workingImage, - $this->oldImage, - 0, - 0, - $cropX, - $cropY, - $cropWidth, - $cropHeight, - $cropWidth, - $cropHeight - ); - - // update all the variables and resources to be correct - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $this->maxWidth; - $this->currentDimensions['height'] = $this->maxHeight; - - return $this; - } - - /** - * Adaptively Resizes the Image and Crops Using a Quadrant - * - * This function attempts to get the image to as close to the provided dimensions as possible, and then crops the - * remaining overflow using the quadrant to get the image to be the size specified. - * - * The quadrants available are Top, Bottom, Center, Left, and Right: - * - * - * +---+---+---+ - * | | T | | - * +---+---+---+ - * | L | C | R | - * +---+---+---+ - * | | B | | - * +---+---+---+ - * - * Note that if your image is Landscape and you choose either of the Top or Bottom quadrants (which won't - * make sence since only the Left and Right would be available, then the Center quadrant will be used - * to crop. This would have exactly the same result as using adaptiveResize(). - * The same goes if your image is portrait and you choose either the Left or Right quadrants. - * - * @param int $maxWidth - * @param int $maxHeight - * @param string $quadrant T, B, C, L, R - * @return \PHPThumb\GD - */ - public function adaptiveResizeQuadrant($width, $height, $quadrant = 'C') - { - // make sure our arguments are valid - if (!is_numeric($width) || $width == 0) { - throw new \InvalidArgumentException('$width must be numeric and greater than zero'); - } - - if (!is_numeric($height) || $height == 0) { - throw new \InvalidArgumentException('$height must be numeric and greater than zero'); - } - - // make sure we're not exceeding our image size if we're not supposed to - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height; - $this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width; - } else { - $this->maxHeight = intval($height); - $this->maxWidth = intval($width); - } - - $this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']); - - // resize the image to be close to our desired dimensions - $this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - - // reset the max dimensions... - if ($this->options['resizeUp'] === false) { - $this->maxHeight = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height; - $this->maxWidth = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width; - } else { - $this->maxHeight = intval($height); - $this->maxWidth = intval($width); - } - - // create the working image - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight); - } else { - $this->workingImage = imagecreate($this->maxWidth, $this->maxHeight); - } - - $this->preserveAlpha(); - - $cropWidth = $this->maxWidth; - $cropHeight = $this->maxHeight; - $cropX = 0; - $cropY = 0; - - // Crop the rest of the image using the quadrant - - if ($this->currentDimensions['width'] > $this->maxWidth) { - // Image is landscape - switch ($quadrant) { - case 'L': - $cropX = 0; - break; - case 'R': - $cropX = intval(($this->currentDimensions['width'] - $this->maxWidth)); - break; - case 'C': - default: - $cropX = intval(($this->currentDimensions['width'] - $this->maxWidth) / 2); - break; - } - } elseif ($this->currentDimensions['height'] > $this->maxHeight) { - // Image is portrait - switch ($quadrant) { - case 'T': - $cropY = 0; - break; - case 'B': - $cropY = intval(($this->currentDimensions['height'] - $this->maxHeight)); - break; - case 'C': - default: - $cropY = intval(($this->currentDimensions['height'] - $this->maxHeight) / 2); - break; - } - } - - imagecopyresampled( - $this->workingImage, - $this->oldImage, - 0, - 0, - $cropX, - $cropY, - $cropWidth, - $cropHeight, - $cropWidth, - $cropHeight - ); - - // update all the variables and resources to be correct - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $this->maxWidth; - $this->currentDimensions['height'] = $this->maxHeight; - - return $this; - } - - /** - * Resizes an image by a given percent uniformly, - * Percentage should be whole number representation (i.e. 1-100) - * - * @param int $percent - * @return GD - * @throws \InvalidArgumentException - */ - public function resizePercent($percent = 0) - { - if (!is_numeric($percent)) { - throw new \InvalidArgumentException ('$percent must be numeric'); - } - - $this->percent = intval($percent); - - $this->calcImageSizePercent($this->currentDimensions['width'], $this->currentDimensions['height']); - - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - } else { - $this->workingImage = imagecreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); - } - - $this->preserveAlpha(); - - imagecopyresampled( - $this->workingImage, - $this->oldImage, - 0, - 0, - 0, - 0, - $this->newDimensions['newWidth'], - $this->newDimensions['newHeight'], - $this->currentDimensions['width'], - $this->currentDimensions['height'] - ); - - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $this->newDimensions['newWidth']; - $this->currentDimensions['height'] = $this->newDimensions['newHeight']; - - return $this; - } - - /** - * Crops an image from the center with provided dimensions - * - * If no height is given, the width will be used as a height, thus creating a square crop - * - * @param int $cropWidth - * @param int $cropHeight - * @return \PHPThumb\GD - */ - public function cropFromCenter($cropWidth, $cropHeight = null) - { - if (!is_numeric($cropWidth)) { - throw new \InvalidArgumentException('$cropWidth must be numeric'); - } - - if ($cropHeight !== null && !is_numeric($cropHeight)) { - throw new \InvalidArgumentException('$cropHeight must be numeric'); - } - - if ($cropHeight === null) { - $cropHeight = $cropWidth; - } - - $cropWidth = ($this->currentDimensions['width'] < $cropWidth) ? $this->currentDimensions['width'] : $cropWidth; - $cropHeight = ($this->currentDimensions['height'] < $cropHeight) ? $this->currentDimensions['height'] : $cropHeight; - - $cropX = intval(($this->currentDimensions['width'] - $cropWidth) / 2); - $cropY = intval(($this->currentDimensions['height'] - $cropHeight) / 2); - - $this->crop($cropX, $cropY, $cropWidth, $cropHeight); - - return $this; - } - - /** - * Vanilla Cropping - Crops from x,y with specified width and height - * - * @param int $startX - * @param int $startY - * @param int $cropWidth - * @param int $cropHeight - * @return \PHPThumb\GD - */ - public function crop($startX, $startY, $cropWidth, $cropHeight) - { - // validate input - if (!is_numeric($startX)) { - throw new \InvalidArgumentException('$startX must be numeric'); - } - - if (!is_numeric($startY)) { - throw new \InvalidArgumentException('$startY must be numeric'); - } - - if (!is_numeric($cropWidth)) { - throw new \InvalidArgumentException('$cropWidth must be numeric'); - } - - if (!is_numeric($cropHeight)) { - throw new \InvalidArgumentException('$cropHeight must be numeric'); - } - - // do some calculations - $cropWidth = ($this->currentDimensions['width'] < $cropWidth) ? $this->currentDimensions['width'] : $cropWidth; - $cropHeight = ($this->currentDimensions['height'] < $cropHeight) ? $this->currentDimensions['height'] : $cropHeight; - - // ensure everything's in bounds - if (($startX + $cropWidth) > $this->currentDimensions['width']) { - $startX = ($this->currentDimensions['width'] - $cropWidth); - } - - if (($startY + $cropHeight) > $this->currentDimensions['height']) { - $startY = ($this->currentDimensions['height'] - $cropHeight); - } - - if ($startX < 0) { - $startX = 0; - } - - if ($startY < 0) { - $startY = 0; - } - - // create the working image - if (function_exists('imagecreatetruecolor')) { - $this->workingImage = imagecreatetruecolor($cropWidth, $cropHeight); - } else { - $this->workingImage = imagecreate($cropWidth, $cropHeight); - } - - $this->preserveAlpha(); - - imagecopyresampled( - $this->workingImage, - $this->oldImage, - 0, - 0, - $startX, - $startY, - $cropWidth, - $cropHeight, - $cropWidth, - $cropHeight - ); - - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $cropWidth; - $this->currentDimensions['height'] = $cropHeight; - - return $this; - } - - /** - * Rotates image either 90 degrees clockwise or counter-clockwise - * - * @param string $direction - * @retunrn \PHPThumb\GD - */ - public function rotateImage($direction = 'CW') - { - if ($direction == 'CW') { - $this->rotateImageNDegrees(90); - } else { - $this->rotateImageNDegrees(-90); - } - - return $this; - } - - /** - * Rotates image specified number of degrees - * - * @param int $degrees - * @return \PHPThumb\GD - */ - public function rotateImageNDegrees($degrees) - { - if (!is_numeric($degrees)) { - throw new \InvalidArgumentException('$degrees must be numeric'); - } - - if (!function_exists('imagerotate')) { - throw new \RuntimeException('Your version of GD does not support image rotation'); - } - - $this->workingImage = imagerotate($this->oldImage, $degrees, 0); - - $newWidth = $this->currentDimensions['height']; - $newHeight = $this->currentDimensions['width']; - $this->oldImage = $this->workingImage; - $this->currentDimensions['width'] = $newWidth; - $this->currentDimensions['height'] = $newHeight; - - return $this; - } - - /** - * Applies a filter to the image - * - * @param int $filter - * @return \PHPThumb\GD - */ - public function imageFilter($filter, $arg1 = false, $arg2 = false, $arg3 = false, $arg4 = false) - { - if (!is_numeric($filter)) { - throw new \InvalidArgumentException('$filter must be numeric'); - } - - if (!function_exists('imagefilter')) { - throw new \RuntimeException('Your version of GD does not support image filters'); - } - - $result = false; - if ($arg1 === false) { - $result = imagefilter($this->oldImage, $filter); - } elseif ($arg2 === false) { - $result = imagefilter($this->oldImage, $filter, $arg1); - } elseif ($arg3 === false) { - $result = imagefilter($this->oldImage, $filter, $arg1, $arg2); - } elseif ($arg4 === false) { - $result = imagefilter($this->oldImage, $filter, $arg1, $arg2, $arg3); - } else { - $result = imagefilter($this->oldImage, $filter, $arg1, $arg2, $arg3, $arg4); - } - - if (!$result) { - throw new \RuntimeException('GD imagefilter failed'); - } - - $this->workingImage = $this->oldImage; - - return $this; - } - - /** - * Shows an image - * - * This function will show the current image by first sending the appropriate header - * for the format, and then outputting the image data. If headers have already been sent, - * a runtime exception will be thrown - * - * @param bool $rawData Whether or not the raw image stream should be output - * @return \PHPThumb\GD - */ - public function show($rawData = false) - { - //Execute any plugins - if ($this->plugins) { - foreach ($this->plugins as $plugin) { - /* @var $plugin \PHPThumb\PluginInterface */ - $plugin->execute($this); - } - } - - if (headers_sent() && php_sapi_name() != 'cli') { - throw new \RuntimeException('Cannot show image, headers have already been sent'); - } - - // When the interlace option equals true or false call imageinterlace else leave it to default - if ($this->options['interlace'] === true) { - imageinterlace($this->oldImage, 1); - } elseif ($this->options['interlace'] === false) { - imageinterlace($this->oldImage, 0); - } - - switch ($this->format) { - case 'GIF': - if ($rawData === false) { - header('Content-type: image/gif'); - } - imagegif($this->oldImage); - break; - case 'JPG': - if ($rawData === false) { - header('Content-type: image/jpeg'); - } - imagejpeg($this->oldImage, null, $this->options['jpegQuality']); - break; - case 'PNG': - case 'STRING': - if ($rawData === false) { - header('Content-type: image/png'); - } - imagepng($this->oldImage); - break; - } - - return $this; - } - - /** - * Returns the Working Image as a String - * - * This function is useful for getting the raw image data as a string for storage in - * a database, or other similar things. - * - * @return string - */ - public function getImageAsString() - { - $data = null; - ob_start(); - $this->show(true); - $data = ob_get_contents(); - ob_end_clean(); - - return $data; - } - - /** - * Saves an image - * - * This function will make sure the target directory is writeable, and then save the image. - * - * If the target directory is not writeable, the function will try to correct the permissions (if allowed, this - * is set as an option ($this->options['correctPermissions']). If the target cannot be made writeable, then a - * \RuntimeException is thrown. - * - * @param string $fileName The full path and filename of the image to save - * @param string $format The format to save the image in (optional, must be one of [GIF,JPG,PNG] - * @return \PHPThumb\GD - */ - public function save($fileName, $format = null) - { - $validFormats = array('GIF', 'JPG', 'PNG'); - $format = ($format !== null) ? strtoupper($format) : $this->format; - - if (!in_array($format, $validFormats)) { - throw new \InvalidArgumentException("Invalid format type specified in save function: {$format}"); - } - - // make sure the directory is writeable - if (!is_writeable(dirname($fileName))) { - // try to correct the permissions - if ($this->options['correctPermissions'] === true) { - @chmod(dirname($fileName), 0777); - - // throw an exception if not writeable - if (!is_writeable(dirname($fileName))) { - throw new \RuntimeException("File is not writeable, and could not correct permissions: {$fileName}"); - } - } else { // throw an exception if not writeable - throw new \RuntimeException("File not writeable: {$fileName}"); - } - } - - // When the interlace option equals true or false call imageinterlace else leave it to default - if ($this->options['interlace'] === true) { - imageinterlace($this->oldImage, 1); - } elseif ($this->options['interlace'] === false) { - imageinterlace($this->oldImage, 0); - } - - switch ($format) { - case 'GIF': - imagegif($this->oldImage, $fileName); - break; - case 'JPG': - imagejpeg($this->oldImage, $fileName, $this->options['jpegQuality']); - break; - case 'PNG': - imagepng($this->oldImage, $fileName); - break; - } - - return $this; - } - - ################################# - # ----- GETTERS / SETTERS ----- # - ################################# - - /** - * Sets options for all operations. - * @param array $options - * @return GD - */ - public function setOptions(array $options = array()) - { - // we've yet to init the default options, so create them here - if (sizeof($this->options) == 0) { - $defaultOptions = array( - 'resizeUp' => false, - 'jpegQuality' => 100, - 'correctPermissions' => false, - 'preserveAlpha' => true, - 'alphaMaskColor' => array (255, 255, 255), - 'preserveTransparency' => true, - 'transparencyMaskColor' => array (0, 0, 0), - 'interlace' => null - ); - } else { // otherwise, let's use what we've got already - $defaultOptions = $this->options; - } - - $this->options = array_merge($defaultOptions, $options); - - return $this; - } - - /** - * Returns $currentDimensions. - * - * @see \PHPThumb\GD::$currentDimensions - */ - public function getCurrentDimensions() - { - return $this->currentDimensions; - } - - /** - * @param $currentDimensions - * @return GD - */ - public function setCurrentDimensions($currentDimensions) - { - $this->currentDimensions = $currentDimensions; - - return $this; - } - - /** - * @return int - */ - public function getMaxHeight() - { - return $this->maxHeight; - } - - /** - * @param $maxHeight - * @return GD - */ - public function setMaxHeight($maxHeight) - { - $this->maxHeight = $maxHeight; - - return $this; - } - - /** - * @return int - */ - public function getMaxWidth() - { - return $this->maxWidth; - } - - /** - * @param $maxWidth - * @return GD - */ - public function setMaxWidth($maxWidth) - { - $this->maxWidth = $maxWidth; - - return $this; - } - - /** - * Returns $newDimensions. - * - * @see \PHPThumb\GD::$newDimensions - */ - public function getNewDimensions() - { - return $this->newDimensions; - } - - /** - * Sets $newDimensions. - * - * @param object $newDimensions - * @see \PHPThumb\GD::$newDimensions - */ - public function setNewDimensions($newDimensions) - { - $this->newDimensions = $newDimensions; - - return $this; - } - - /** - * Returns $options. - * - * @see \PHPThumb\GD::$options - */ - public function getOptions() - { - return $this->options; - } - - /** - * Returns $percent. - * - * @see \PHPThumb\GD::$percent - */ - public function getPercent() - { - return $this->percent; - } - - /** - * Sets $percent. - * - * @param object $percent - * @see \PHPThumb\GD::$percent - */ - public function setPercent($percent) - { - $this->percent = $percent; - - return $this; - } - - /** - * Returns $oldImage. - * - * @see \PHPThumb\GD::$oldImage - */ - public function getOldImage() - { - return $this->oldImage; - } - - /** - * Sets $oldImage. - * - * @param object $oldImage - * @see \PHPThumb\GD::$oldImage - */ - public function setOldImage($oldImage) - { - $this->oldImage = $oldImage; - - return $this; - } - - /** - * Returns $workingImage. - * - * @see \PHPThumb\GD::$workingImage - */ - public function getWorkingImage() - { - return $this->workingImage; - } - - /** - * Sets $workingImage. - * - * @param object $workingImage - * @see \PHPThumb\GD::$workingImage - */ - public function setWorkingImage($workingImage) - { - $this->workingImage = $workingImage; - - return $this; - } - - - ################################# - # ----- UTILITY FUNCTIONS ----- # - ################################# - - /** - * Calculates a new width and height for the image based on $this->maxWidth and the provided dimensions - * - * @return array - * @param int $width - * @param int $height - */ - protected function calcWidth($width, $height) - { - $newWidthPercentage = (100 * $this->maxWidth) / $width; - $newHeight = ($height * $newWidthPercentage) / 100; - - return array( - 'newWidth' => intval($this->maxWidth), - 'newHeight' => intval($newHeight) - ); - } - - /** - * Calculates a new width and height for the image based on $this->maxWidth and the provided dimensions - * - * @return array - * @param int $width - * @param int $height - */ - protected function calcHeight($width, $height) - { - $newHeightPercentage = (100 * $this->maxHeight) / $height; - $newWidth = ($width * $newHeightPercentage) / 100; - - return array( - 'newWidth' => ceil($newWidth), - 'newHeight' => ceil($this->maxHeight) - ); - } - - /** - * Calculates a new width and height for the image based on $this->percent and the provided dimensions - * - * @return array - * @param int $width - * @param int $height - */ - protected function calcPercent($width, $height) - { - $newWidth = ($width * $this->percent) / 100; - $newHeight = ($height * $this->percent) / 100; - - return array( - 'newWidth' => ceil($newWidth), - 'newHeight' => ceil($newHeight) - ); - } - - /** - * Calculates the new image dimensions - * - * These calculations are based on both the provided dimensions and $this->maxWidth and $this->maxHeight - * - * @param int $width - * @param int $height - */ - protected function calcImageSize($width, $height) - { - $newSize = array( - 'newWidth' => $width, - 'newHeight' => $height - ); - - if ($this->maxWidth > 0) { - $newSize = $this->calcWidth($width, $height); - - if ($this->maxHeight > 0 && $newSize['newHeight'] > $this->maxHeight) { - $newSize = $this->calcHeight($newSize['newWidth'], $newSize['newHeight']); - } - } - - if ($this->maxHeight > 0) { - $newSize = $this->calcHeight($width, $height); - - if ($this->maxWidth > 0 && $newSize['newWidth'] > $this->maxWidth) { - $newSize = $this->calcWidth($newSize['newWidth'], $newSize['newHeight']); - } - } - - $this->newDimensions = $newSize; - } - - /** - * Calculates new image dimensions, not allowing the width and height to be less than either the max width or height - * - * @param int $width - * @param int $height - */ - protected function calcImageSizeStrict($width, $height) - { - // first, we need to determine what the longest resize dimension is.. - if ($this->maxWidth >= $this->maxHeight) { - // and determine the longest original dimension - if ($width > $height) { - $newDimensions = $this->calcHeight($width, $height); - - if ($newDimensions['newWidth'] < $this->maxWidth) { - $newDimensions = $this->calcWidth($width, $height); - } - } elseif ($height >= $width) { - $newDimensions = $this->calcWidth($width, $height); - - if ($newDimensions['newHeight'] < $this->maxHeight) { - $newDimensions = $this->calcHeight($width, $height); - } - } - } elseif ($this->maxHeight > $this->maxWidth) { - if ($width >= $height) { - $newDimensions = $this->calcWidth($width, $height); - - if ($newDimensions['newHeight'] < $this->maxHeight) { - $newDimensions = $this->calcHeight($width, $height); - } - } elseif ($height > $width) { - $newDimensions = $this->calcHeight($width, $height); - - if ($newDimensions['newWidth'] < $this->maxWidth) { - $newDimensions = $this->calcWidth($width, $height); - } - } - } - - $this->newDimensions = $newDimensions; - } - - /** - * Calculates new dimensions based on $this->percent and the provided dimensions - * - * @param int $width - * @param int $height - */ - protected function calcImageSizePercent($width, $height) - { - if ($this->percent > 0) { - $this->newDimensions = $this->calcPercent($width, $height); - } - } - - /** - * Determines the file format by mime-type - * - * This function will throw exceptions for invalid images / mime-types - * - */ - protected function determineFormat() - { - $formatInfo = getimagesize($this->fileName); - - // non-image files will return false - if ($formatInfo === false) { - if ($this->remoteImage) { - throw new \Exception("Could not determine format of remote image: {$this->fileName}"); - } else { - throw new \Exception("File is not a valid image: {$this->fileName}"); - } - } - - $mimeType = isset($formatInfo['mime']) ? $formatInfo['mime'] : null; - - switch ($mimeType) { - case 'image/gif': - $this->format = 'GIF'; - break; - case 'image/jpeg': - $this->format = 'JPG'; - break; - case 'image/png': - $this->format = 'PNG'; - break; - default: - throw new \Exception("Image format not supported: {$mimeType}"); - } - } - - /** - * Makes sure the correct GD implementation exists for the file type - * - */ - protected function verifyFormatCompatiblity() - { - $isCompatible = true; - $gdInfo = gd_info(); - - switch ($this->format) { - case 'GIF': - $isCompatible = isset($gdInfo['GIF Create Support']); - break; - case 'JPG': - $isCompatible = (isset($gdInfo['JPG Support']) || isset($gdInfo['JPEG Support'])) ? true : false; - break; - case 'PNG': - $isCompatible = isset($gdInfo[$this->format . ' Support']); - break; - default: - $isCompatible = false; - } - - if (!$isCompatible) { - // one last check for "JPEG" instead - $isCompatible = isset($gdInfo['JPEG Support']); - - if (!$isCompatible) { - throw new \Exception("Your GD installation does not support {$this->format} image types"); - } - } - } - - /** - * Preserves the alpha or transparency for PNG and GIF files - * - * Alpha / transparency will not be preserved if the appropriate options are set to false. - * Also, the GIF transparency is pretty skunky (the results aren't awesome), but it works like a - * champ... that's the nature of GIFs tho, so no huge surprise. - * - * This functionality was originally suggested by commenter Aimi (no links / site provided) - Thanks! :) - * - */ - protected function preserveAlpha() - { - if ($this->format == 'PNG' && $this->options['preserveAlpha'] === true) { - imagealphablending($this->workingImage, false); - - $colorTransparent = imagecolorallocatealpha( - $this->workingImage, - $this->options['alphaMaskColor'][0], - $this->options['alphaMaskColor'][1], - $this->options['alphaMaskColor'][2], - 0 - ); - - imagefill($this->workingImage, 0, 0, $colorTransparent); - imagesavealpha($this->workingImage, true); - } - // preserve transparency in GIFs... this is usually pretty rough tho - if ($this->format == 'GIF' && $this->options['preserveTransparency'] === true) { - $colorTransparent = imagecolorallocate( - $this->workingImage, - $this->options['transparencyMaskColor'][0], - $this->options['transparencyMaskColor'][1], - $this->options['transparencyMaskColor'][2] - ); - - imagecolortransparent($this->workingImage, $colorTransparent); - imagetruecolortopalette($this->workingImage, true, 256); - } - } -} diff --git a/snappymail/v/0.0.0/app/libraries/PHPThumb/PHPThumb.php b/snappymail/v/0.0.0/app/libraries/PHPThumb/PHPThumb.php deleted file mode 100644 index a86c8c91e..000000000 --- a/snappymail/v/0.0.0/app/libraries/PHPThumb/PHPThumb.php +++ /dev/null @@ -1,143 +0,0 @@ - - * Copyright (c) 2009, Ian Selby/Gen X Design - * - * Author(s): Ian Selby - * - * Licensed under the MIT License - * Redistributions of files must retain the above copyright notice. - * - * @author Ian Selby - * @copyright Copyright (c) 2009 Gen X Design - * @link http://phpthumb.gxdlabs.com - * @license http://www.opensource.org/licenses/mit-license.php The MIT License - */ - -abstract class PHPThumb -{ - /** - * The name of the file we're manipulating - * This must include the path to the file (absolute paths recommended) - * - * @var string - */ - protected $fileName; - - /** - * What the file format is (mime-type) - * - * @var string - */ - protected $format; - - /** - * Whether or not the image is hosted remotely - * - * @var bool - */ - protected $remoteImage; - - /** - * An array of attached plugins to execute in order. - * @var array - */ - protected $plugins; - - /** - * @param $fileName - * @param array $options - * @param array $plugins - */ - public function __construct($fileName, array $options = array(), array $plugins = array()) - { - $this->fileName = $fileName; - $this->remoteImage = false; - - if(!$this->validateRequestedResource($fileName)) { - throw new \InvalidArgumentException("Image file not found: {$fileName}"); - } - - $this->setOptions($options); - - $this->plugins = $plugins; - } - - abstract public function setOptions(array $options = array()); - - /** - * Check the provided filename/url. If it is a url, validate that it is properly - * formatted. If it is a file, check to make sure that it actually exists on - * the filesystem. - * - * @param $filename - * @return bool - */ - protected function validateRequestedResource($filename) - { - if(false !== filter_var($filename, FILTER_VALIDATE_URL)) { - $this->remoteImage = true; - return true; - } - - if (file_exists($filename)) { - return true; - } - - return false; - } - - /** - * Returns the filename. - * @return string - */ - public function getFileName() - { - return $this->fileName; - } - - /** - * Sets the filename. - * @param $fileName - * @return PHPThumb - */ - public function setFileName($fileName) - { - $this->fileName = $fileName; - - return $this; - } - - /** - * Returns the format. - * @return string - */ - public function getFormat() - { - return $this->format; - } - - /** - * Sets the format. - * @param $format - * @return PHPThumb - */ - public function setFormat($format) - { - $this->format = $format; - - return $this; - } - - /** - * Returns whether the image exists remotely, i.e. it was loaded via a URL. - * @return bool - */ - public function getIsRemoteImage() - { - return $this->remoteImage; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/PHPThumb/PluginInterface.php b/snappymail/v/0.0.0/app/libraries/PHPThumb/PluginInterface.php deleted file mode 100644 index 56df9768a..000000000 --- a/snappymail/v/0.0.0/app/libraries/PHPThumb/PluginInterface.php +++ /dev/null @@ -1,12 +0,0 @@ - - * Copyright (c) 2009, Ian Selby/Gen X Design - * - * Author(s): Ian Selby - * - * Licensed under the MIT License - * Redistributions of files must retain the above copyright notice. - * - * @author Ian Selby - * @copyright Copyright (c) 2009 Gen X Design - * @link http://phpthumb.gxdlabs.com - * @license http://www.opensource.org/licenses/mit-license.php The MIT License - * @version 3.0 - * @package PhpThumb - * @filesource - */ - -/** - * GD Reflection Lib Plugin - * - * This plugin allows you to create those fun Apple(tm)-style reflections in your images - * - * @package PhpThumb - * @subpackage Plugins - */ -class Reflection implements \PHPThumb\PluginInterface -{ - protected $currentDimensions; - protected $workingImage; - protected $newImage; - protected $options; - - protected $percent; - protected $reflection; - protected $white; - protected $border; - protected $borderColor; - - public function __construct($percent, $reflection, $white, $border, $borderColor) - { - $this->percent = $percent; - $this->reflection = $reflection; - $this->white = $white; - $this->border = $border; - $this->borderColor = $borderColor; - } - - /** - * @param \PHPThumb\PHPThumb $phpthumb - * @return \PHPThumb\PHPThumb - */ - public function execute($phpthumb) - { - $this->currentDimensions = $phpthumb->getCurrentDimensions(); - $this->workingImage = $phpthumb->getWorkingImage(); - $this->newImage = $phpthumb->getOldImage(); - $this->options = $phpthumb->getOptions(); - - $width = $this->currentDimensions['width']; - $height = $this->currentDimensions['height']; - $this->reflectionHeight = intval($height * ($this->reflection / 100)); - $newHeight = $height + $this->reflectionHeight; - $reflectedPart = $height * ($this->percent / 100); - - $this->workingImage = imagecreatetruecolor($width, $newHeight); - - imagealphablending($this->workingImage, true); - - $colorToPaint = imagecolorallocatealpha( - $this->workingImage, - 255, - 255, - 255, - 0 - ); - - imagefilledrectangle( - $this->workingImage, - 0, - 0, - $width, - $newHeight, - $colorToPaint - ); - - imagecopyresampled( - $this->workingImage, - $this->newImage, - 0, - 0, - 0, - $reflectedPart, - $width, - $this->reflectionHeight, - $width, - ($height - $reflectedPart) - ); - - $this->imageFlipVertical(); - - imagecopy( - $this->workingImage, - $this->newImage, - 0, - 0, - 0, - 0, - $width, - $height - ); - - imagealphablending($this->workingImage, true); - - for ($i = 0; $i < $this->reflectionHeight; $i++) { - $colorToPaint = imagecolorallocatealpha( - $this->workingImage, - 255, - 255, - 255, - ($i / $this->reflectionHeight * -1 + 1) * $this->white - ); - - imagefilledrectangle( - $this->workingImage, - 0, - $height + $i, - $width, - $height + $i, - $colorToPaint - ); - } - - if ($this->border == true) { - $rgb = $this->hex2rgb($this->borderColor, false); - $colorToPaint = imagecolorallocate($this->workingImage, $rgb[0], $rgb[1], $rgb[2]); - - //top line - imageline( - $this->workingImage, - 0, - 0, - $width, - 0, - $colorToPaint - ); - - //bottom line - imageline( - $this->workingImage, - 0, - $height, - $width, - $height, - $colorToPaint - ); - - //left line - imageline( - $this->workingImage, - 0, - 0, - 0, - $height, - $colorToPaint - ); - - //right line - imageline( - $this->workingImage, - $width - 1, - 0, - $width - 1, - $height, - $colorToPaint - ); - } - - if ($phpthumb->getFormat() == 'PNG') { - $colorTransparent = imagecolorallocatealpha( - $this->workingImage, - $this->options['alphaMaskColor'][0], - $this->options['alphaMaskColor'][1], - $this->options['alphaMaskColor'][2], - 0 - ); - - imagefill($this->workingImage, 0, 0, $colorTransparent); - imagesavealpha($this->workingImage, true); - } - - $phpthumb->setOldImage($this->workingImage); - $this->currentDimensions['width'] = $width; - $this->currentDimensions['height'] = $newHeight; - $phpthumb->setCurrentDimensions($this->currentDimensions); - - return $phpthumb; - } - - /** - * Flips the image vertically - * - */ - protected function imageFlipVertical () - { - $x_i = imagesx($this->workingImage); - $y_i = imagesy($this->workingImage); - - for ($x = 0; $x < $x_i; $x++) { - for ($y = 0; $y < $y_i; $y++) { - imagecopy( - $this->workingImage, - $this->workingImage, - $x, - $y_i - $y - 1, - $x, - $y, - 1, - 1 - ); - } - } - } - - /** - * Converts a hex color to rgb tuples - * - * @return mixed - * @param string $hex - * @param bool $asString - */ - protected function hex2rgb ($hex, $asString = false) - { - // strip off any leading # - if (0 === strpos($hex, '#')) { - $hex = substr($hex, 1); - } elseif (0 === strpos($hex, '&H')) { - $hex = substr($hex, 2); - } - - // break into hex 3-tuple - $cutpoint = ceil(strlen($hex) / 2)-1; - $rgb = explode(':', wordwrap($hex, $cutpoint, ':', $cutpoint), 3); - - // convert each tuple to decimal - $rgb[0] = (isset($rgb[0]) ? hexdec($rgb[0]) : 0); - $rgb[1] = (isset($rgb[1]) ? hexdec($rgb[1]) : 0); - $rgb[2] = (isset($rgb[2]) ? hexdec($rgb[2]) : 0); - - return ($asString ? "{$rgb[0]} {$rgb[1]} {$rgb[2]}" : $rgb); - } -} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php index 5566d350c..d03aa43eb 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php @@ -245,16 +245,13 @@ trait Raw { try { - $oImagine = new \Imagine\Gd\Imagine(); - - $oImage = $oImagine->load(\stream_get_contents($rResource)); - - $oImage = $self->correctImageOrientation($oImage, $bDetectImageOrientation, 60); - + $oImage = static::loadImage(\stream_get_contents($rResource), $bDetectImageOrientation, 60); + $oImage->setImageFormat('png'); +// $oImage->setImageFormat('webp'); \header('Content-Disposition: inline; '. \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut.'_thumb60x60.png')), true); - - $oImage->show('png'); + \header('Content-Type: '.$oImage->getImageMimeType()); + echo $oImage->getImageBlob(); } catch (\Throwable $oException) { @@ -262,23 +259,16 @@ trait Raw } } else if ($bDetectImageOrientation && - \in_array($sContentTypeOut, array('image/png', 'image/jpeg', 'image/jpg')) && - \MailSo\Base\Utils::FunctionExistsAndEnabled('gd_info')) + \in_array($sContentTypeOut, array('image/png', 'image/jpeg', 'image/jpg', 'image/webp'))) { try { - $oImagine = new \Imagine\Gd\Imagine(); - $sLoadedData = \stream_get_contents($rResource); - - $oImage = $oImagine->load($sLoadedData); - - $oImage = $self->correctImageOrientation($oImage, $bDetectImageOrientation); - + $oImage = static::loadImage($sLoadedData, $bDetectImageOrientation); \header('Content-Disposition: inline; '. \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true); - - $oImage->show($sContentTypeOut === 'image/png' ? 'png' : 'jpg'); + \header('Content-Type: '.$oImage->getImageMimeType()); + echo $oImage->getImageBlob(); } catch (\Throwable $oException) { @@ -362,29 +352,21 @@ trait Raw }, $sFolder, $iUid, true, $sMimeIndex); } - private function correctImageOrientation(\Imagine\Image\AbstractImage $oImage, bool $bDetectImageOrientation = true, int $iThumbnailBoxSize = 0) : \Imagine\Image\AbstractImage + private static function loadImage(string $data, bool $bDetectImageOrientation = true, int $iThumbnailBoxSize = 0) : \SnappyMail\Image { - $iOrientation = 1; - if ($bDetectImageOrientation && \MailSo\Base\Utils::FunctionExistsAndEnabled('exif_read_data') && - \MailSo\Base\Utils::FunctionExistsAndEnabled('gd_info')) - { - $oMetadata = $oImage->metadata(new \Imagine\Image\Metadata\ExifMetadataReader()); - $iOrientation = isset($oMetadata['ifd0.Orientation']) && - is_numeric($oMetadata['ifd0.Orientation']) ? (int) $oMetadata['ifd0.Orientation'] : 1; - } - - if (0 < $iThumbnailBoxSize) { - $oImage = $oImage->thumbnail( - new \Imagine\Image\Box($iThumbnailBoxSize, $iThumbnailBoxSize), - \Imagine\Image\ImageInterface::THUMBNAIL_OUTBOUND); - } + if (\extension_loaded('gmagick')) { $handler = 'gmagick'; } + else if (\extension_loaded('imagick')) { $handler = 'imagick'; } + else if (\extension_loaded('gd')) { $handler = 'gd2'; } + else { return null; } + $handler = 'SnappyMail\\Image\\'.$handler.'::createFromString'; + $oImage = $handler($data); // rotateImageByOrientation - if (0 < $iOrientation) { - switch ($iOrientation) + if ($bDetectImageOrientation) { + switch ($oImage->getImageOrientation()) { case 2: // flip horizontal - $oImage->flipHorizontally(); + $oImage->flopImage(); break; case 3: // rotate 180 @@ -392,11 +374,11 @@ trait Raw break; case 4: // flip vertical - $oImage->flipVertically(); + $oImage->flipImage(); break; case 5: // vertical flip + 90 rotate - $oImage->flipVertically(); + $oImage->flipImage(); $oImage->rotate(90); break; @@ -405,7 +387,7 @@ trait Raw break; case 7: // horizontal flip + 90 rotate - $oImage->flipHorizontally(); + $oImage->flopImage(); $oImage->rotate(90); break; @@ -415,6 +397,13 @@ trait Raw } } + if (0 < $iThumbnailBoxSize) { + $oImage->cropThumbnailImage($iThumbnailBoxSize, $iThumbnailBoxSize); + } + + $oImage->stripImage(); + return $oImage; } + } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image.php b/snappymail/v/0.0.0/app/libraries/snappymail/image.php new file mode 100644 index 000000000..0e866291d --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image.php @@ -0,0 +1,10 @@ +img) { + \imagedestroy($this->img); + $this->img = null; + } + } + + function __toString() + { + return $this->getImageBlob(); + } + + public static function createFromString(string $data) + { + if (!($imginfo = \getimagesizefromstring($data))) { + throw new \InvalidArgumentException('Invalid image'); + } + $format = ''; + switch ($imginfo[2]) + { + case IMAGETYPE_GIF: $format = 'gif'; break; + case IMAGETYPE_JPEG: $format = 'jpeg'; break; + case IMAGETYPE_PNG: $format = 'png'; break; + case IMAGETYPE_WEBP: $format = 'webp'; break; + default: throw new \Exception('Unsupported fileformat: '.$imginfo['mime']); + } + $gd2 = new static(); + $gd2->img = \imagecreatefromstring($data); + if (!\is_resource($gd2->img)) { + throw new \InvalidArgumentException('Failed to load image'); + } + $gd2->file = 'blob'; + $gd2->type = (int) $imginfo[2]; + $gd2->format = $format; + if (\is_callable('exif_read_data')) { + $exif = \exif_read_data('data://'.$imginfo['mime'].';base64,' . \base64_encode($data)); + if ($exif) { + $gd2->orientation = \max(1, \intval($oMetadata['IFD0.Orientation'] ?? 0)); + } + } + return $gd2; + } + + public function rotate(float $degrees) : bool + { + return $this->rotateImage(0, $degrees); + } + + private function store_image($filename) : bool + { + switch ($this->format) + { + case 'png': + case 'png8': + case 'png24': + case 'png32': + \imagesavealpha($this->img, true); + return \imagepng($this->img, $filename, 9); + + case 'jpg': + case 'jpeg': + return \imagejpeg($this->img, $filename, $this->compression_q); + + case 'gif': + return \imagegif($this->img, $filename); + + case 'webp': + if (!\imageistruecolor($this->img)) { + \imagepalettetotruecolor($this->img); + if (-1 < \imagecolortransparent($this->img)) { + \imagealphablending($this->img, true); + \imagesavealpha($this->img, true); + } + } + return \imagewebp($this->img, $filename, $this->compression_q); + } + return false; + } + + private function create_image($width = -1, $height = -1, $trueColor = null) + { + if (-1 == $width) { $width = \imagesx($this->img); } + if (-1 == $height) { $height = \imagesy($this->img); } + if ($trueColor || ($this->img && \imageistruecolor($this->img))) { + $tmp_img = \imagecreatetruecolor($width, $height); + \imagesavealpha($tmp_img, true); + $trans_colour = \imagecolorallocatealpha($tmp_img, 0, 0, 0, 127); + \imagefill($tmp_img, 0, 0, $trans_colour); + } else { + $tmp_img = \imagecreate($width, $height); + \imagepalettecopy($tmp_img, $this->img); + $t_clr_i = \imagecolortransparent($this->img); + if (-1 !== $t_clr_i) { + \imagecolortransparent($tmp_img, $t_clr_i); + \imagefill($tmp_img, 0, 0, $t_clr_i); + } + } + return $tmp_img; + } + + /** + * Imagick PECL similar methods + */ + + public function cropImage($width, $height, $x, $y) + { + $x = \min(\imagesx($this->img), \max(0, $x)); + $y = \min(\imagesy($this->img), \max(0, $y)); + $width = \min($width, \imagesx($this->img) - $x); + $height = \min($height, \imagesy($this->img) - $y); + $tmp_img = $this->create_image($width, $height); + if (!\imagecopy($tmp_img, $this->img, 0, 0, $x, $y, $width, $height)) { + \imagedestroy($tmp_img); + throw new \Exception('Failed image transformation: crop()'); + } + \imagedestroy($this->img); + $this->img = $tmp_img; + return true; + } + + public function cropThumbnailImage($width, $height) + { + $x = \imagesx($this->img); + $y = \imagesy($this->img); + $tx = $x/$width; + $ty = $y/$height; + if ($tx > $ty) { + $x = \round($x/$ty); + $this->thumbnailImage($x, $height); + $x = \floor(($x-$width)/2); + $y = 0; + } else if ($tx < $ty) { + $y = \round($y/$tx); + $this->thumbnailImage($width, $y); + $x = 0; + $y = \floor(($y-$height)/2); + } else { + return $this->thumbnailImage($width, $height); + } + return $this->cropImage($width, $height, $x, $y); + } + + public function flipImage() + { + return \imageflip($this->img, IMG_FLIP_VERTICAL); + } + + public function flopImage() + { + return \imageflip($this->img, IMG_FLIP_HORIZONTAL); + } + + public function getImageBlob() + { + \ob_start(); + if (!$this->store_image(null)) { + \ob_end_clean(); + throw new \Exception('Failed to generate image blob'); + } + return \ob_get_clean(); + } + + public function getImageMimeType() + { + switch ($this->format) + { + case 'png': + case 'png8': + case 'png24': + case 'png32': + return 'image/png'; + case 'jpg': + case 'jpeg': + return 'image/jpeg'; + case 'gif': + return 'image/gif'; + case 'webp': + return 'image/webp'; + } + return false; + } + + public function getImageOrientation() : int + { + return $this->orientation; + } + + public function rotateImage($background, $degrees) + { + if (0 === ($degrees % 360)) { return true; } + /** rotate clockwise */ + if (!\function_exists('imagerotate')) { require __DIR__ . '/gd2/imagerotate.inc'; } + $tmp_img = \imagerotate($this->img, $degrees * -1, 0); + if (!\is_resource($tmp_img)) { return false; } + \imagedestroy($this->img); + $this->img = $tmp_img; + return true; + } + + public function thumbnailImage($width, $height, $bestfit = false) + { + if (!$width && !$height) { return false; } + if (0 > \min($width, $height)) { return false; } + $x = \imagesx($this->img); + $y = \imagesy($this->img); + $tx = $width ? $x/$width : 0; + $ty = $height ? $y/$height : 0; + if (!$width || ($bestfit && $tx < $ty)) { $width = \round($x / $ty); } + if (!$height || ($bestfit && $tx > $ty)) { $height = \round($y / $tx); } + $tmp_img = $this->create_image($width, $height); + if (!\is_resource($tmp_img)) { return false; } + \imagealphablending($tmp_img, false); + if (!\imagecopyresampled($tmp_img, $this->img, 0, 0, 0, 0, $width, $height, $x, $y)) { + if (!\imagecopyresized($tmp_img, $this->img, 0, 0, 0, 0, $width, $height, $x, $y)) { + \imagedestroy($tmp_img); + return false; + } + } + \imagedestroy($this->img); + $this->img = $tmp_img; + return true; + } + + public function setImageFormat($format) { $this->format = \strtolower($format); } + + public function stripImage() { return $this; } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2/imagerotate.inc b/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2/imagerotate.inc new file mode 100644 index 000000000..f3ccacde6 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2/imagerotate.inc @@ -0,0 +1,73 @@ + $maxx) $maxx = $x2; + if ($x2 < $minx) $minx = $x2; + if ($y2 > $maxy) $maxy = $y2; + if ($y2 < $miny) $miny = $y2; + } + } + + // Determine the new image size + $dstw = $maxx - $minx + 1; + $dsth = $maxy - $miny + 1; + + // Create our new image + $dstImg = imagecreatetruecolor($dstw, $dsth); + + // Fill the background with our transparent color + if (null == $transparentColor) $transparentColor = imagecolorallocate($dstImg, 1, 2, 3); + imagecolortransparent($dstImg, $transparentColor); + imagefilledrectangle($dstImg, 0, 0, $dstw + 1, $dsth + 1, $transparentColor); + + // Get the new origin + $neworiginx = -$minx; + $neworiginy = -$miny; + + // Fill in the pixels + foreach ($pixels as $data) + { + list($x, $y, $color) = $data; + $newx = $neworiginx + $x; + $newy = $neworiginy - $y; + imagesetpixel($dstImg, $newx, $newy, $color); + } + + return $dstImg; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php b/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php new file mode 100644 index 000000000..8f505d979 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php @@ -0,0 +1,60 @@ +clear(); + } + + public static function createFromString(string $data) + { + $gmagick = new static(); + if (!$gmagick->readimageblob($data)) { + throw new \InvalidArgumentException('Failed to load image'); + } + if (\is_callable('exif_read_data')) { + $exif = \exif_read_data('data://'.$imginfo['mime'].';base64,' . \base64_encode($data)); + if ($exif) { + $gmagick->orientation = \max(1, \intval($oMetadata['IFD0.Orientation'] ?? 0)); + } + } + return $gmagick; + } + + public function rotate(float $degrees) : bool + { + return $this->rotateImage(new \GmagickPixel(), $degrees); + } + + public function getImageMimeType() + { + switch (\strtolower(parent::getImageFormat())) + { + case 'png': + case 'png8': + case 'png24': + case 'png32': + return 'image/png'; + case 'jpeg': + return 'image/jpeg'; + case 'gif': + return 'image/gif'; + case 'webp': + return 'image/webp'; + } + return false; + } + + public function getImageOrientation() : int + { + return $this->orientation; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php b/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php new file mode 100644 index 000000000..a3de44597 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php @@ -0,0 +1,28 @@ +clear(); + } + + public static function createFromString(string $data) + { + $imagick = new static(); + if (!$imagick->readImageBlob($data)) { + throw new \InvalidArgumentException('Failed to load image'); + } + $imagick->setImageMatte(true); + return $imagick; + } + + public function rotate(float $degrees) : bool + { + return $this->rotateImage(new \ImagickPixel(), $degrees); + } +} From d2534b5ed9c3f2141d279fd3d47d3f91230b4e81 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 1 Apr 2021 21:01:35 +0200 Subject: [PATCH 34/90] Implemented: plugin CSS RainLoop still had a TODO for implementing plugin CSS This is now used by the CKEditor plugin --- plugins/ckeditor/ckeditor.less | 213 ++++++++++++++++++ plugins/ckeditor/index.php | 7 +- .../RainLoop/Plugins/AbstractPlugin.php | 10 + .../libraries/RainLoop/Plugins/Manager.php | 114 +++------- 4 files changed, 260 insertions(+), 84 deletions(-) create mode 100644 plugins/ckeditor/ckeditor.less diff --git a/plugins/ckeditor/ckeditor.less b/plugins/ckeditor/ckeditor.less new file mode 100644 index 000000000..3a1349013 --- /dev/null +++ b/plugins/ckeditor/ckeditor.less @@ -0,0 +1,213 @@ +/*.cke_button__sourcedialog_label { + display: none !important; +}*/ + +.cke_chrome { + border: 1px solid #ccc !important; +} + +.cke_toolgroup { + padding-right: 0 !important; + background: #FBFBFB !important; +} + +.cke_toolgroup, .cke_combo_button { + border: 1px solid #A6A6A6 !important; +} + +.cke_top { + padding: 2px; + box-shadow: none !important; + border-bottom: 1px solid #b6b6b6 !important; + background: #F0F0F0 !important; +} + +.cke_combo_on a.cke_combo_button, +.cke_combo_off a.cke_combo_button:hover, +.cke_combo_off a.cke_combo_button:focus, +.cke_combo_off a.cke_combo_button:active { + padding: 1px !important; + margin-left: 0 !important; +} + +.cke_toolgroup a.cke_button:last-child:after, +.cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after { + border: none !important; + height: 0 !important; + width: 0 !important; + margin: 0 !important; + padding: 0 !important; +} + +.cke_button_on { + background: #ddd !important; +} + +.cke_combo { + margin-top: 1px !important; +} + +.cke_combo__fontsize { + margin-left: 3px !important; + .cke_combo_text { + width: 40px !important; + } +} + +.cke_combo_text { + line-height: 24px !important; + padding-left: 8px !important; +} + +.cke_combo_button { + background: #FBFBFB !important; +} + +.cke_source { + font-family: var(--fontMono) !important; + padding: 10px !important; + padding-right: 0 !important; + box-shadow: none !important; +} + +.cke_plain { + + background-color: #fff; + outline: none; + overflow: auto; + z-index: 1; + margin: 0; + border: 0; + + padding: 10px; + padding-right: 0; + white-space: pre-wrap; + font-family: var(--fontMono), monospace; + font-size: 13px; + line-height: 16px; + color: #333; + resize: none; + + border-radius: 0; + box-shadow: none; + box-sizing: border-box; +} + +.cke_plain:focus, .cke_source:focus { + border: 0; +} + +.b-compose .cke_chrome { + border-right: 0; + border-left: 0; + border-bottom: 0; + box-shadow: none; +} + +.cke_wysiwyg_div { + + padding: 10px !important; + font-family: var(--fontSans); + font-size: 13px; + line-height: 16px; + color: #333; + + ul { + padding-left: 40px; + li { + list-style-type: disc !important; + } + } + + ol { + padding-left: 40px; + li { + list-style-type: decimal !important; + } + } + + pre, code { + margin: 0; + padding: 0; + background: #fff; + border: none; + border-radius: 0; + font-family: var(--fontMono); + display: block; + word-break: normal; + word-wrap: break-word; + background-color: #f9f9f9; + } + + code { + display: inline; + padding: 2px 5px; + } + + pre { + padding: 5px 10px; + border-radius: 5px; + background-color: #f9f9f9; + } + + pre > code { + padding: 0; + } + + blockquote { + border: 0; + border-left: solid 2px #444; + margin: 5px 0 5px 5px; + padding-left: 5px; + } + + blockquote p { + margin: 0 0 10px; + font-size: 14px; + line-height: 20px; + } + + img { + vertical-align: bottom; + } + + a { + color: blue; + text-decoration: underline; + + &:visited { + color: #609; + } + &:active { + color: red; + } + } +} + +.cke_dialog { + + a:hover { + text-decoration: none; + } + + .cke_dialog_ui_labeled_content { + margin-top: 5px; + margin-bottom: 5px; + } + + .cke_dialog_ui_input_select, .cke_dialog_ui_input_text, .cke_dialog_ui_input_textarea { + + box-shadow: none; + border-radius: 2px; + + &:focus { + outline: 0; + border: 1px solid #999; + } + } + + .cke_dialog_ui_input_select, .cke_dialog_ui_input_text { + height: 25px; + line-height: 25px; + } +} diff --git a/plugins/ckeditor/index.php b/plugins/ckeditor/index.php index 8659d7efc..9db7d0375 100644 --- a/plugins/ckeditor/index.php +++ b/plugins/ckeditor/index.php @@ -4,9 +4,9 @@ class CKEditorPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'CKEditor', - VERSION = '2.0', - RELEASE = '2021-03-25', - REQUIRED = '2.4.2', + VERSION = '2.1', + RELEASE = '2021-04-01', + REQUIRED = '2.4.4', DESCRIPTION = 'Use CKEditor instead of Squire as WYSIWYG'; public function Init() : void @@ -32,6 +32,7 @@ class CKEditorPlugin extends \RainLoop\Plugins\AbstractPlugin } if (\is_file("{$path}/ckeditor.js")) { + $this->addCss('ckeditor.less'); $this->addJs('ckeditor.js'); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php index 8496b4a76..8b783ef80 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php @@ -172,6 +172,16 @@ abstract class AbstractPlugin return $this; } + protected function addCss(string $sFile, bool $bAdminScope = false) : self + { + if ($this->oPluginManager) + { + $this->oPluginManager->AddCss($this->sPath.'/'.$sFile, $bAdminScope); + } + + return $this; + } + protected function addJs(string $sFile, bool $bAdminScope = false) : self { if ($this->oPluginManager) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php index c5193eac7..64cb0c191 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php @@ -9,50 +9,16 @@ class Manager */ private $oActions; - /** - * @var array - */ - private $aHooks; - - /** - * @var array - */ - private $aJs; - - /** - * @var array - */ - private $aAdminJs; - - /** - * @var array - */ - private $aTemplates; - - /** - * @var array - */ - private $aAdminTemplates; - - /** - * @var array - */ - private $aProcessTemplate; - - /** - * @var array - */ - private $aAdditionalParts; - - /** - * @var array - */ - private $aAdditionalJson; - - /** - * @var array - */ - private $aPlugins; + private + $aHooks = array(), + $aCss = array([], []), + $aJs = array([], []), + $aTemplates = array(), + $aAdminTemplates = array(), + $aProcessTemplate = array(), + $aAdditionalParts = array(), + $aAdditionalJson = array(), + $aPlugins = array(); /** * @var bool @@ -68,17 +34,6 @@ class Manager { $this->oLogger = null; $this->oActions = $oActions; - $this->aPlugins = array(); - - $this->aHooks = array(); - $this->aJs = array(); - $this->aAdminJs = array(); - $this->aTemplates = array(); - $this->aAdminTemplates = array(); - - $this->aJsonFilters = array(); - $this->aAdditionalJson = array(); - $this->aProcessTemplate = array(); $oConfig = $this->oActions->Config(); $this->bIsEnabled = (bool) $oConfig->Get('plugins', 'enable', false); @@ -216,14 +171,20 @@ class Manager public function HaveJs(bool $bAdminScope = false) : bool { - $bResult = false; + return $this->bIsEnabled && 0 < \count($this->aJs[$bAdminScope ? 1 : 0]); + } - if ($this->bIsEnabled) - { - $bResult = $bAdminScope ? 0 < \count($this->aAdminJs) : 0 < \count($this->aJs); + public function CompileCss(bool $bAdminScope = false) : string + { + $aResult = array(); + if ($this->bIsEnabled) { + foreach ($this->aCss[$bAdminScope ? 1 : 0] as $sFile) { + if (\is_readable($sFile)) { + $aResult[] = \file_get_contents($sFile); + } + } } - - return $bResult; + return \implode("\n", $aResult); } public function CompileJs(bool $bAdminScope = false) : string @@ -231,8 +192,7 @@ class Manager $aResult = array(); if ($this->bIsEnabled) { - $aJs = $bAdminScope ? $this->aAdminJs : $this->aJs; - foreach ($aJs as $sFile) + foreach ($this->aJs[$bAdminScope ? 1 : 0] as $sFile) { if (\is_readable($sFile)) { @@ -244,14 +204,6 @@ class Manager return \implode("\n", $aResult); } - /** - * @todo - */ - public function CompileCss(bool $bAdminScope = false) : string - { - return ''; - } - public function CompileTemplate(array &$aList, bool $bAdminScope = false) : void { if ($this->bIsEnabled) @@ -326,18 +278,18 @@ class Manager return $this; } + public function AddCss(string $sFile, bool $bAdminScope = false) : self + { + if ($this->bIsEnabled) { + $this->aCss[$bAdminScope ? 1 : 0] = $sFile; + } + return $this; + } + public function AddJs(string $sFile, bool $bAdminScope = false) : self { - if ($this->bIsEnabled) - { - if ($bAdminScope) - { - $this->aAdminJs[$sFile] = $sFile; - } - else - { - $this->aJs[$sFile] = $sFile; - } + if ($this->bIsEnabled) { + $this->aJs[$bAdminScope ? 1 : 0][$sFile] = $sFile; } return $this; From 66cb1a272c60e49fe6d5cf2bf97f51dd06c29435 Mon Sep 17 00:00:00 2001 From: djmaze Date: Thu, 1 Apr 2021 21:49:43 +0200 Subject: [PATCH 35/90] Rename 'SabreForRainLoop' to the normal 'Sabre' and update to 1.8.12 Will update later to v4.1.5+ --- .../RainLoop/Providers/AddressBook.php | 8 +- .../Providers/AddressBook/Classes/Contact.php | 16 +- .../Providers/AddressBook/PdoAddressBook.php | 20 +- .../CalDAV/Backend/AbstractBackend.php | 16 +- .../CalDAV/Backend/BackendInterface.php | 10 +- .../CalDAV/Backend/NotificationSupport.php | 12 +- .../CalDAV/Backend/PDO.php | 32 ++-- .../CalDAV/Backend/SharingSupport.php | 14 +- .../CalDAV/Calendar.php | 18 +- .../CalDAV/CalendarObject.php | 16 +- .../CalDAV/CalendarQueryParser.php | 24 +-- .../CalDAV/CalendarQueryValidator.php | 16 +- .../CalDAV/CalendarRootNode.php | 16 +- .../CalDAV/Exception/InvalidComponentType.php | 10 +- .../CalDAV/ICSExportPlugin.php | 14 +- .../CalDAV/ICalendar.php | 10 +- .../CalDAV/ICalendarObject.php | 8 +- .../CalDAV/IShareableCalendar.php | 8 +- .../CalDAV/ISharedCalendar.php | 8 +- .../CalDAV/Notifications/Collection.php | 18 +- .../CalDAV/Notifications/ICollection.php | 10 +- .../CalDAV/Notifications/INode.php | 10 +- .../Notifications/INotificationType.php | 8 +- .../CalDAV/Notifications/Node.php | 22 +-- .../Notifications/Notification/Invite.php | 16 +- .../Notification/InviteReply.php | 12 +- .../Notification/SystemStatus.php | 10 +- .../CalDAV/Plugin.php | 28 +-- .../CalDAV/Principal/Collection.php | 8 +- .../CalDAV/Principal/IProxyRead.php | 8 +- .../CalDAV/Principal/IProxyWrite.php | 8 +- .../CalDAV/Principal/ProxyRead.php | 10 +- .../CalDAV/Principal/ProxyWrite.php | 10 +- .../CalDAV/Principal/User.php | 10 +- .../CalDAV/Property/AllowedSharingModes.php | 8 +- .../CalDAV/Property/Invite.php | 12 +- .../Property/ScheduleCalendarTransp.php | 10 +- .../SupportedCalendarComponentSet.php | 10 +- .../CalDAV/Property/SupportedCalendarData.php | 10 +- .../CalDAV/Property/SupportedCollationSet.php | 8 +- .../CalDAV/Schedule/IMip.php | 10 +- .../Sabre/CalDAV/Schedule/IOutbox.php | 16 ++ .../CalDAV/Schedule/Outbox.php | 16 +- .../CalDAV/ShareableCalendar.php | 8 +- .../CalDAV/SharedCalendar.php | 10 +- .../CalDAV/SharingPlugin.php | 18 +- .../CalDAV/UserCalendars.php | 14 +- .../CalDAV/Version.php | 8 +- .../CardDAV/AddressBook.php | 12 +- .../CardDAV/AddressBookQueryParser.php | 8 +- .../CardDAV/AddressBookRoot.php | 12 +- .../CardDAV/Backend/AbstractBackend.php | 6 +- .../CardDAV/Backend/BackendInterface.php | 10 +- .../CardDAV/Backend/PDO.php | 14 +- .../CardDAV/Card.php | 12 +- .../CardDAV/IAddressBook.php | 8 +- .../CardDAV/ICard.php | 8 +- .../CardDAV/IDirectory.php | 6 +- .../CardDAV/Plugin.php | 22 +-- .../CardDAV/Property/SupportedAddressData.php | 10 +- .../CardDAV/UserAddressBooks.php | 12 +- .../CardDAV/VCFExportPlugin.php | 12 +- .../CardDAV/Version.php | 8 +- .../DAV/Auth/Backend/AbstractBasic.php | 10 +- .../DAV/Auth/Backend/AbstractDigest.php | 10 +- .../DAV/Auth/Backend/Apache.php | 8 +- .../DAV/Auth/Backend/BackendInterface.php | 10 +- .../DAV/Auth/Backend/File.php | 8 +- .../DAV/Auth/Backend/PDO.php | 6 +- .../DAV/Auth/Plugin.php | 12 +- .../DAV/Browser/GuessContentType.php | 8 +- .../DAV/Browser/MapGetToPropFind.php | 10 +- .../DAV/Browser/Plugin.php | 26 +-- .../DAV/Browser/assets/favicon.ico | Bin .../DAV/Browser/assets/icons/addressbook.png | Bin .../DAV/Browser/assets/icons/calendar.png | Bin .../DAV/Browser/assets/icons/card.png | Bin .../DAV/Browser/assets/icons/collection.png | Bin .../DAV/Browser/assets/icons/file.png | Bin .../DAV/Browser/assets/icons/parent.png | Bin .../DAV/Browser/assets/icons/principal.png | Bin .../DAV/Client.php | 176 ++++++++++-------- .../DAV/Collection.php | 8 +- .../DAV/Exception.php | 6 +- .../DAV/Exception/BadRequest.php | 8 +- .../DAV/Exception/Conflict.php | 8 +- .../DAV/Exception/ConflictingLock.php | 8 +- .../Sabre/DAV/Exception/FileNotFound.php | 19 ++ .../DAV/Exception/Forbidden.php | 8 +- .../DAV/Exception/InsufficientStorage.php | 8 +- .../DAV/Exception/InvalidResourceType.php | 8 +- .../Sabre/DAV/Exception/LengthRequired.php | 30 +++ .../Exception/LockTokenMatchesRequestUri.php | 8 +- .../DAV/Exception/Locked.php | 10 +- .../DAV/Exception/MethodNotAllowed.php | 12 +- .../DAV/Exception/NotAuthenticated.php | 8 +- .../DAV/Exception/NotFound.php | 8 +- .../DAV/Exception/NotImplemented.php | 8 +- .../DAV/Exception/PaymentRequired.php | 8 +- .../DAV/Exception/PreconditionFailed.php | 8 +- .../DAV/Exception/ReportNotSupported.php | 8 +- .../RequestedRangeNotSatisfiable.php | 8 +- .../DAV/Exception/ServiceUnavailable.php | 8 +- .../DAV/Exception/UnsupportedMediaType.php | 8 +- .../DAV/FS/Directory.php | 8 +- .../DAV/FS/File.php | 8 +- .../DAV/FS/Node.php | 8 +- .../DAV/FSExt/Directory.php | 10 +- .../DAV/FSExt/File.php | 56 ++++-- .../DAV/FSExt/Node.php | 10 +- .../{SabreForRainLoop => Sabre}/DAV/File.php | 6 +- .../DAV/ICollection.php | 8 +- .../DAV/IExtendedCollection.php | 6 +- .../{SabreForRainLoop => Sabre}/DAV/IFile.php | 6 +- .../{SabreForRainLoop => Sabre}/DAV/INode.php | 6 +- .../DAV/IProperties.php | 6 +- .../DAV/IQuota.php | 6 +- .../DAV/Locks/Backend/AbstractBackend.php | 8 +- .../DAV/Locks/Backend/BackendInterface.php | 10 +- .../DAV/Locks/Backend/FS.php | 10 +- .../DAV/Locks/Backend/File.php | 10 +- .../DAV/Locks/Backend/PDO.php | 10 +- .../DAV/Locks/LockInfo.php | 8 +- .../DAV/Locks/Plugin.php | 31 +-- .../DAV/Mount/Plugin.php | 10 +- .../{SabreForRainLoop => Sabre}/DAV/Node.php | 10 +- .../DAV/ObjectTree.php | 6 +- .../DAV/PartialUpdate/IFile.php | 13 +- .../Sabre/DAV/PartialUpdate/IPatchSupport.php | 48 +++++ .../DAV/PartialUpdate/Plugin.php | 108 +++++++---- .../DAV/Property.php | 6 +- .../DAV/Property/GetLastModified.php | 10 +- .../DAV/Property/Href.php | 8 +- .../DAV/Property/HrefList.php | 8 +- .../DAV/Property/IHref.php | 6 +- .../DAV/Property/LockDiscovery.php | 8 +- .../DAV/Property/ResourceType.php | 8 +- .../DAV/Property/Response.php | 8 +- .../DAV/Property/ResponseList.php | 12 +- .../DAV/Property/SupportedLock.php | 8 +- .../DAV/Property/SupportedReportSet.php | 8 +- .../DAV/PropertyInterface.php | 6 +- .../DAV/Server.php | 49 ++--- .../DAV/ServerPlugin.php | 10 +- .../DAV/SimpleCollection.php | 10 +- .../DAV/SimpleFile.php | 6 +- .../DAV/StringUtil.php | 8 +- .../DAV/TemporaryFileFilterPlugin.php | 10 +- .../{SabreForRainLoop => Sabre}/DAV/Tree.php | 6 +- .../DAV/Tree/Filesystem.php | 10 +- .../DAV/URLUtil.php | 13 +- .../DAV/UUIDUtil.php | 6 +- .../DAVACL => Sabre/DAV}/Version.php | 8 +- .../DAV/XMLUtil.php | 14 +- .../DAVACL/AbstractPrincipalCollection.php | 8 +- .../DAVACL/Exception/AceConflict.php | 8 +- .../DAVACL/Exception/NeedPrivileges.php | 8 +- .../DAVACL/Exception/NoAbstract.php | 8 +- .../Exception/NotRecognizedPrincipal.php | 8 +- .../Exception/NotSupportedPrivilege.php | 8 +- .../DAVACL/IACL.php | 10 +- .../DAVACL/IPrincipal.php | 8 +- .../DAVACL/IPrincipalCollection.php | 8 +- .../DAVACL/Plugin.php | 24 +-- .../DAVACL/Principal.php | 12 +- .../PrincipalBackend/AbstractBackend.php | 6 +- .../PrincipalBackend/BackendInterface.php | 10 +- .../DAVACL/PrincipalBackend/PDO.php | 10 +- .../DAVACL/PrincipalCollection.php | 10 +- .../DAVACL/Property/Acl.php | 10 +- .../DAVACL/Property/AclRestrictions.php | 8 +- .../Property/CurrentUserPrivilegeSet.php | 8 +- .../DAVACL/Property/Principal.php | 8 +- .../DAVACL/Property/SupportedPrivilegeSet.php | 10 +- .../DAV => Sabre/DAVACL}/Version.php | 6 +- .../HTTP/AWSAuth.php | 6 +- .../HTTP/AbstractAuth.php | 10 +- .../HTTP/BasicAuth.php | 8 +- .../HTTP/DigestAuth.php | 10 +- .../HTTP/Request.php | 6 +- .../HTTP/Response.php | 8 +- .../{SabreForRainLoop => Sabre}/HTTP/Util.php | 6 +- .../app/libraries/Sabre/HTTP/Version.php | 24 +++ .../v/0.0.0/app/libraries/Sabre/LICENSE | 27 +++ .../VObject/Cli.php | 2 +- .../VObject/Component.php | 6 +- .../VObject/Component/VAlarm.php | 4 +- .../VObject/Component/VCalendar.php | 148 +++++++-------- .../VObject/Component/VCard.php | 116 ++++++------ .../VObject/Component/VEvent.php | 4 +- .../VObject/Component/VFreeBusy.php | 4 +- .../VObject/Component/VJournal.php | 4 +- .../VObject/Component/VTodo.php | 4 +- .../VObject/DateTimeParser.php | 2 +- .../VObject/Document.php | 6 +- .../VObject/ElementList.php | 2 +- .../VObject/EofException.php | 2 +- .../VObject/FreeBusyGenerator.php | 6 +- .../VObject/Node.php | 2 +- .../VObject/Parameter.php | 2 +- .../VObject/ParseException.php | 2 +- .../VObject/Parser/Json.php | 18 +- .../VObject/Parser/MimeDir.php | 20 +- .../VObject/Parser/Parser.php | 2 +- .../VObject/Property.php | 2 +- .../VObject/Property/Binary.php | 4 +- .../VObject/Property/Boolean.php | 4 +- .../VObject/Property/FlatText.php | 2 +- .../VObject/Property/FloatValue.php | 4 +- .../VObject/Property/ICalendar/CalAddress.php | 4 +- .../VObject/Property/ICalendar/Date.php | 10 +- .../VObject/Property/ICalendar/DateTime.php | 10 +- .../VObject/Property/ICalendar/Duration.php | 8 +- .../VObject/Property/ICalendar/Period.php | 8 +- .../VObject/Property/ICalendar/Recur.php | 6 +- .../VObject/Property/Integer.php | 4 +- .../VObject/Property/Text.php | 10 +- .../VObject/Property/Time.php | 4 +- .../VObject/Property/Unknown.php | 10 +- .../VObject/Property/Uri.php | 4 +- .../VObject/Property/UtcOffset.php | 2 +- .../VObject/Property/VCard/Date.php | 4 +- .../VObject/Property/VCard/DateAndOrTime.php | 6 +- .../VObject/Property/VCard/DateTime.php | 4 +- .../VObject/Property/VCard/LanguageTag.php | 4 +- .../VObject/Property/VCard/TimeStamp.php | 6 +- .../VObject/Reader.php | 2 +- .../VObject/RecurrenceIterator.php | 2 +- .../VObject/Splitter/ICalendar.php | 8 +- .../VObject/Splitter/SplitterInterface.php | 4 +- .../VObject/Splitter/VCard.php | 8 +- .../VObject/StringUtil.php | 2 +- .../VObject/TimeZoneUtil.php | 4 +- .../VObject/VCardConverter.php | 2 +- .../VObject/Version.php | 2 +- .../CalDAV/Schedule/IOutbox.php | 16 -- .../DAV/Exception/FileNotFound.php | 19 -- .../SabreForRainLoop/HTTP/Version.php | 24 --- 238 files changed, 1511 insertions(+), 1310 deletions(-) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Backend/AbstractBackend.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Backend/BackendInterface.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Backend/NotificationSupport.php (71%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Backend/PDO.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Backend/SharingSupport.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Calendar.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/CalendarObject.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/CalendarQueryParser.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/CalendarQueryValidator.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/CalendarRootNode.php (78%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Exception/InvalidComponentType.php (73%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/ICSExportPlugin.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/ICalendar.php (76%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/ICalendarObject.php (61%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/IShareableCalendar.php (82%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/ISharedCalendar.php (74%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/Collection.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/ICollection.php (54%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/INode.php (65%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/INotificationType.php (79%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/Node.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/Notification/Invite.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/Notification/InviteReply.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Notifications/Notification/SystemStatus.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Plugin.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Principal/Collection.php (75%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Principal/IProxyRead.php (54%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Principal/IProxyWrite.php (54%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Principal/ProxyRead.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Principal/ProxyWrite.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Principal/User.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Property/AllowedSharingModes.php (89%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Property/Invite.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Property/ScheduleCalendarTransp.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Property/SupportedCalendarComponentSet.php (88%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Property/SupportedCalendarData.php (79%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Property/SupportedCollationSet.php (82%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Schedule/IMip.php (92%) create mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IOutbox.php rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Schedule/Outbox.php (89%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/ShareableCalendar.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/SharedCalendar.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/SharingPlugin.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/UserCalendars.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CalDAV/Version.php (50%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/AddressBook.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/AddressBookQueryParser.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/AddressBookRoot.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Backend/AbstractBackend.php (61%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Backend/BackendInterface.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Backend/PDO.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Card.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/IAddressBook.php (53%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/ICard.php (51%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/IDirectory.php (67%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Plugin.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Property/SupportedAddressData.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/UserAddressBooks.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/VCFExportPlugin.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/CardDAV/Version.php (51%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Backend/AbstractBasic.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Backend/AbstractDigest.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Backend/Apache.php (86%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Backend/BackendInterface.php (67%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Backend/File.php (88%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Backend/PDO.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Auth/Plugin.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/GuessContentType.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/MapGetToPropFind.php (82%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/Plugin.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/favicon.ico (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/addressbook.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/calendar.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/card.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/collection.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/file.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/parent.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Browser/assets/icons/principal.png (100%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Client.php (82%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Collection.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception.php (88%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/BadRequest.php (58%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/Conflict.php (61%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/ConflictingLock.php (81%) create mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/FileNotFound.php rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/Forbidden.php (58%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/InsufficientStorage.php (58%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/InvalidResourceType.php (71%) create mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/LockTokenMatchesRequestUri.php (80%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/Locked.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/MethodNotAllowed.php (65%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/NotAuthenticated.php (65%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/NotFound.php (58%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/NotImplemented.php (58%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/PaymentRequired.php (66%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/PreconditionFailed.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/ReportNotSupported.php (76%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/RequestedRangeNotSatisfiable.php (67%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/ServiceUnavailable.php (65%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Exception/UnsupportedMediaType.php (62%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/FS/Directory.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/FS/File.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/FS/Node.php (86%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/FSExt/Directory.php (92%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/FSExt/File.php (53%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/FSExt/Node.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/File.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/ICollection.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/IExtendedCollection.php (74%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/IFile.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/INode.php (80%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/IProperties.php (92%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/IQuota.php (77%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/Backend/AbstractBackend.php (62%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/Backend/BackendInterface.php (77%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/Backend/FS.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/Backend/File.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/Backend/PDO.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/LockInfo.php (80%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Locks/Plugin.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Mount/Plugin.php (88%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Node.php (74%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/ObjectTree.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/PartialUpdate/IFile.php (69%) create mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/IPatchSupport.php rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/PartialUpdate/Plugin.php (57%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property.php (75%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/GetLastModified.php (86%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/Href.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/HrefList.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/IHref.php (66%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/LockDiscovery.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/ResourceType.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/Response.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/ResponseList.php (74%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/SupportedLock.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Property/SupportedReportSet.php (92%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/PropertyInterface.php (63%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Server.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/ServerPlugin.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/SimpleCollection.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/SimpleFile.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/StringUtil.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/TemporaryFileFilterPlugin.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Tree.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/Tree/Filesystem.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/URLUtil.php (86%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/UUIDUtil.php (89%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop/DAVACL => Sabre/DAV}/Version.php (57%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAV/XMLUtil.php (92%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/AbstractPrincipalCollection.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Exception/AceConflict.php (76%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Exception/NeedPrivileges.php (89%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Exception/NoAbstract.php (76%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Exception/NotRecognizedPrincipal.php (77%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Exception/NotSupportedPrivilege.php (77%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/IACL.php (84%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/IPrincipal.php (89%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/IPrincipalCollection.php (84%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Plugin.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Principal.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/PrincipalBackend/AbstractBackend.php (64%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/PrincipalBackend/BackendInterface.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/PrincipalBackend/PDO.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/PrincipalCollection.php (69%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Property/Acl.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Property/AclRestrictions.php (75%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Property/CurrentUserPrivilegeSet.php (92%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Property/Principal.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/DAVACL/Property/SupportedPrivilegeSet.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop/DAV => Sabre/DAVACL}/Version.php (63%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/AWSAuth.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/AbstractAuth.php (87%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/BasicAuth.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/DigestAuth.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/Request.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/Response.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/HTTP/Util.php (93%) create mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Version.php create mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/LICENSE rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Cli.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VAlarm.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VCalendar.php (57%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VCard.php (63%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VEvent.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VFreeBusy.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VJournal.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Component/VTodo.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/DateTimeParser.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Document.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/ElementList.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/EofException.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/FreeBusyGenerator.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Node.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Parameter.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/ParseException.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Parser/Json.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Parser/MimeDir.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Parser/Parser.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Binary.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Boolean.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/FlatText.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/FloatValue.php (96%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/ICalendar/CalAddress.php (88%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/ICalendar/Date.php (63%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/ICalendar/DateTime.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/ICalendar/Duration.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/ICalendar/Period.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/ICalendar/Recur.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Integer.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Text.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Time.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Unknown.php (81%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/Uri.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/UtcOffset.php (94%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/VCard/Date.php (85%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/VCard/DateAndOrTime.php (95%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/VCard/DateTime.php (86%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/VCard/LanguageTag.php (92%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Property/VCard/TimeStamp.php (91%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Reader.php (98%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/RecurrenceIterator.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Splitter/ICalendar.php (93%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Splitter/SplitterInterface.php (90%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Splitter/VCard.php (89%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/StringUtil.php (97%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/TimeZoneUtil.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/VCardConverter.php (99%) rename snappymail/v/0.0.0/app/libraries/{SabreForRainLoop => Sabre}/VObject/Version.php (91%) delete mode 100644 snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Schedule/IOutbox.php delete mode 100644 snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/FileNotFound.php delete mode 100644 snappymail/v/0.0.0/app/libraries/SabreForRainLoop/HTTP/Version.php diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php index 635d9d5d0..8c3b2a129 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php @@ -223,7 +223,7 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider { $iCount = 0; - if (\class_exists('SabreForRainLoop\DAV\Client') && $this->IsActive() && \is_string($sVcfData)) + if (\class_exists('Sabre\DAV\Client') && $this->IsActive() && \is_string($sVcfData)) { $sVcfData = \trim($sVcfData); if ("\xef\xbb\xbf" === \substr($sVcfData, 0, 3)) @@ -234,7 +234,7 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider $oVCardSplitter = null; try { - $oVCardSplitter = new \SabreForRainLoop\VObject\Splitter\VCard($sVcfData); + $oVCardSplitter = new \Sabre\VObject\Splitter\VCard($sVcfData); } catch (\Throwable $oExc) { @@ -249,13 +249,13 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider while ($oVCard = $oVCardSplitter->getNext()) { - if ($oVCard instanceof \SabreForRainLoop\VObject\Component\VCard) + if ($oVCard instanceof \Sabre\VObject\Component\VCard) { \MailSo\Base\Utils::ResetTimeLimit(); if (empty($oVCard->UID)) { - $oVCard->UID = \SabreForRainLoop\DAV\UUIDUtil::getUUID(); + $oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID(); } $oContact->PopulateByVCard($oVCard->UID, $oVCard->serialize()); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index 3a564f7ba..72fb82175 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -163,8 +163,8 @@ class Contact implements \JsonSerializable public function RegenerateContactStr() : void { - $this->IdContactStr = \class_exists('SabreForRainLoop\DAV\Client') ? - \SabreForRainLoop\DAV\UUIDUtil::getUUID() : \MailSo\Base\Utils::Md5Rand(); + $this->IdContactStr = \class_exists('Sabre\DAV\Client') ? + \Sabre\DAV\UUIDUtil::getUUID() : \MailSo\Base\Utils::Md5Rand(); } public function GetEmails() : array @@ -190,7 +190,7 @@ class Contact implements \JsonSerializable { $this->UpdateDependentValues(); - if (!\class_exists('SabreForRainLoop\DAV\Client')) + if (!\class_exists('Sabre\DAV\Client')) { return ''; } @@ -205,7 +205,7 @@ class Contact implements \JsonSerializable { try { - $oVCard = \SabreForRainLoop\VObject\Reader::read($sPreVCard); + $oVCard = \Sabre\VObject\Reader::read($sPreVCard); } catch (\Throwable $oExc) { @@ -224,7 +224,7 @@ class Contact implements \JsonSerializable if (!$oVCard) { - $oVCard = new \SabreForRainLoop\VObject\Component\VCard(); + $oVCard = new \Sabre\VObject\Component\VCard(); } $oVCard->VERSION = '3.0'; @@ -510,7 +510,7 @@ class Contact implements \JsonSerializable $this->Properties = array(); - if (!\class_exists('SabreForRainLoop\DAV\Client')) + if (!\class_exists('Sabre\DAV\Client')) { return false; } @@ -524,7 +524,7 @@ class Contact implements \JsonSerializable try { - $oVCard = \SabreForRainLoop\VObject\Reader::read($sVCard); + $oVCard = \Sabre\VObject\Reader::read($sVCard); } catch (\Throwable $oExc) { @@ -621,7 +621,7 @@ class Contact implements \JsonSerializable $this->addArrayPropertyHelper($aProperties, $oVCard->TEL, PropertyType::PHONE); } - $sUidValue = $oVCard->UID ? (string) $oVCard->UID : \SabreForRainLoop\DAV\UUIDUtil::getUUID(); + $sUidValue = $oVCard->UID ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID(); $aProperties[] = new Property(PropertyType::UID, $sUidValue); if (empty($this->IdContactStr)) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php index 1fba18443..f8f1aed03 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php @@ -214,7 +214,7 @@ class PdoAddressBook return $aResponse; } - private function detectionPropFind(\SabreForRainLoop\DAV\Client $oClient, string $sPath) : ?array + private function detectionPropFind(\Sabre\DAV\Client $oClient, string $sPath) : ?array { $aResponse = null; @@ -239,7 +239,7 @@ class PdoAddressBook return $aResponse; } - private function getContactsPaths(\SabreForRainLoop\DAV\Client $oClient, string $sUser, string $sPassword, string $sProxy = '') : array + private function getContactsPaths(\Sabre\DAV\Client $oClient, string $sUser, string $sPassword, string $sProxy = '') : array { $aContactsPaths = array(); @@ -285,7 +285,7 @@ class PdoAddressBook if (empty($sNextPath)) { $oResourceType = isset($aItem['{DAV:}resourcetype']) ? $aItem['{DAV:}resourcetype'] : null; - /* @var $oResourceType \SabreForRainLoop\DAV\Property\ResourceType */ + /* @var $oResourceType \Sabre\DAV\Property\ResourceType */ if ($oResourceType && $oResourceType->is('{DAV:}collection')) { $sNextPath = $sKey; @@ -408,7 +408,7 @@ class PdoAddressBook if (!empty($sKey) && $aItem && isset($aItem['{DAV:}resourcetype'])) { $oResourceType = $aItem['{DAV:}resourcetype']; - /* @var $oResourceType \SabreForRainLoop\DAV\Property\ResourceType */ + /* @var $oResourceType \Sabre\DAV\Property\ResourceType */ if ($oResourceType && $oResourceType->is('{DAV:}collection')) { @@ -425,7 +425,7 @@ class PdoAddressBook return $aContactsPaths; } - private function checkContactsPath(\SabreForRainLoop\DAV\Client $oClient, string $sPath) : bool + private function checkContactsPath(\Sabre\DAV\Client $oClient, string $sPath) : bool { if (!$oClient) { @@ -456,7 +456,7 @@ class PdoAddressBook if (!empty($sKey) && isset($aItem['{DAV:}resourcetype'])) { $oResourceType = $aItem['{DAV:}resourcetype']; - /* @var $oResourceType \SabreForRainLoop\DAV\Property\ResourceType */ + /* @var $oResourceType \Sabre\DAV\Property\ResourceType */ if ($oResourceType && $oResourceType->is('{DAV:}collection') && $oResourceType->is('{urn:ietf:params:xml:ns:carddav}addressbook')) @@ -475,7 +475,7 @@ class PdoAddressBook return $bGood; } - public function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : \SabreForRainLoop\DAV\Client + public function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : \Sabre\DAV\Client { if (!\preg_match('/^http[s]?:\/\//i', $sUrl)) { @@ -509,7 +509,7 @@ class PdoAddressBook $aSettings['proxy'] = $sProxy; } - $oClient = new \SabreForRainLoop\DAV\Client($aSettings); + $oClient = new \Sabre\DAV\Client($aSettings); $oClient->setVerifyPeer(false); $oClient->__UrlPath__ = $aUrl['path']; @@ -519,9 +519,9 @@ class PdoAddressBook return $oClient; } - public function getDavClient(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : ?\SabreForRainLoop\DAV\Client + public function getDavClient(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : ?\Sabre\DAV\Client { - if (!\class_exists('SabreForRainLoop\DAV\Client')) + if (!\class_exists('Sabre\DAV\Client')) { return null; } diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/AbstractBackend.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php similarity index 91% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/AbstractBackend.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php index eab644076..06fdbd9a9 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/AbstractBackend.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php @@ -1,18 +1,18 @@ getCalendarObjects($calendarId); - $validator = new \SabreForRainLoop\CalDAV\CalendarQueryValidator(); + $validator = new \Sabre\CalDAV\CalendarQueryValidator(); foreach($objects as $object) { diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/BackendInterface.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php similarity index 96% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/BackendInterface.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php index ddfe5d8c0..e2a034240 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/BackendInterface.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php @@ -1,13 +1,13 @@ getValue()); } @@ -255,16 +255,16 @@ class PDO extends AbstractBackend { break; default : // Checking the property map - if (!isset($this->propertyMap[$propertyName])) { + if (isset($this->propertyMap[$propertyName])) { // We don't know about this property. $hasError = true; $result[403][$propertyName] = null; unset($mutations[$propertyName]); - break; - } + } else { - $fieldName = $this->propertyMap[$propertyName]; - $newValues[$fieldName] = $propertyValue; + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + } } } @@ -497,7 +497,7 @@ class PDO extends AbstractBackend { } } if (!$componentType) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); + throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); } if ($componentType === 'VEVENT') { $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); @@ -572,7 +572,7 @@ class PDO extends AbstractBackend { * query. * * The list of filters are specified as an array. The exact array is - * documented by \SabreForRainLoop\CalDAV\CalendarQueryParser. + * documented by \Sabre\CalDAV\CalendarQueryParser. * * Note that it is extremely likely that getCalendarObject for every path * returned from this method will be called almost immediately after. You @@ -601,7 +601,7 @@ class PDO extends AbstractBackend { * time-range filter specified on a VEVENT must for instance also handle * recurrence rules correctly. * A good example of how to interprete all these filters can also simply - * be found in \SabreForRainLoop\CalDAV\CalendarQueryFilter. This class is as correct + * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct * as possible, so it gives you a good idea on what type of stuff you need * to think of. * @@ -615,7 +615,7 @@ class PDO extends AbstractBackend { public function calendarQuery($calendarId, array $filters) { $result = array(); - $validator = new \SabreForRainLoop\CalDAV\CalendarQueryValidator(); + $validator = new \Sabre\CalDAV\CalendarQueryValidator(); $componentType = null; $requirePostFilter = true; diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/SharingSupport.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php similarity index 94% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/SharingSupport.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php index 3bfcc145d..2d1e3662e 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Backend/SharingSupport.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php @@ -1,6 +1,6 @@ xpath->query('/cal:calendar-query/cal:filter'); if ($filter->length !== 1) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('Only one filter element is allowed'); + throw new \Sabre\DAV\Exception\BadRequest('Only one filter element is allowed'); } $compFilters = $this->parseCompFilters($filter->item(0)); if (count($compFilters)!==1) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.'); + throw new \Sabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.'); } $this->filters = $compFilters[0]; - $this->requestedProperties = array_keys(\SabreForRainLoop\DAV\XMLUtil::parseProperties($this->dom->firstChild)); + $this->requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($this->dom->firstChild)); $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); if ($expand->length>0) { @@ -132,7 +132,7 @@ class CalendarQueryParser { 'VFREEBUSY', 'VALARM', ))) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); + throw new \Sabre\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); }; $result[] = $compFilter; @@ -253,7 +253,7 @@ class CalendarQueryParser { } if (!is_null($start) && !is_null($end) && $end <= $start) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter'); + throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter'); } return array( @@ -273,19 +273,19 @@ class CalendarQueryParser { $start = $parentNode->getAttribute('start'); if(!$start) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element'); + throw new \Sabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element'); } $start = VObject\DateTimeParser::parseDateTime($start); $end = $parentNode->getAttribute('end'); if(!$end) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element'); + throw new \Sabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element'); } $end = VObject\DateTimeParser::parseDateTime($end); if ($end <= $start) { - throw new \SabreForRainLoop\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); + throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); } return array( diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/CalendarQueryValidator.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php similarity index 94% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/CalendarQueryValidator.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php index 4537d3bf5..c9cb46338 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/CalendarQueryValidator.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php @@ -1,8 +1,8 @@ name . ' components'); + throw new \Sabre\DAV\Exception\NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); case 'COMPLETED' : case 'CREATED' : @@ -383,7 +383,7 @@ class CalendarQueryValidator { default : - throw new \SabreForRainLoop\DAV\Exception\BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); + throw new \Sabre\DAV\Exception\BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); } diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/CalendarRootNode.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php similarity index 78% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/CalendarRootNode.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php index 69e01b1a4..905155a60 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/CalendarRootNode.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php @@ -1,8 +1,8 @@ xmlNamespaces[self::NS_CALDAV] = 'cal'; $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; - $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'SabreForRainLoop\\CalDAV\\Property\\SupportedCalendarComponentSet'; - $server->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'SabreForRainLoop\\CalDAV\\Property\\ScheduleCalendarTransp'; + $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Property\\SupportedCalendarComponentSet'; + $server->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Property\\ScheduleCalendarTransp'; - $server->resourceTypeMapping['\\SabreForRainLoop\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; - $server->resourceTypeMapping['\\SabreForRainLoop\\CalDAV\\Schedule\\IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; - $server->resourceTypeMapping['\\SabreForRainLoop\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; - $server->resourceTypeMapping['\\SabreForRainLoop\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; - $server->resourceTypeMapping['\\SabreForRainLoop\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; array_push($server->protectedProperties, @@ -286,7 +286,7 @@ class Plugin extends DAV\ServerPlugin { // for clients matching iCal in the user agent //$ua = $this->server->httpRequest->getHeader('User-Agent'); //if (strpos($ua,'iCal/')!==false) { - // throw new \SabreForRainLoop\DAV\Exception\Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); + // throw new \Sabre\DAV\Exception\Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); //} $body = $this->server->httpRequest->getBody(true); diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Principal/Collection.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php similarity index 75% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Principal/Collection.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php index e4559c878..8de530bcb 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/Principal/Collection.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php @@ -1,7 +1,7 @@ server = $server; - $server->resourceTypeMapping['SabreForRainLoop\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared'; + $server->resourceTypeMapping['Sabre\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared'; array_push( $this->server->protectedProperties, diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/UserCalendars.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php similarity index 95% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/UserCalendars.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php index 17f7956f0..e2bc36f36 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CalDAV/UserCalendars.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php @@ -1,23 +1,23 @@ xmlNamespaces[self::NS_CARDDAV] = 'card'; /* Mapping Interfaces to {DAV:}resourcetype values */ - $server->resourceTypeMapping['SabreForRainLoop\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; - $server->resourceTypeMapping['SabreForRainLoop\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; + $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; + $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; /* Adding properties that may never be changed */ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; @@ -73,7 +73,7 @@ class Plugin extends DAV\ServerPlugin { $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; - $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'SabreForRainLoop\\DAV\\Property\\Href'; + $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Property\\Href'; $this->server = $server; @@ -654,7 +654,7 @@ class Plugin extends DAV\ServerPlugin { /** * This method is used to generate HTML output for the - * SabreForRainLoop\DAV\Browser\Plugin. This allows us to generate an interface users + * Sabre\DAV\Browser\Plugin. This allows us to generate an interface users * can use to create new calendars. * * @param DAV\INode $node diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CardDAV/Property/SupportedAddressData.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php similarity index 87% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CardDAV/Property/SupportedAddressData.php rename to snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php index 084c06f32..39433234a 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/CardDAV/Property/SupportedAddressData.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php @@ -1,9 +1,9 @@ 'icons/file', - 'SabreForRainLoop\\DAV\\ICollection' => 'icons/collection', - 'SabreForRainLoop\\DAVACL\\IPrincipal' => 'icons/principal', - 'SabreForRainLoop\\CalDAV\\ICalendar' => 'icons/calendar', - 'SabreForRainLoop\\CardDAV\\IAddressBook' => 'icons/addressbook', - 'SabreForRainLoop\\CardDAV\\ICard' => 'icons/card', + 'Sabre\\DAV\\IFile' => 'icons/file', + 'Sabre\\DAV\\ICollection' => 'icons/collection', + 'Sabre\\DAVACL\\IPrincipal' => 'icons/principal', + 'Sabre\\CalDAV\\ICalendar' => 'icons/calendar', + 'Sabre\\CardDAV\\IAddressBook' => 'icons/addressbook', + 'Sabre\\CardDAV\\ICard' => 'icons/card', ); /** @@ -49,7 +49,7 @@ class Plugin extends DAV\ServerPlugin { /** * reference to server class * - * @var SabreForRainLoop\DAV\Server + * @var Sabre\DAV\Server */ protected $server; @@ -374,7 +374,7 @@ class Plugin extends DAV\ServerPlugin { $html.=$output; $html.= " -
Generated by SabreDAV " . $version . " (c)2007-2013 http://code.google.com/p/sabredav/
+
Generated by SabreDAV " . $version . " (c)2007-2015 http://sabre.io/
"; @@ -400,7 +400,7 @@ class Plugin extends DAV\ServerPlugin { // We also know fairly certain that if an object is a non-extended // SimpleCollection, we won't need to show the panel either. - if (get_class($node)==='SabreForRainLoop\\DAV\\SimpleCollection') + if (get_class($node)==='Sabre\\DAV\\SimpleCollection') return; $output.= '
diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/favicon.ico b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/favicon.ico similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/favicon.ico rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/favicon.ico diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/addressbook.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/addressbook.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/addressbook.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/addressbook.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/calendar.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/calendar.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/calendar.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/calendar.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/card.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/card.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/card.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/card.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/collection.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/collection.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/collection.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/collection.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/file.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/file.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/file.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/file.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/parent.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/parent.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/parent.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/parent.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/principal.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/principal.png similarity index 100% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Browser/assets/icons/principal.png rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/principal.png diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Client.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php similarity index 82% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Client.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php index b751993e8..2d7ea4040 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Client.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php @@ -1,6 +1,6 @@ authType = self::AUTH_BASIC | self::AUTH_DIGEST; } - $this->propertyMap['{DAV:}resourcetype'] = 'SabreForRainLoop\\DAV\\Property\\ResourceType'; + $this->propertyMap['{DAV:}resourcetype'] = 'Sabre\\DAV\\Property\\ResourceType'; } @@ -149,7 +149,7 @@ class Client { * @param int $depth * @return array */ - public function propFind($url, array $properties, $depth = 0) : array { + public function propFind($url, array $properties, $depth = 0) { $body = '' . "\n"; $body.= '' . "\n"; @@ -304,8 +304,9 @@ class Client { * @param string $url * @param string $body * @param array $headers + * @return array */ - public function request($method, $url = '', $body = null, $headers = array()) : array { + public function request($method, $url = '', $body = null, $headers = array()) { $url = $this->getAbsoluteUrl($url); @@ -313,22 +314,29 @@ class Client { CURLOPT_RETURNTRANSFER => true, // Return headers as part of the response CURLOPT_HEADER => true, - CURLOPT_POSTFIELDS => $body, - CURLOPT_USERAGENT => 'SnappyMail DAV Client', // TODO rainloop + + // For security we cast this to a string. If somehow an array could + // be passed here, it would be possible for an attacker to use @ to + // post local files. + CURLOPT_POSTFIELDS => (string)$body, + CURLOPT_USERAGENT => 'SnappyMail DAV Client', // TODO rainloop // Automatically follow redirects CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, + CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, + CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, ); if($this->verifyPeer !== null) { $curlSettings[CURLOPT_SSL_VERIFYPEER] = $this->verifyPeer; - // TODO rainloop - if (!$this->verifyPeer) { - $curlSettings[CURLOPT_SSL_VERIFYHOST] = 0; - } // --- + // TODO rainloop + if (!$this->verifyPeer) { + $curlSettings[CURLOPT_SSL_VERIFYHOST] = 0; + } + // END rainloop } - if($this->trustedCertificates) { + if($this->trustedCertificates) { $curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates; } @@ -375,9 +383,6 @@ class Client { $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password; } -// var_dump($url); -// var_dump($curlSettings); - list( $response, $curlInfo, @@ -385,12 +390,9 @@ class Client { $curlError ) = $this->curlRequest($url, $curlSettings); -// var_dump($response); - $headerBlob = substr($response, 0, $curlInfo['header_size']); $response = substr($response, $curlInfo['header_size']); - // In the case of 100 Continue, or redirects we'll have multiple lists // of headers for each separate HTTP response. We can easily split this // because they are separated by \r\n\r\n @@ -417,7 +419,7 @@ class Client { ); if ($curlErrNo) { - throw new Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ', uri: ' . $url . ')'); + throw new Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')'); } if ($response['statusCode']>=400) { @@ -468,70 +470,70 @@ class Client { // @codeCoverageIgnoreStart protected function curlRequest($url, $settings) { - // TODO rainloop + // TODO rainloop $curl = curl_init($url); - if (ini_get('open_basedir') === '') - { - curl_setopt_array($curl, $settings); - $data = curl_exec($curl); - } - else - { - $settings[CURLOPT_FOLLOWLOCATION] = false; - curl_setopt_array($curl, $settings); + if (ini_get('open_basedir') === '') + { + curl_setopt_array($curl, $settings); + $data = curl_exec($curl); + } + else + { + $settings[CURLOPT_FOLLOWLOCATION] = false; + curl_setopt_array($curl, $settings); - $max_redirects = isset($settings[CURLOPT_MAXREDIRS]) ? $settings[CURLOPT_MAXREDIRS] : 5; - $mr = $max_redirects; - if ($mr > 0) - { - $newurl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); + $max_redirects = isset($settings[CURLOPT_MAXREDIRS]) ? $settings[CURLOPT_MAXREDIRS] : 5; + $mr = $max_redirects; + if ($mr > 0) + { + $newurl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); - $rcurl = curl_copy_handle($curl); - curl_setopt($rcurl, CURLOPT_HEADER, true); - curl_setopt($rcurl, CURLOPT_NOBODY, true); - curl_setopt($rcurl, CURLOPT_FORBID_REUSE, false); - curl_setopt($rcurl, CURLOPT_RETURNTRANSFER, true); - do - { - curl_setopt($rcurl, CURLOPT_URL, $newurl); - $header = curl_exec($rcurl); - if (curl_errno($rcurl)) - { - $code = 0; - } - else - { - $code = curl_getinfo($rcurl, CURLINFO_HTTP_CODE); - if ($code == 301 || $code == 302) - { - $matches = array(); - preg_match('/Location:(.*?)\n/', $header, $matches); - $newurl = trim(array_pop($matches)); - } - else - { - $code = 0; - } - } - } while ($code && --$mr); + $rcurl = curl_copy_handle($curl); + curl_setopt($rcurl, CURLOPT_HEADER, true); + curl_setopt($rcurl, CURLOPT_NOBODY, true); + curl_setopt($rcurl, CURLOPT_FORBID_REUSE, false); + curl_setopt($rcurl, CURLOPT_RETURNTRANSFER, true); + do + { + curl_setopt($rcurl, CURLOPT_URL, $newurl); + $header = curl_exec($rcurl); + if (curl_errno($rcurl)) + { + $code = 0; + } + else + { + $code = curl_getinfo($rcurl, CURLINFO_HTTP_CODE); + if ($code == 301 || $code == 302) + { + $matches = array(); + preg_match('/Location:(.*?)\n/', $header, $matches); + $newurl = trim(array_pop($matches)); + } + else + { + $code = 0; + } + } + } while ($code && --$mr); - curl_close($rcurl); - if ($mr > 0) - { - curl_setopt($curl, CURLOPT_URL, $newurl); - } - } + curl_close($rcurl); + if ($mr > 0) + { + curl_setopt($curl, CURLOPT_URL, $newurl); + } + } - if ($mr == 0 && $max_redirects > 0) - { - $data = false; - } - else - { - $data = curl_exec($curl); - } - } + if ($mr == 0 && $max_redirects > 0) + { + $data = false; + } + else + { + $data = curl_exec($curl); + } + } return array( $data, @@ -539,6 +541,17 @@ class Client { curl_errno($curl), curl_error($curl) ); + // END rainloop + + $curl = curl_init($url); + curl_setopt_array($curl, $settings); + + return array( + curl_exec($curl), + curl_getinfo($curl), + curl_errno($curl), + curl_error($curl) + ); } // @codeCoverageIgnoreEnd @@ -598,7 +611,12 @@ class Client { $body = XMLUtil::convertDAVNamespace($body); + // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or + // 5.4.13. + $previous = libxml_disable_entity_loader(true); $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA); + libxml_disable_entity_loader($previous); + if ($responseXML===false) { throw new \InvalidArgumentException('The passed data is not valid XML'); } diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Collection.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Collection.php similarity index 91% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Collection.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Collection.php index 1e070e76c..caec02933 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Collection.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Collection.php @@ -1,6 +1,6 @@ ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); $errorNode->appendChild($error); diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php new file mode 100644 index 000000000..d2a5a8e82 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php @@ -0,0 +1,30 @@ +getAllowedMethods($server->getRequestUri()); diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/NotAuthenticated.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php similarity index 65% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/NotAuthenticated.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php index 99e2ad262..26eb61d3d 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/NotAuthenticated.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php @@ -1,8 +1,8 @@ - * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/). - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). + * @license http://sabre.io/license/ Modified BSD License */ class ServiceUnavailable extends DAV\Exception { diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/UnsupportedMediaType.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php similarity index 62% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/UnsupportedMediaType.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php index 05b245d91..990ca0ccb 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Exception/UnsupportedMediaType.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php @@ -1,6 +1,6 @@ path, 'c'); - fseek($f,$offset-1); + switch($rangeType) { + case 1 : + $f = fopen($this->path, 'a'); + break; + case 2 : + $f = fopen($this->path, 'c'); + fseek($f,$offset); + break; + case 3 : + $f = fopen($this->path, 'c'); + fseek($f, $offset, SEEK_END); + break; + } if (is_string($data)) { fwrite($f, $data); } else { diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/FSExt/Node.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php similarity index 95% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/FSExt/Node.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php index 17cd32c81..73467fffc 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/FSExt/Node.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php @@ -1,17 +1,17 @@ addPlugin($lockPlugin); * - * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/). + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + * @license http://sabre.io/license/ Modified BSD License */ class Plugin extends DAV\ServerPlugin { @@ -30,7 +30,7 @@ class Plugin extends DAV\ServerPlugin { /** * server * - * @var SabreForRainLoop\DAV\Server + * @var Sabre\DAV\Server */ protected $server; @@ -66,7 +66,7 @@ class Plugin extends DAV\ServerPlugin { * Returns a plugin name. * * Using this name other plugins will be able to access other plugins - * using SabreForRainLoop\DAV\Server::getPlugin + * using Sabre\DAV\Server::getPlugin * * @return string */ @@ -216,7 +216,7 @@ class Plugin extends DAV\ServerPlugin { /** * Returns all lock information on a particular uri * - * This function should return an array with SabreForRainLoop\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array. + * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array. * * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object @@ -449,7 +449,7 @@ class Plugin extends DAV\ServerPlugin { * It will check if the requested url is locked, and see if the correct lock tokens are passed * * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri - * @param mixed $lastLock This variable will be populated with the last checked lock object (SabreForRainLoop\DAV\Locks\LockInfo) + * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre\DAV\Locks\LockInfo) * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees. * @return bool */ @@ -503,7 +503,7 @@ class Plugin extends DAV\ServerPlugin { $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); $node = $this->server->tree->getNodeForPath($uri); - $etagValid = $node->getETag()==$conditionToken[2]; + $etagValid = $node instanceof DAV\IFile && $node->getETag()==$conditionToken[2]; } @@ -614,17 +614,24 @@ class Plugin extends DAV\ServerPlugin { } /** - * Parses a webdav lock xml body, and returns a new SabreForRainLoop\DAV\Locks\LockInfo object + * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object * * @param string $body * @return DAV\Locks\LockInfo */ protected function parseLockRequest($body) { + // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or + // 5.4.13. + $previous = libxml_disable_entity_loader(true); + + $xml = simplexml_load_string( DAV\XMLUtil::convertDAVNamespace($body), null, LIBXML_NOWARNING); + libxml_disable_entity_loader($previous); + $xml->registerXPathNamespace('d','urn:DAV'); $lockInfo = new LockInfo(); diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Mount/Plugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php similarity index 88% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Mount/Plugin.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php index 4959f012b..71cbe6e29 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Mount/Plugin.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php @@ -1,24 +1,24 @@ addPlugin($patchPlugin); * - * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/). + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + * @license http://sabre.io/license/ Modified BSD License */ class Plugin extends DAV\ServerPlugin { + const RANGE_APPEND = 1; + const RANGE_START = 2; + const RANGE_END = 3; + /** * Reference to server * - * @var SabreForRainLoop\DAV\Server + * @var Sabre\DAV\Server */ protected $server; @@ -94,13 +98,13 @@ class Plugin extends DAV\ServerPlugin { public function getHTTPMethods($uri) { $tree = $this->server->tree; - - if ($tree->nodeExists($uri) && - $tree->getNodeForPath($uri) instanceof IFile) { - return array('PATCH'); - } - - return array(); + if ($tree->nodeExists($uri)) { + $node = $tree->getNodeForPath($uri); + if ($node instanceof IFile || $node instanceof IPatchSupport) { + return array('PATCH'); + } + } + return array(); } @@ -129,7 +133,7 @@ class Plugin extends DAV\ServerPlugin { // Get the node. Will throw a 404 if not found $node = $this->server->tree->getNodeForPath($uri); - if (!($node instanceof IFile)) { + if (!$node instanceof IFile && !$node instanceof IPatchSupport) { throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); } @@ -148,17 +152,23 @@ class Plugin extends DAV\ServerPlugin { } $len = $this->server->httpRequest->getHeader('Content-Length'); + if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required'); - // Load the begin and end data - $start = ($range[0])?$range[0]:0; - $end = ($range[1])?$range[1]:$len-1; - - // Check consistency - if($end < $start) - throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); - if($end - $start + 1 != $len) - throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets'); - + switch($range[0]) { + case self::RANGE_START : + // Calculate the end-range if it doesn't exist. + if (!$range[2]) { + $range[2] = $range[1] + $len - 1; + } else { + if ($range[2] < $range[1]) { + throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); + } + if($range[2] - $range[1] + 1 != $len) { + throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); + } + } + break; + } // Checking If-None-Match and related headers. if (!$this->server->checkPreconditions()) return; @@ -166,7 +176,23 @@ class Plugin extends DAV\ServerPlugin { return; $body = $this->server->httpRequest->getBody(); - $etag = $node->putRange($body, $start-1); + + + if ($node instanceof IPatchSupport) { + $etag = $node->patch($body, $range[0], isset($range[1])?$range[1]:null); + } else { + // The old interface + switch($range[0]) { + case self::RANGE_APPEND : + throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport'); + case self::RANGE_START : + $etag = $node->putRange($body, $range[1]); + break; + case self::RANGE_END : + throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport'); + break; + } + } $this->server->broadcastEvent('afterWriteContent',array($uri, $node)); @@ -182,13 +208,18 @@ class Plugin extends DAV\ServerPlugin { * Returns the HTTP custom range update header * * This method returns null if there is no well-formed HTTP range request - * header or array($start, $end). + * header. It returns array(1) if it was an append request, array(2, + * $start, $end) if it's a start and end range, lastly it's array(3, + * $endoffset) if the offset was negative, and should be calculated from + * the end of the file. * - * The first number is the offset of the first byte in the range. - * The second number is the offset of the last byte in the range. + * Examples: * - * If the second offset is null, it should be treated as the offset of the last byte of the entity - * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * null - invalid + * array(1) - append + * array(2,10,15) - update bytes 10, 11, 12, 13, 14, 15 + * array(2,10,null) - update bytes 10 until the end of the patch body + * array(3,-5) - update from 5 bytes from the end of the file. * * @return array|null */ @@ -199,14 +230,17 @@ class Plugin extends DAV\ServerPlugin { // Matching "Range: bytes=1234-5678: both numbers are optional - if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; + if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null; - if ($matches[1]==='' && $matches[2]==='') return null; - - return array( - $matches[1]!==''?$matches[1]:null, - $matches[2]!==''?$matches[2]:null, - ); + if ($matches[1]==='append') { + return array(self::RANGE_APPEND); + } elseif (strlen($matches[2])>0) { + return array(self::RANGE_START, $matches[2], $matches[3]?:null); + } elseif ($matches[4]) { + return array(self::RANGE_END, $matches[4]); + } else { + return null; + } } } diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Property.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php similarity index 75% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Property.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php index 36d4ea694..b2d5eb247 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Property.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php @@ -1,15 +1,15 @@ responses = $responses; diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Property/SupportedLock.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php similarity index 90% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Property/SupportedLock.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php index 5ddde6478..3898ecee6 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/Property/SupportedLock.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php @@ -1,8 +1,8 @@ 'SabreForRainLoop\\DAV\\Property\\ResourceType', + '{DAV:}resourcetype' => 'Sabre\\DAV\\Property\\ResourceType', ); public $protectedProperties = array( @@ -131,12 +131,12 @@ class Server { * based on a node's classname or interface. * * The preset ensures that {DAV:}collection is automaticlly added for nodes - * implementing SabreForRainLoop\DAV\ICollection. + * implementing Sabre\DAV\ICollection. * * @var array */ public $resourceTypeMapping = array( - 'SabreForRainLoop\\DAV\\ICollection' => '{DAV:}collection', + 'Sabre\\DAV\\ICollection' => '{DAV:}collection', ); /** @@ -152,12 +152,12 @@ class Server { /** * Sets up the server * - * If a SabreForRainLoop\DAV\Tree object is passed as an argument, it will - * use it as the directory tree. If a SabreForRainLoop\DAV\INode is passed, it - * will create a SabreForRainLoop\DAV\ObjectTree and use the node as the root. + * If a Sabre\DAV\Tree object is passed as an argument, it will + * use it as the directory tree. If a Sabre\DAV\INode is passed, it + * will create a Sabre\DAV\ObjectTree and use the node as the root. * - * If nothing is passed, a SabreForRainLoop\DAV\SimpleCollection is created in - * a SabreForRainLoop\DAV\ObjectTree. + * If nothing is passed, a Sabre\DAV\SimpleCollection is created in + * a Sabre\DAV\ObjectTree. * * If an array is passed, we automatically create a root node, and use * the nodes in the array as top-level children. @@ -176,7 +176,7 @@ class Server { // create the root node. foreach($treeOrNode as $node) { if (!($node instanceof INode)) { - throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement SabreForRainLoop\\DAV\\INode'); + throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre\\DAV\\INode'); } } @@ -187,7 +187,7 @@ class Server { $root = new SimpleCollection('root'); $this->tree = new ObjectTree($root); } else { - throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of SabreForRainLoop\\DAV\\Tree, SabreForRainLoop\\DAV\\INode, an array or null'); + throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null'); } $this->httpResponse = new HTTP\Response(); $this->httpRequest = new HTTP\Request(); @@ -340,7 +340,7 @@ class Server { /** * Adds a plugin to the server * - * For more information, console the documentation of SabreForRainLoop\DAV\ServerPlugin + * For more information, console the documentation of Sabre\DAV\ServerPlugin * * @param ServerPlugin $plugin * @return void @@ -684,6 +684,9 @@ class Server { */ protected function httpDelete($uri) { + // Checking If-None-Match and related headers. + if (!$this->checkPreconditions()) return; + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; $this->tree->delete($uri); $this->broadcastEvent('afterUnbind',array($uri)); @@ -871,13 +874,13 @@ class Server { } + // Checking If-None-Match and related headers. + if (!$this->checkPreconditions()) return; + if ($this->tree->nodeExists($uri)) { $node = $this->tree->getNodeForPath($uri); - // Checking If-None-Match and related headers. - if (!$this->checkPreconditions()) return; - // If the node is a collection, we'll deny it if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.'); if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false; @@ -1157,7 +1160,7 @@ class Server { /** * Returns the HTTP depth header * - * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the SabreForRainLoop\DAV\Server::DEPTH_INFINITY object + * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent * * @param mixed $default @@ -1820,7 +1823,7 @@ class Server { )); } - // If the node is not an instance of SabreForRainLoop\DAV\IProperties, every + // If the node is not an instance of Sabre\DAV\IProperties, every // property is 403 Forbidden if (!$hasError && count($remainingProperties) && !($node instanceof IProperties)) { $hasError = true; diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/ServerPlugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php similarity index 85% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/ServerPlugin.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php index 83cde5756..d232cfe4b 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/ServerPlugin.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php @@ -1,22 +1,22 @@ name = $name; foreach($children as $child) { - if (!($child instanceof INode)) throw new Exception('Only instances of SabreForRainLoop\DAV\INode are allowed to be passed in the children argument'); + if (!($child instanceof INode)) throw new Exception('Only instances of Sabre\DAV\INode are allowed to be passed in the children argument'); $this->addChild($child); } @@ -78,7 +78,7 @@ class SimpleCollection extends Collection { * This method makes use of the getChildren method to grab all the child nodes, and compares the name. * Generally its wise to override this, as this can usually be optimized * - * This method must throw SabreForRainLoop\DAV\Exception\NotFound if the node does not + * This method must throw Sabre\DAV\Exception\NotFound if the node does not * exist. * * @param string $name diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/SimpleFile.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php similarity index 93% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/SimpleFile.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php index d1b2d7e3a..ede3ff48e 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAV/SimpleFile.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php @@ -1,6 +1,6 @@ server->getPlugin('auth'); if (is_null($authPlugin)) return null; - /** @var $authPlugin SabreForRainLoop\DAV\Auth\Plugin */ + /** @var $authPlugin Sabre\DAV\Auth\Plugin */ $userName = $authPlugin->getCurrentUser(); if (!$userName) return null; @@ -599,7 +599,7 @@ class Plugin extends DAV\ServerPlugin { * If this is ommitted, the standard * principal collection-set will be used. * @return array This method returns an array structure similar to - * SabreForRainLoop\DAV\Server::getPropertiesForPath. Returned + * Sabre\DAV\Server::getPropertiesForPath. Returned * properties are index by a HTTP status code. * */ @@ -678,11 +678,11 @@ class Plugin extends DAV\ServerPlugin { // Automatically mapping nodes implementing IPrincipal to the // {DAV:}principal resourcetype. - $server->resourceTypeMapping['SabreForRainLoop\\DAVACL\\IPrincipal'] = '{DAV:}principal'; + $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; // Mapping the group-member-set property to the HrefList property // class. - $server->propertyMap['{DAV:}group-member-set'] = 'SabreForRainLoop\\DAV\\Property\\HrefList'; + $server->propertyMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Property\\HrefList'; } @@ -965,7 +965,7 @@ class Plugin extends DAV\ServerPlugin { $propertyDelta['{DAV:}group-member-set']->getHrefs() ); } else { - throw new DAV\Exception('The group-member-set property MUST be an instance of SabreForRainLoop\DAV\Property\HrefList or null'); + throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null'); } if (!($node instanceof IPrincipal)) { diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAVACL/Principal.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php similarity index 94% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAVACL/Principal.php rename to snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php index e99ec737d..7270aec39 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/DAVACL/Principal.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php @@ -1,8 +1,8 @@ httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { + if (($user = $this->httpRequest->getRawServerValue('PHP_AUTH_USER'))!==null && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))!==null) { return array($user,$pass); diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/HTTP/DigestAuth.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php similarity index 95% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/HTTP/DigestAuth.php rename to snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php index 62310f7ce..c15273939 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/HTTP/DigestAuth.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php @@ -1,6 +1,6 @@ children[] = $property; } } else { - throw new \InvalidArgumentException('You must pass a \\SabreForRainLoop\\VObject\\Component, \\SabreForRainLoop\\VObject\\Property or scalar type'); + throw new \InvalidArgumentException('You must pass a \\Sabre\\VObject\\Component, \\Sabre\\VObject\\Property or scalar type'); } } diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VAlarm.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php similarity index 98% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VAlarm.php rename to snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php index 6951c1996..2f86c44fd 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VAlarm.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VAlarm.php @@ -1,7 +1,7 @@ 'SabreForRainLoop\\VObject\\Component\\VEvent', - 'VFREEBUSY' => 'SabreForRainLoop\\VObject\\Component\\VFreeBusy', - 'VJOURNAL' => 'SabreForRainLoop\\VObject\\Component\\VJournal', - 'VTODO' => 'SabreForRainLoop\\VObject\\Component\\VTodo', - 'VALARM' => 'SabreForRainLoop\\VObject\\Component\\VAlarm', + 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent', + 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy', + 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal', + 'VTODO' => 'Sabre\\VObject\\Component\\VTodo', + 'VALARM' => 'Sabre\\VObject\\Component\\VAlarm', ); /** @@ -45,21 +45,21 @@ class VCalendar extends VObject\Document { * @var array */ static public $valueMap = array( - 'BINARY' => 'SabreForRainLoop\\VObject\\Property\\Binary', - 'BOOLEAN' => 'SabreForRainLoop\\VObject\\Property\\Boolean', - 'CAL-ADDRESS' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\CalAddress', - 'DATE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Date', - 'DATE-TIME' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'DURATION' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Duration', - 'FLOAT' => 'SabreForRainLoop\\VObject\\Property\\FloatValue', - 'INTEGER' => 'SabreForRainLoop\\VObject\\Property\\Integer', - 'PERIOD' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Period', - 'RECUR' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Recur', - 'TEXT' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'TIME' => 'SabreForRainLoop\\VObject\\Property\\Time', - 'UNKNOWN' => 'SabreForRainLoop\\VObject\\Property\\Unknown', // jCard / jCal-only. - 'URI' => 'SabreForRainLoop\\VObject\\Property\\Uri', - 'UTC-OFFSET' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset', + 'BINARY' => 'Sabre\\VObject\\Property\\Binary', + 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean', + 'CAL-ADDRESS' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress', + 'DATE' => 'Sabre\\VObject\\Property\\ICalendar\\Date', + 'DATE-TIME' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration', + 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue', + 'INTEGER' => 'Sabre\\VObject\\Property\\Integer', + 'PERIOD' => 'Sabre\\VObject\\Property\\ICalendar\\Period', + 'RECUR' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', + 'TEXT' => 'Sabre\\VObject\\Property\\Text', + 'TIME' => 'Sabre\\VObject\\Property\\Time', + 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only. + 'URI' => 'Sabre\\VObject\\Property\\Uri', + 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset', ); /** @@ -69,75 +69,75 @@ class VCalendar extends VObject\Document { */ static public $propertyMap = array( // Calendar properties - 'CALSCALE' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'METHOD' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'PRODID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'VERSION' => 'SabreForRainLoop\\VObject\\Property\\FlatText', + 'CALSCALE' => 'Sabre\\VObject\\Property\\FlatText', + 'METHOD' => 'Sabre\\VObject\\Property\\FlatText', + 'PRODID' => 'Sabre\\VObject\\Property\\FlatText', + 'VERSION' => 'Sabre\\VObject\\Property\\FlatText', // Component properties - 'ATTACH' => 'SabreForRainLoop\\VObject\\Property\\Binary', - 'CATEGORIES' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'CLASS' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'COMMENT' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'DESCRIPTION' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'GEO' => 'SabreForRainLoop\\VObject\\Property\\FloatValue', - 'LOCATION' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'PERCENT-COMPLETE' => 'SabreForRainLoop\\VObject\\Property\\Integer', - 'PRIORITY' => 'SabreForRainLoop\\VObject\\Property\\Integer', - 'RESOURCES' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'STATUS' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'SUMMARY' => 'SabreForRainLoop\\VObject\\Property\\FlatText', + 'ATTACH' => 'Sabre\\VObject\\Property\\Binary', + 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text', + 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', + 'COMMENT' => 'Sabre\\VObject\\Property\\FlatText', + 'DESCRIPTION' => 'Sabre\\VObject\\Property\\FlatText', + 'GEO' => 'Sabre\\VObject\\Property\\FloatValue', + 'LOCATION' => 'Sabre\\VObject\\Property\\FlatText', + 'PERCENT-COMPLETE' => 'Sabre\\VObject\\Property\\Integer', + 'PRIORITY' => 'Sabre\\VObject\\Property\\Integer', + 'RESOURCES' => 'Sabre\\VObject\\Property\\Text', + 'STATUS' => 'Sabre\\VObject\\Property\\FlatText', + 'SUMMARY' => 'Sabre\\VObject\\Property\\FlatText', // Date and Time Component Properties - 'COMPLETED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'DTEND' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'DUE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'DTSTART' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'DURATION' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Duration', - 'FREEBUSY' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Period', - 'TRANSP' => 'SabreForRainLoop\\VObject\\Property\\FlatText', + 'COMPLETED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DTEND' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DUE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DTSTART' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration', + 'FREEBUSY' => 'Sabre\\VObject\\Property\\ICalendar\\Period', + 'TRANSP' => 'Sabre\\VObject\\Property\\FlatText', // Time Zone Component Properties - 'TZID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'TZNAME' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'TZOFFSETFROM' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset', - 'TZOFFSETTO' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset', - 'TZURL' => 'SabreForRainLoop\\VObject\\Property\\Uri', + 'TZID' => 'Sabre\\VObject\\Property\\FlatText', + 'TZNAME' => 'Sabre\\VObject\\Property\\FlatText', + 'TZOFFSETFROM' => 'Sabre\\VObject\\Property\\UtcOffset', + 'TZOFFSETTO' => 'Sabre\\VObject\\Property\\UtcOffset', + 'TZURL' => 'Sabre\\VObject\\Property\\Uri', // Relationship Component Properties - 'ATTENDEE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\CalAddress', - 'CONTACT' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'ORGANIZER' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\CalAddress', - 'RECURRENCE-ID' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'RELATED-TO' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'URL' => 'SabreForRainLoop\\VObject\\Property\\Uri', - 'UID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', + 'ATTENDEE' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress', + 'CONTACT' => 'Sabre\\VObject\\Property\\FlatText', + 'ORGANIZER' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress', + 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'RELATED-TO' => 'Sabre\\VObject\\Property\\FlatText', + 'URL' => 'Sabre\\VObject\\Property\\Uri', + 'UID' => 'Sabre\\VObject\\Property\\FlatText', // Recurrence Component Properties - 'EXDATE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'RDATE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'RRULE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Recur', - 'EXRULE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545 + 'EXDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'RDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'RRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', + 'EXRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545 // Alarm Component Properties - 'ACTION' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'REPEAT' => 'SabreForRainLoop\\VObject\\Property\\Integer', - 'TRIGGER' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Duration', + 'ACTION' => 'Sabre\\VObject\\Property\\FlatText', + 'REPEAT' => 'Sabre\\VObject\\Property\\Integer', + 'TRIGGER' => 'Sabre\\VObject\\Property\\ICalendar\\Duration', // Change Management Component Properties - 'CREATED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'DTSTAMP' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'LAST-MODIFIED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'SEQUENCE' => 'SabreForRainLoop\\VObject\\Property\\Integer', + 'CREATED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DTSTAMP' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'SEQUENCE' => 'Sabre\\VObject\\Property\\Integer', // Request Status - 'REQUEST-STATUS' => 'SabreForRainLoop\\VObject\\Property\\Text', + 'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text', // Additions from draft-daboo-valarm-extensions-04 - 'ALARM-AGENT' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'ACKNOWLEDGED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime', - 'PROXIMITY' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'DEFAULT-ALARM' => 'SabreForRainLoop\\VObject\\Property\\Boolean', + 'ALARM-AGENT' => 'Sabre\\VObject\\Property\\Text', + 'ACKNOWLEDGED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'PROXIMITY' => 'Sabre\\VObject\\Property\\Text', + 'DEFAULT-ALARM' => 'Sabre\\VObject\\Property\\Boolean', ); diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VCard.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php similarity index 63% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VCard.php rename to snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php index 1390aca70..781158a87 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VCard.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VCard.php @@ -1,9 +1,9 @@ 'SabreForRainLoop\\VObject\\Property\\Binary', - 'BOOLEAN' => 'SabreForRainLoop\\VObject\\Property\\Boolean', - 'CONTENT-ID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // vCard 2.1 only - 'DATE' => 'SabreForRainLoop\\VObject\\Property\\VCard\\Date', - 'DATE-TIME' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateTime', - 'DATE-AND-OR-TIME' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only - 'FLOAT' => 'SabreForRainLoop\\VObject\\Property\\FloatValue', - 'INTEGER' => 'SabreForRainLoop\\VObject\\Property\\Integer', - 'LANGUAGE-TAG' => 'SabreForRainLoop\\VObject\\Property\\VCard\\LanguageTag', - 'TIMESTAMP' => 'SabreForRainLoop\\VObject\\Property\\VCard\\TimeStamp', - 'TEXT' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'TIME' => 'SabreForRainLoop\\VObject\\Property\\Time', - 'UNKNOWN' => 'SabreForRainLoop\\VObject\\Property\\Unknown', // jCard / jCal-only. - 'URI' => 'SabreForRainLoop\\VObject\\Property\\Uri', - 'URL' => 'SabreForRainLoop\\VObject\\Property\\Uri', // vCard 2.1 only - 'UTC-OFFSET' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset', + 'BINARY' => 'Sabre\\VObject\\Property\\Binary', + 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean', + 'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only + 'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date', + 'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime', + 'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only + 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue', + 'INTEGER' => 'Sabre\\VObject\\Property\\Integer', + 'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag', + 'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp', + 'TEXT' => 'Sabre\\VObject\\Property\\Text', + 'TIME' => 'Sabre\\VObject\\Property\\Time', + 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only. + 'URI' => 'Sabre\\VObject\\Property\\Uri', + 'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only + 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset', ); /** @@ -65,53 +65,53 @@ class VCard extends VObject\Document { static public $propertyMap = array( // vCard 2.1 properties and up - 'N' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'FN' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'PHOTO' => 'SabreForRainLoop\\VObject\\Property\\Binary', // Todo: we should add a class for Binary values. - 'BDAY' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateAndOrTime', - 'ADR' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'LABEL' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // Removed in vCard 4.0 - 'TEL' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'EMAIL' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'MAILER' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // Removed in vCard 4.0 - 'GEO' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'TITLE' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'ROLE' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'LOGO' => 'SabreForRainLoop\\VObject\\Property\\Binary', - // 'AGENT' => 'SabreForRainLoop\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so + 'N' => 'Sabre\\VObject\\Property\\Text', + 'FN' => 'Sabre\\VObject\\Property\\FlatText', + 'PHOTO' => 'Sabre\\VObject\\Property\\Binary', // Todo: we should add a class for Binary values. + 'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', + 'ADR' => 'Sabre\\VObject\\Property\\Text', + 'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 + 'TEL' => 'Sabre\\VObject\\Property\\FlatText', + 'EMAIL' => 'Sabre\\VObject\\Property\\FlatText', + 'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 + 'GEO' => 'Sabre\\VObject\\Property\\FlatText', + 'TITLE' => 'Sabre\\VObject\\Property\\FlatText', + 'ROLE' => 'Sabre\\VObject\\Property\\FlatText', + 'LOGO' => 'Sabre\\VObject\\Property\\Binary', + // 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so // not supported at the moment - 'ORG' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'NOTE' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'REV' => 'SabreForRainLoop\\VObject\\Property\\VCard\\TimeStamp', - 'SOUND' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'URL' => 'SabreForRainLoop\\VObject\\Property\\Uri', - 'UID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'VERSION' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'KEY' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'TZ' => 'SabreForRainLoop\\VObject\\Property\\Text', + 'ORG' => 'Sabre\\VObject\\Property\\Text', + 'NOTE' => 'Sabre\\VObject\\Property\\FlatText', + 'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp', + 'SOUND' => 'Sabre\\VObject\\Property\\FlatText', + 'URL' => 'Sabre\\VObject\\Property\\Uri', + 'UID' => 'Sabre\\VObject\\Property\\FlatText', + 'VERSION' => 'Sabre\\VObject\\Property\\FlatText', + 'KEY' => 'Sabre\\VObject\\Property\\FlatText', + 'TZ' => 'Sabre\\VObject\\Property\\Text', // vCard 3.0 properties - 'CATEGORIES' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'SORT-STRING' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'PRODID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'NICKNAME' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'CLASS' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // Removed in vCard 4.0 + 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text', + 'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText', + 'PRODID' => 'Sabre\\VObject\\Property\\FlatText', + 'NICKNAME' => 'Sabre\\VObject\\Property\\Text', + 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 // rfc2739 properties - 'FBURL' => 'SabreForRainLoop\\VObject\\Property\\Uri', - 'CAPURI' => 'SabreForRainLoop\\VObject\\Property\\Uri', - 'CALURI' => 'SabreForRainLoop\\VObject\\Property\\Uri', + 'FBURL' => 'Sabre\\VObject\\Property\\Uri', + 'CAPURI' => 'Sabre\\VObject\\Property\\Uri', + 'CALURI' => 'Sabre\\VObject\\Property\\Uri', // rfc4770 properties - 'IMPP' => 'SabreForRainLoop\\VObject\\Property\\Uri', + 'IMPP' => 'Sabre\\VObject\\Property\\Uri', // vCard 4.0 properties - 'XML' => 'SabreForRainLoop\\VObject\\Property\\FlatText', - 'ANNIVERSARY' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateAndOrTime', - 'CLIENTPIDMAP' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'LANG' => 'SabreForRainLoop\\VObject\\Property\\VCard\\LanguageTag', - 'GENDER' => 'SabreForRainLoop\\VObject\\Property\\Text', - 'KIND' => 'SabreForRainLoop\\VObject\\Property\\FlatText', + 'XML' => 'Sabre\\VObject\\Property\\FlatText', + 'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', + 'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text', + 'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag', + 'GENDER' => 'Sabre\\VObject\\Property\\Text', + 'KIND' => 'Sabre\\VObject\\Property\\FlatText', ); @@ -342,8 +342,8 @@ class VCard extends VObject\Document { $className = parent::getClassNameForPropertyName($propertyName); // In vCard 4, BINARY no longer exists, and we need URI instead. - if ($className == 'SabreForRainLoop\\VObject\\Property\\Binary' && $this->getDocumentType()===self::VCARD40) { - return 'SabreForRainLoop\\VObject\\Property\\Uri'; + if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType()===self::VCARD40) { + return 'Sabre\\VObject\\Property\\Uri'; } return $className; diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VEvent.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php similarity index 96% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VEvent.php rename to snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php index b1dc19331..0ce204566 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Component/VEvent.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Component/VEvent.php @@ -1,7 +1,7 @@ objects[] = $object; } else { - throw new \InvalidArgumentException('You can only pass strings or \\SabreForRainLoop\\VObject\\Component arguments to setObjects'); + throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects'); } } diff --git a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Node.php b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php similarity index 99% rename from snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Node.php rename to snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php index 3330fceff..3260a8c02 100644 --- a/snappymail/v/0.0.0/app/libraries/SabreForRainLoop/VObject/Node.php +++ b/snappymail/v/0.0.0/app/libraries/Sabre/VObject/Node.php @@ -1,6 +1,6 @@ Date: Fri, 2 Apr 2021 13:01:11 +0200 Subject: [PATCH 36/90] Remove most of Sabre No need for all DAV server code when only the Client is used And replace Sabre\DAV\UUIDUtil with better SnappyMail\UUID --- .../RainLoop/Providers/AddressBook.php | 2 +- .../Providers/AddressBook/Classes/Contact.php | 5 +- .../Sabre/CalDAV/Backend/AbstractBackend.php | 155 -- .../Sabre/CalDAV/Backend/BackendInterface.php | 233 -- .../CalDAV/Backend/NotificationSupport.php | 47 - .../libraries/Sabre/CalDAV/Backend/PDO.php | 691 ------ .../Sabre/CalDAV/Backend/SharingSupport.php | 243 -- .../app/libraries/Sabre/CalDAV/Calendar.php | 376 --- .../libraries/Sabre/CalDAV/CalendarObject.php | 279 --- .../Sabre/CalDAV/CalendarQueryParser.php | 298 --- .../Sabre/CalDAV/CalendarQueryValidator.php | 392 --- .../Sabre/CalDAV/CalendarRootNode.php | 77 - .../CalDAV/Exception/InvalidComponentType.php | 35 - .../Sabre/CalDAV/ICSExportPlugin.php | 142 -- .../app/libraries/Sabre/CalDAV/ICalendar.php | 36 - .../Sabre/CalDAV/ICalendarObject.php | 21 - .../Sabre/CalDAV/IShareableCalendar.php | 48 - .../Sabre/CalDAV/ISharedCalendar.php | 36 - .../Sabre/CalDAV/Notifications/Collection.php | 173 -- .../CalDAV/Notifications/ICollection.php | 24 - .../Sabre/CalDAV/Notifications/INode.php | 38 - .../Notifications/INotificationType.php | 44 - .../Sabre/CalDAV/Notifications/Node.php | 192 -- .../Notifications/Notification/Invite.php | 324 --- .../Notification/InviteReply.php | 218 -- .../Notification/SystemStatus.php | 182 -- .../app/libraries/Sabre/CalDAV/Plugin.php | 1338 ---------- .../Sabre/CalDAV/Principal/Collection.php | 32 - .../Sabre/CalDAV/Principal/IProxyRead.php | 19 - .../Sabre/CalDAV/Principal/IProxyWrite.php | 19 - .../Sabre/CalDAV/Principal/ProxyRead.php | 180 -- .../Sabre/CalDAV/Principal/ProxyWrite.php | 180 -- .../libraries/Sabre/CalDAV/Principal/User.php | 134 - .../CalDAV/Property/AllowedSharingModes.php | 74 - .../Sabre/CalDAV/Property/Invite.php | 227 -- .../Property/ScheduleCalendarTransp.php | 102 - .../SupportedCalendarComponentSet.php | 88 - .../CalDAV/Property/SupportedCalendarData.php | 40 - .../CalDAV/Property/SupportedCollationSet.php | 45 - .../libraries/Sabre/CalDAV/Schedule/IMip.php | 111 - .../Sabre/CalDAV/Schedule/IOutbox.php | 16 - .../Sabre/CalDAV/Schedule/Outbox.php | 163 -- .../Sabre/CalDAV/ShareableCalendar.php | 72 - .../libraries/Sabre/CalDAV/SharedCalendar.php | 116 - .../libraries/Sabre/CalDAV/SharingPlugin.php | 526 ---- .../libraries/Sabre/CalDAV/UserCalendars.php | 342 --- .../app/libraries/Sabre/CalDAV/Version.php | 24 - .../libraries/Sabre/CardDAV/AddressBook.php | 315 --- .../Sabre/CardDAV/AddressBookQueryParser.php | 221 -- .../Sabre/CardDAV/AddressBookRoot.php | 80 - .../Sabre/CardDAV/Backend/AbstractBackend.php | 18 - .../CardDAV/Backend/BackendInterface.php | 166 -- .../libraries/Sabre/CardDAV/Backend/PDO.php | 333 --- .../app/libraries/Sabre/CardDAV/Card.php | 260 -- .../libraries/Sabre/CardDAV/IAddressBook.php | 20 - .../app/libraries/Sabre/CardDAV/ICard.php | 20 - .../libraries/Sabre/CardDAV/IDirectory.php | 21 - .../app/libraries/Sabre/CardDAV/Plugin.php | 706 ------ .../CardDAV/Property/SupportedAddressData.php | 72 - .../Sabre/CardDAV/UserAddressBooks.php | 260 -- .../Sabre/CardDAV/VCFExportPlugin.php | 108 - .../app/libraries/Sabre/CardDAV/Version.php | 26 - .../Sabre/DAV/Auth/Backend/AbstractBasic.php | 87 - .../Sabre/DAV/Auth/Backend/AbstractDigest.php | 101 - .../Sabre/DAV/Auth/Backend/Apache.php | 63 - .../DAV/Auth/Backend/BackendInterface.php | 36 - .../libraries/Sabre/DAV/Auth/Backend/File.php | 77 - .../libraries/Sabre/DAV/Auth/Backend/PDO.php | 65 - .../app/libraries/Sabre/DAV/Auth/Plugin.php | 112 - .../Sabre/DAV/Browser/GuessContentType.php | 99 - .../Sabre/DAV/Browser/MapGetToPropFind.php | 57 - .../libraries/Sabre/DAV/Browser/Plugin.php | 491 ---- .../Sabre/DAV/Browser/assets/favicon.ico | Bin 4286 -> 0 bytes .../DAV/Browser/assets/icons/addressbook.png | Bin 7232 -> 0 bytes .../DAV/Browser/assets/icons/calendar.png | Bin 4388 -> 0 bytes .../Sabre/DAV/Browser/assets/icons/card.png | Bin 5695 -> 0 bytes .../DAV/Browser/assets/icons/collection.png | Bin 3474 -> 0 bytes .../Sabre/DAV/Browser/assets/icons/file.png | Bin 2837 -> 0 bytes .../Sabre/DAV/Browser/assets/icons/parent.png | Bin 3474 -> 0 bytes .../DAV/Browser/assets/icons/principal.png | Bin 5480 -> 0 bytes .../app/libraries/Sabre/DAV/Collection.php | 110 - .../Sabre/DAV/Exception/ConflictingLock.php | 37 - .../Sabre/DAV/Exception/FileNotFound.php | 19 - .../DAV/Exception/InvalidResourceType.php | 33 - .../Sabre/DAV/Exception/LengthRequired.php | 30 - .../Exception/LockTokenMatchesRequestUri.php | 41 - .../libraries/Sabre/DAV/Exception/Locked.php | 73 - .../DAV/Exception/ReportNotSupported.php | 32 - .../DAV/Exception/ServiceUnavailable.php | 30 - .../DAV/Exception/UnsupportedMediaType.php | 28 - .../app/libraries/Sabre/DAV/FS/Directory.php | 140 -- .../0.0.0/app/libraries/Sabre/DAV/FS/File.php | 91 - .../0.0.0/app/libraries/Sabre/DAV/FS/Node.php | 82 - .../libraries/Sabre/DAV/FSExt/Directory.php | 159 -- .../app/libraries/Sabre/DAV/FSExt/File.php | 146 -- .../app/libraries/Sabre/DAV/FSExt/Node.php | 214 -- .../v/0.0.0/app/libraries/Sabre/DAV/File.php | 85 - .../app/libraries/Sabre/DAV/ICollection.php | 77 - .../Sabre/DAV/IExtendedCollection.php | 28 - .../v/0.0.0/app/libraries/Sabre/DAV/IFile.php | 77 - .../v/0.0.0/app/libraries/Sabre/DAV/INode.php | 46 - .../app/libraries/Sabre/DAV/IProperties.php | 71 - .../0.0.0/app/libraries/Sabre/DAV/IQuota.php | 27 - .../DAV/Locks/Backend/AbstractBackend.php | 21 - .../DAV/Locks/Backend/BackendInterface.php | 51 - .../libraries/Sabre/DAV/Locks/Backend/FS.php | 193 -- .../Sabre/DAV/Locks/Backend/File.php | 183 -- .../libraries/Sabre/DAV/Locks/Backend/PDO.php | 167 -- .../libraries/Sabre/DAV/Locks/LockInfo.php | 81 - .../app/libraries/Sabre/DAV/Locks/Plugin.php | 649 ----- .../app/libraries/Sabre/DAV/Mount/Plugin.php | 83 - .../v/0.0.0/app/libraries/Sabre/DAV/Node.php | 55 - .../app/libraries/Sabre/DAV/ObjectTree.php | 159 -- .../Sabre/DAV/PartialUpdate/IFile.php | 39 - .../Sabre/DAV/PartialUpdate/IPatchSupport.php | 48 - .../Sabre/DAV/PartialUpdate/Plugin.php | 246 -- .../app/libraries/Sabre/DAV/Property.php | 31 - .../Sabre/DAV/Property/GetLastModified.php | 78 - .../app/libraries/Sabre/DAV/Property/Href.php | 99 - .../libraries/Sabre/DAV/Property/HrefList.php | 105 - .../libraries/Sabre/DAV/Property/IHref.php | 25 - .../Sabre/DAV/Property/LockDiscovery.php | 104 - .../Sabre/DAV/Property/ResourceType.php | 127 - .../libraries/Sabre/DAV/Property/Response.php | 157 -- .../Sabre/DAV/Property/ResponseList.php | 59 - .../Sabre/DAV/Property/SupportedLock.php | 78 - .../Sabre/DAV/Property/SupportedReportSet.php | 111 - .../libraries/Sabre/DAV/PropertyInterface.php | 21 - .../0.0.0/app/libraries/Sabre/DAV/Server.php | 2178 ----------------- .../app/libraries/Sabre/DAV/ServerPlugin.php | 90 - .../libraries/Sabre/DAV/SimpleCollection.php | 108 - .../app/libraries/Sabre/DAV/SimpleFile.php | 121 - .../app/libraries/Sabre/DAV/StringUtil.php | 91 - .../Sabre/DAV/TemporaryFileFilterPlugin.php | 289 --- .../v/0.0.0/app/libraries/Sabre/DAV/Tree.php | 193 -- .../libraries/Sabre/DAV/Tree/Filesystem.php | 133 - .../0.0.0/app/libraries/Sabre/DAV/URLUtil.php | 124 - .../app/libraries/Sabre/DAV/UUIDUtil.php | 64 - .../0.0.0/app/libraries/Sabre/DAV/Version.php | 24 - .../DAVACL/AbstractPrincipalCollection.php | 155 -- .../Sabre/DAVACL/Exception/AceConflict.php | 35 - .../Sabre/DAVACL/Exception/NeedPrivileges.php | 83 - .../Sabre/DAVACL/Exception/NoAbstract.php | 35 - .../Exception/NotRecognizedPrincipal.php | 35 - .../Exception/NotSupportedPrivilege.php | 35 - .../0.0.0/app/libraries/Sabre/DAVACL/IACL.php | 74 - .../app/libraries/Sabre/DAVACL/IPrincipal.php | 77 - .../Sabre/DAVACL/IPrincipalCollection.php | 42 - .../app/libraries/Sabre/DAVACL/Plugin.php | 1402 ----------- .../app/libraries/Sabre/DAVACL/Principal.php | 281 --- .../PrincipalBackend/AbstractBackend.php | 18 - .../PrincipalBackend/BackendInterface.php | 153 -- .../Sabre/DAVACL/PrincipalBackend/PDO.php | 428 ---- .../Sabre/DAVACL/PrincipalCollection.php | 33 - .../libraries/Sabre/DAVACL/Property/Acl.php | 211 -- .../Sabre/DAVACL/Property/AclRestrictions.php | 34 - .../Property/CurrentUserPrivilegeSet.php | 124 - .../Sabre/DAVACL/Property/Principal.php | 161 -- .../DAVACL/Property/SupportedPrivilegeSet.php | 94 - .../app/libraries/Sabre/DAVACL/Version.php | 24 - .../app/libraries/Sabre/HTTP/AWSAuth.php | 227 -- .../app/libraries/Sabre/HTTP/AbstractAuth.php | 111 - .../app/libraries/Sabre/HTTP/BasicAuth.php | 67 - .../app/libraries/Sabre/HTTP/DigestAuth.php | 240 -- .../app/libraries/Sabre/HTTP/Request.php | 284 --- .../app/libraries/Sabre/HTTP/Response.php | 175 -- .../v/0.0.0/app/libraries/Sabre/HTTP/Util.php | 82 - .../app/libraries/Sabre/HTTP/Version.php | 24 - .../v/0.0.0/app/libraries/snappymail/uuid.php | 100 + 169 files changed, 103 insertions(+), 24775 deletions(-) delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/NotificationSupport.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/PDO.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Calendar.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarObject.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryParser.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Exception/InvalidComponentType.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICSExportPlugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICalendar.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICalendarObject.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/IShareableCalendar.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ISharedCalendar.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Collection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/ICollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/INode.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/INotificationType.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Node.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/Invite.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/InviteReply.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/SystemStatus.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/IProxyRead.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/IProxyWrite.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/ProxyRead.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/ProxyWrite.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/User.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/AllowedSharingModes.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/Invite.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/ScheduleCalendarTransp.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IMip.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IOutbox.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/Outbox.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ShareableCalendar.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharedCalendar.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharingPlugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Version.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBook.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookQueryParser.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookRoot.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Backend/AbstractBackend.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Backend/BackendInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Backend/PDO.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Card.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/IAddressBook.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/ICard.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/IDirectory.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/UserAddressBooks.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/VCFExportPlugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Version.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/AbstractBasic.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/Apache.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/BackendInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/File.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/PDO.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/GuessContentType.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/MapGetToPropFind.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/favicon.ico delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/addressbook.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/calendar.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/card.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/collection.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/file.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/parent.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/principal.png delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Collection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ConflictingLock.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/FileNotFound.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/InvalidResourceType.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/Locked.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ReportNotSupported.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ServiceUnavailable.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/Directory.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/File.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/Node.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Directory.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/File.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/File.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/ICollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/IExtendedCollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/IFile.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/INode.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/IProperties.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/IQuota.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/AbstractBackend.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/BackendInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/FS.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/File.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/PDO.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/LockInfo.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Node.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/ObjectTree.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/IFile.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/IPatchSupport.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/GetLastModified.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Href.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/HrefList.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/IHref.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/LockDiscovery.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResourceType.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Response.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResponseList.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedReportSet.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/PropertyInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Server.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleCollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/StringUtil.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree/Filesystem.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/URLUtil.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/UUIDUtil.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Version.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/AbstractPrincipalCollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/AceConflict.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NeedPrivileges.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NoAbstract.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotSupportedPrivilege.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/IACL.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/IPrincipal.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/IPrincipalCollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Plugin.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/BackendInterface.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/PDO.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalCollection.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Acl.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/AclRestrictions.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Principal.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/SupportedPrivilegeSet.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Version.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/AWSAuth.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/AbstractAuth.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/BasicAuth.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Request.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Response.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Util.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Version.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/uuid.php diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php index 8c3b2a129..8d280338e 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php @@ -255,7 +255,7 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider if (empty($oVCard->UID)) { - $oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID(); + $oVCard->UID = \SnappyMail\UUID::generate(); } $oContact->PopulateByVCard($oVCard->UID, $oVCard->serialize()); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index 72fb82175..f8ac98358 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -163,8 +163,7 @@ class Contact implements \JsonSerializable public function RegenerateContactStr() : void { - $this->IdContactStr = \class_exists('Sabre\DAV\Client') ? - \Sabre\DAV\UUIDUtil::getUUID() : \MailSo\Base\Utils::Md5Rand(); + $this->IdContactStr = \SnappyMail\UUID::generate(); } public function GetEmails() : array @@ -621,7 +620,7 @@ class Contact implements \JsonSerializable $this->addArrayPropertyHelper($aProperties, $oVCard->TEL, PropertyType::PHONE); } - $sUidValue = $oVCard->UID ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID(); + $sUidValue = $oVCard->UID ? (string) $oVCard->UID : \SnappyMail\UUID::generate(); $aProperties[] = new Property(PropertyType::UID, $sUidValue); if (empty($this->IdContactStr)) diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php deleted file mode 100644 index 06fdbd9a9..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/AbstractBackend.php +++ /dev/null @@ -1,155 +0,0 @@ - array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param mixed $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, array $mutations) { - - return false; - - } - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by \Sabre\CalDAV\CalendarQueryParser. - * - * Note that it is extremely likely that getCalendarObject for every path - * returned from this method will be called almost immediately after. You - * may want to anticipate this to speed up these requests. - * - * This method provides a default implementation, which parses *all* the - * iCalendar objects in the specified calendar. - * - * This default may well be good enough for personal use, and calendars - * that aren't very large. But if you anticipate high usage, big calendars - * or high loads, you are strongly adviced to optimize certain paths. - * - * The best way to do so is override this method and to optimize - * specifically for 'common filters'. - * - * Requests that are extremely common are: - * * requests for just VEVENTS - * * requests for just VTODO - * * requests with a time-range-filter on either VEVENT or VTODO. - * - * ..and combinations of these requests. It may not be worth it to try to - * handle every possible situation and just rely on the (relatively - * easy to use) CalendarQueryValidator to handle the rest. - * - * Note that especially time-range-filters may be difficult to parse. A - * time-range filter specified on a VEVENT must for instance also handle - * recurrence rules correctly. - * A good example of how to interprete all these filters can also simply - * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct - * as possible, so it gives you a good idea on what type of stuff you need - * to think of. - * - * @param mixed $calendarId - * @param array $filters - * @return array - */ - public function calendarQuery($calendarId, array $filters) { - - $result = array(); - $objects = $this->getCalendarObjects($calendarId); - - $validator = new \Sabre\CalDAV\CalendarQueryValidator(); - - foreach($objects as $object) { - - if ($this->validateFilterForObject($object, $filters)) { - $result[] = $object['uri']; - } - - } - - return $result; - - } - - /** - * This method validates if a filters (as passed to calendarQuery) matches - * the given object. - * - * @param array $object - * @param array $filters - * @return bool - */ - protected function validateFilterForObject(array $object, array $filters) { - - // Unfortunately, setting the 'calendardata' here is optional. If - // it was excluded, we actually need another call to get this as - // well. - if (!isset($object['calendardata'])) { - $object = $this->getCalendarObject($object['calendarid'], $object['uri']); - } - - $data = is_resource($object['calendardata'])?stream_get_contents($object['calendardata']):$object['calendardata']; - $vObject = VObject\Reader::read($data); - - $validator = new CalDAV\CalendarQueryValidator(); - return $validator->validate($vObject, $filters); - - } - - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php deleted file mode 100644 index e2a034240..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/BackendInterface.php +++ /dev/null @@ -1,233 +0,0 @@ - array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param mixed $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, array $mutations); - - /** - * Delete a calendar and all it's objects - * - * @param mixed $calendarId - * @return void - */ - public function deleteCalendar($calendarId); - - /** - * Returns all calendar objects within a calendar. - * - * Every item contains an array with the following keys: - * * id - unique identifier which will be used for subsequent updates - * * calendardata - The iCalendar-compatible calendar data - * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. - * * lastmodified - a timestamp of the last modification time - * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: - * ' "abcdef"') - * * calendarid - The calendarid as it was passed to this function. - * * size - The size of the calendar objects, in bytes. - * - * Note that the etag is optional, but it's highly encouraged to return for - * speed reasons. - * - * The calendardata is also optional. If it's not returned - * 'getCalendarObject' will be called later, which *is* expected to return - * calendardata. - * - * If neither etag or size are specified, the calendardata will be - * used/fetched to determine these numbers. If both are specified the - * amount of times this is needed is reduced by a great degree. - * - * @param mixed $calendarId - * @return array - */ - public function getCalendarObjects($calendarId); - - /** - * Returns information from a single calendar object, based on it's object - * uri. - * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. - * - * This method must return null if the object did not exist. - * - * @param mixed $calendarId - * @param string $objectUri - * @return array|null - */ - public function getCalendarObject($calendarId,$objectUri); - - /** - * Creates a new calendar object. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function createCalendarObject($calendarId,$objectUri,$calendarData); - - /** - * Updates an existing calendarobject, based on it's uri. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function updateCalendarObject($calendarId,$objectUri,$calendarData); - - /** - * Deletes an existing calendar object. - * - * @param mixed $calendarId - * @param string $objectUri - * @return void - */ - public function deleteCalendarObject($calendarId,$objectUri); - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by Sabre\CalDAV\CalendarQueryParser. - * - * Note that it is extremely likely that getCalendarObject for every path - * returned from this method will be called almost immediately after. You - * may want to anticipate this to speed up these requests. - * - * This method provides a default implementation, which parses *all* the - * iCalendar objects in the specified calendar. - * - * This default may well be good enough for personal use, and calendars - * that aren't very large. But if you anticipate high usage, big calendars - * or high loads, you are strongly adviced to optimize certain paths. - * - * The best way to do so is override this method and to optimize - * specifically for 'common filters'. - * - * Requests that are extremely common are: - * * requests for just VEVENTS - * * requests for just VTODO - * * requests with a time-range-filter on either VEVENT or VTODO. - * - * ..and combinations of these requests. It may not be worth it to try to - * handle every possible situation and just rely on the (relatively - * easy to use) CalendarQueryValidator to handle the rest. - * - * Note that especially time-range-filters may be difficult to parse. A - * time-range filter specified on a VEVENT must for instance also handle - * recurrence rules correctly. - * A good example of how to interprete all these filters can also simply - * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct - * as possible, so it gives you a good idea on what type of stuff you need - * to think of. - * - * @param mixed $calendarId - * @param array $filters - * @return array - */ - public function calendarQuery($calendarId, array $filters); - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/NotificationSupport.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/NotificationSupport.php deleted file mode 100644 index 0145dfb3f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/NotificationSupport.php +++ /dev/null @@ -1,47 +0,0 @@ - 'displayname', - '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', - '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', - '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', - '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', - ); - - /** - * Creates the backend - * - * @param \PDO $pdo - * @param string $calendarTableName - * @param string $calendarObjectTableName - */ - public function __construct(\PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') { - - $this->pdo = $pdo; - $this->calendarTableName = $calendarTableName; - $this->calendarObjectTableName = $calendarObjectTableName; - - } - - /** - * Returns a list of calendars for a principal. - * - * Every project is an array with the following keys: - * * id, a unique id that will be used by other functions to modify the - * calendar. This can be the same as the uri or a database key. - * * uri, which the basename of the uri with which the calendar is - * accessed. - * * principaluri. The owner of the calendar. Almost always the same as - * principalUri passed to this method. - * - * Furthermore it can contain webdav properties in clark notation. A very - * common one is '{DAV:}displayname'. - * - * @param string $principalUri - * @return array - */ - public function getCalendarsForUser($principalUri) { - - $fields = array_values($this->propertyMap); - $fields[] = 'id'; - $fields[] = 'uri'; - $fields[] = 'ctag'; - $fields[] = 'components'; - $fields[] = 'principaluri'; - $fields[] = 'transparent'; - - // Making fields a comma-delimited list - $fields = implode(', ', $fields); - $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC"); - $stmt->execute(array($principalUri)); - - $calendars = array(); - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - - $components = array(); - if ($row['components']) { - $components = explode(',',$row['components']); - } - - $calendar = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'principaluri' => $row['principaluri'], - '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', - '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet($components), - '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), - ); - - - foreach($this->propertyMap as $xmlName=>$dbName) { - $calendar[$xmlName] = $row[$dbName]; - } - - $calendars[] = $calendar; - - } - - return $calendars; - - } - - /** - * Creates a new calendar for a principal. - * - * If the creation was a success, an id must be returned that can be used to reference - * this calendar in other methods, such as updateCalendar - * - * @param string $principalUri - * @param string $calendarUri - * @param array $properties - * @return string - */ - public function createCalendar($principalUri, $calendarUri, array $properties) { - - $fieldNames = array( - 'principaluri', - 'uri', - 'ctag', - 'transparent', - ); - $values = array( - ':principaluri' => $principalUri, - ':uri' => $calendarUri, - ':ctag' => 1, - ':transparent' => 0, - ); - - // Default value - $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; - $fieldNames[] = 'components'; - if (!isset($properties[$sccs])) { - $values[':components'] = 'VEVENT,VTODO'; - } else { - if (!($properties[$sccs] instanceof CalDAV\Property\SupportedCalendarComponentSet)) { - throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet'); - } - $values[':components'] = implode(',',$properties[$sccs]->getValue()); - } - $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; - if (isset($properties[$transp])) { - $values[':transparent'] = $properties[$transp]->getValue()==='transparent'; - } - - foreach($this->propertyMap as $xmlName=>$dbName) { - if (isset($properties[$xmlName])) { - - $values[':' . $dbName] = $properties[$xmlName]; - $fieldNames[] = $dbName; - } - } - - $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); - $stmt->execute($values); - - return $this->pdo->lastInsertId(); - - } - - /** - * Updates properties for a calendar. - * - * The mutations array uses the propertyName in clark-notation as key, - * and the array value for the property value. In the case a property - * should be deleted, the property value will be null. - * - * This method must be atomic. If one property cannot be changed, the - * entire operation must fail. - * - * If the operation was successful, true can be returned. - * If the operation failed, false can be returned. - * - * Deletion of a non-existent property is always successful. - * - * Lastly, it is optional to return detailed information about any - * failures. In this case an array should be returned with the following - * structure: - * - * array( - * 403 => array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param string $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, array $mutations) { - - $newValues = array(); - $result = array( - 200 => array(), // Ok - 403 => array(), // Forbidden - 424 => array(), // Failed Dependency - ); - - $hasError = false; - - foreach($mutations as $propertyName=>$propertyValue) { - - switch($propertyName) { - case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' : - $fieldName = 'transparent'; - $newValues[$fieldName] = $propertyValue->getValue()==='transparent'; - break; - default : - // Checking the property map - if (isset($this->propertyMap[$propertyName])) { - // We don't know about this property. - $hasError = true; - $result[403][$propertyName] = null; - unset($mutations[$propertyName]); - } else { - - $fieldName = $this->propertyMap[$propertyName]; - $newValues[$fieldName] = $propertyValue; - } - } - - } - - // If there were any errors we need to fail the request - if ($hasError) { - // Properties has the remaining properties - foreach($mutations as $propertyName=>$propertyValue) { - $result[424][$propertyName] = null; - } - - // Removing unused statuscodes for cleanliness - foreach($result as $status=>$properties) { - if (is_array($properties) && count($properties)===0) unset($result[$status]); - } - - return $result; - - } - - // Success - - // Now we're generating the sql query. - $valuesSql = array(); - foreach($newValues as $fieldName=>$value) { - $valuesSql[] = $fieldName . ' = ?'; - } - $valuesSql[] = 'ctag = ctag + 1'; - - $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?"); - $newValues['id'] = $calendarId; - $stmt->execute(array_values($newValues)); - - return true; - - } - - /** - * Delete a calendar and all it's objects - * - * @param string $calendarId - * @return void - */ - public function deleteCalendar($calendarId) { - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); - $stmt->execute(array($calendarId)); - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); - $stmt->execute(array($calendarId)); - - } - - /** - * Returns all calendar objects within a calendar. - * - * Every item contains an array with the following keys: - * * id - unique identifier which will be used for subsequent updates - * * calendardata - The iCalendar-compatible calendar data - * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. - * * lastmodified - a timestamp of the last modification time - * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: - * ' "abcdef"') - * * calendarid - The calendarid as it was passed to this function. - * * size - The size of the calendar objects, in bytes. - * - * Note that the etag is optional, but it's highly encouraged to return for - * speed reasons. - * - * The calendardata is also optional. If it's not returned - * 'getCalendarObject' will be called later, which *is* expected to return - * calendardata. - * - * If neither etag or size are specified, the calendardata will be - * used/fetched to determine these numbers. If both are specified the - * amount of times this is needed is reduced by a great degree. - * - * @param string $calendarId - * @return array - */ - public function getCalendarObjects($calendarId) { - - $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); - $stmt->execute(array($calendarId)); - - $result = array(); - foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { - $result[] = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], - 'size' => (int)$row['size'], - ); - } - - return $result; - - } - - /** - * Returns information from a single calendar object, based on it's object - * uri. - * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. - * - * This method must return null if the object did not exist. - * - * @param string $calendarId - * @param string $objectUri - * @return array|null - */ - public function getCalendarObject($calendarId,$objectUri) { - - $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); - $stmt->execute(array($calendarId, $objectUri)); - $row = $stmt->fetch(\PDO::FETCH_ASSOC); - - if(!$row) return null; - - return array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], - 'size' => (int)$row['size'], - 'calendardata' => $row['calendardata'], - ); - - } - - - /** - * Creates a new calendar object. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function createCalendarObject($calendarId,$objectUri,$calendarData) { - - $extraData = $this->getDenormalizedData($calendarData); - - $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)'); - $stmt->execute(array( - $calendarId, - $objectUri, - $calendarData, - time(), - $extraData['etag'], - $extraData['size'], - $extraData['componentType'], - $extraData['firstOccurence'], - $extraData['lastOccurence'], - )); - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); - $stmt->execute(array($calendarId)); - - return '"' . $extraData['etag'] . '"'; - - } - - /** - * Updates an existing calendarobject, based on it's uri. - * - * It is possible return an etag from this function, which will be used in - * the response to this PUT request. Note that the ETag must be surrounded - * by double-quotes. - * - * However, you should only really return this ETag if you don't mangle the - * calendar-data. If the result of a subsequent GET to this object is not - * the exact same as this request body, you should omit the ETag. - * - * @param mixed $calendarId - * @param string $objectUri - * @param string $calendarData - * @return string|null - */ - public function updateCalendarObject($calendarId,$objectUri,$calendarData) { - - $extraData = $this->getDenormalizedData($calendarData); - - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?'); - $stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri)); - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); - $stmt->execute(array($calendarId)); - - return '"' . $extraData['etag'] . '"'; - - } - - /** - * Parses some information from calendar objects, used for optimized - * calendar-queries. - * - * Returns an array with the following keys: - * * etag - * * size - * * componentType - * * firstOccurence - * * lastOccurence - * - * @param string $calendarData - * @return array - */ - protected function getDenormalizedData($calendarData) { - - $vObject = VObject\Reader::read($calendarData); - $componentType = null; - $component = null; - $firstOccurence = null; - $lastOccurence = null; - foreach($vObject->getComponents() as $component) { - if ($component->name!=='VTIMEZONE') { - $componentType = $component->name; - break; - } - } - if (!$componentType) { - throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); - } - if ($componentType === 'VEVENT') { - $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); - // Finding the last occurence is a bit harder - if (!isset($component->RRULE)) { - if (isset($component->DTEND)) { - $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); - } elseif (isset($component->DURATION)) { - $endDate = clone $component->DTSTART->getDateTime(); - $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue())); - $lastOccurence = $endDate->getTimeStamp(); - } elseif (!$component->DTSTART->hasTime()) { - $endDate = clone $component->DTSTART->getDateTime(); - $endDate->modify('+1 day'); - $lastOccurence = $endDate->getTimeStamp(); - } else { - $lastOccurence = $firstOccurence; - } - } else { - $it = new VObject\RecurrenceIterator($vObject, (string)$component->UID); - $maxDate = new \DateTime(self::MAX_DATE); - if ($it->isInfinite()) { - $lastOccurence = $maxDate->getTimeStamp(); - } else { - $end = $it->getDtEnd(); - while($it->valid() && $end < $maxDate) { - $end = $it->getDtEnd(); - $it->next(); - - } - $lastOccurence = $end->getTimeStamp(); - } - - } - } - - return array( - 'etag' => md5($calendarData), - 'size' => strlen($calendarData), - 'componentType' => $componentType, - 'firstOccurence' => $firstOccurence, - 'lastOccurence' => $lastOccurence, - ); - - } - - /** - * Deletes an existing calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @return void - */ - public function deleteCalendarObject($calendarId,$objectUri) { - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); - $stmt->execute(array($calendarId,$objectUri)); - $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?'); - $stmt->execute(array($calendarId)); - - } - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by \Sabre\CalDAV\CalendarQueryParser. - * - * Note that it is extremely likely that getCalendarObject for every path - * returned from this method will be called almost immediately after. You - * may want to anticipate this to speed up these requests. - * - * This method provides a default implementation, which parses *all* the - * iCalendar objects in the specified calendar. - * - * This default may well be good enough for personal use, and calendars - * that aren't very large. But if you anticipate high usage, big calendars - * or high loads, you are strongly adviced to optimize certain paths. - * - * The best way to do so is override this method and to optimize - * specifically for 'common filters'. - * - * Requests that are extremely common are: - * * requests for just VEVENTS - * * requests for just VTODO - * * requests with a time-range-filter on a VEVENT. - * - * ..and combinations of these requests. It may not be worth it to try to - * handle every possible situation and just rely on the (relatively - * easy to use) CalendarQueryValidator to handle the rest. - * - * Note that especially time-range-filters may be difficult to parse. A - * time-range filter specified on a VEVENT must for instance also handle - * recurrence rules correctly. - * A good example of how to interprete all these filters can also simply - * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct - * as possible, so it gives you a good idea on what type of stuff you need - * to think of. - * - * This specific implementation (for the PDO) backend optimizes filters on - * specific components, and VEVENT time-ranges. - * - * @param string $calendarId - * @param array $filters - * @return array - */ - public function calendarQuery($calendarId, array $filters) { - - $result = array(); - $validator = new \Sabre\CalDAV\CalendarQueryValidator(); - - $componentType = null; - $requirePostFilter = true; - $timeRange = null; - - // if no filters were specified, we don't need to filter after a query - if (!$filters['prop-filters'] && !$filters['comp-filters']) { - $requirePostFilter = false; - } - - // Figuring out if there's a component filter - if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { - $componentType = $filters['comp-filters'][0]['name']; - - // Checking if we need post-filters - if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { - $requirePostFilter = false; - } - // There was a time-range filter - if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { - $timeRange = $filters['comp-filters'][0]['time-range']; - - // If start time OR the end time is not specified, we can do a - // 100% accurate mysql query. - if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { - $requirePostFilter = false; - } - } - - } - - if ($requirePostFilter) { - $query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; - } else { - $query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; - } - - $values = array( - 'calendarid' => $calendarId, - ); - - if ($componentType) { - $query.=" AND componenttype = :componenttype"; - $values['componenttype'] = $componentType; - } - - if ($timeRange && $timeRange['start']) { - $query.=" AND lastoccurence > :startdate"; - $values['startdate'] = $timeRange['start']->getTimeStamp(); - } - if ($timeRange && $timeRange['end']) { - $query.=" AND firstoccurence < :enddate"; - $values['enddate'] = $timeRange['end']->getTimeStamp(); - } - - $stmt = $this->pdo->prepare($query); - $stmt->execute($values); - - $result = array(); - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - if ($requirePostFilter) { - if (!$this->validateFilterForObject($row, $filters)) { - continue; - } - } - $result[] = $row['uri']; - - } - - return $result; - - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php deleted file mode 100644 index 2d1e3662e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Backend/SharingSupport.php +++ /dev/null @@ -1,243 +0,0 @@ -caldavBackend = $caldavBackend; - $this->calendarInfo = $calendarInfo; - - } - - /** - * Returns the name of the calendar - * - * @return string - */ - public function getName() { - - return $this->calendarInfo['uri']; - - } - - /** - * Updates properties such as the display name and description - * - * @param array $mutations - * @return array - */ - public function updateProperties($mutations) { - - return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations); - - } - - /** - * Returns the list of properties - * - * @param array $requestedProperties - * @return array - */ - public function getProperties($requestedProperties) { - - $response = array(); - - foreach($requestedProperties as $prop) switch($prop) { - - case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : - $response[$prop] = new Property\SupportedCalendarData(); - break; - case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : - $response[$prop] = new Property\SupportedCollationSet(); - break; - case '{DAV:}owner' : - $response[$prop] = new DAVACL\Property\Principal(DAVACL\Property\Principal::HREF,$this->calendarInfo['principaluri']); - break; - default : - if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; - break; - - } - return $response; - - } - - /** - * Returns a calendar object - * - * The contained calendar objects are for example Events or Todo's. - * - * @param string $name - * @return \Sabre\CalDAV\ICalendarObject - */ - public function getChild($name) { - - $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); - - if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found'); - - $obj['acl'] = $this->getACL(); - // Removing the irrelivant - foreach($obj['acl'] as $key=>$acl) { - if ($acl['privilege'] === '{' . Plugin::NS_CALDAV . '}read-free-busy') { - unset($obj['acl'][$key]); - } - } - - return new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); - - } - - /** - * Returns the full list of calendar objects - * - * @return array - */ - public function getChildren() { - - $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); - $children = array(); - foreach($objs as $obj) { - $obj['acl'] = $this->getACL(); - // Removing the irrelivant - foreach($obj['acl'] as $key=>$acl) { - if ($acl['privilege'] === '{' . Plugin::NS_CALDAV . '}read-free-busy') { - unset($obj['acl'][$key]); - } - } - $children[] = new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); - } - return $children; - - } - - /** - * Checks if a child-node exists. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); - if (!$obj) - return false; - else - return true; - - } - - /** - * Creates a new directory - * - * We actually block this, as subdirectories are not allowed in calendars. - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed'); - - } - - /** - * Creates a new file - * - * The contents of the new file must be a valid ICalendar string. - * - * @param string $name - * @param resource $calendarData - * @return string|null - */ - public function createFile($name,$calendarData = null) { - - if (is_resource($calendarData)) { - $calendarData = stream_get_contents($calendarData); - } - return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); - - } - - /** - * Deletes the calendar. - * - * @return void - */ - public function delete() { - - $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); - - } - - /** - * Renames the calendar. Note that most calendars use the - * {DAV:}displayname to display a name to display a name. - * - * @param string $newName - * @return void - */ - public function setName($newName) { - - throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported'); - - } - - /** - * Returns the last modification date as a unix timestamp. - * - * @return void - */ - public function getLastModified() { - - return null; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->calendarInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->getOwner() . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-read', - 'protected' => true, - ), - array( - 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', - 'principal' => '{DAV:}authenticated', - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet(); - - // We need to inject 'read-free-busy' in the tree, aggregated under - // {DAV:}read. - foreach($default['aggregates'] as &$agg) { - - if ($agg['privilege'] !== '{DAV:}read') continue; - - $agg['aggregates'][] = array( - 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', - ); - - } - return $default; - - } - - /** - * Performs a calendar-query on the contents of this calendar. - * - * The calendar-query is defined in RFC4791 : CalDAV. Using the - * calendar-query it is possible for a client to request a specific set of - * object, based on contents of iCalendar properties, date-ranges and - * iCalendar component types (VTODO, VEVENT). - * - * This method should just return a list of (relative) urls that match this - * query. - * - * The list of filters are specified as an array. The exact array is - * documented by Sabre\CalDAV\CalendarQueryParser. - * - * @param array $filters - * @return array - */ - public function calendarQuery(array $filters) { - - return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarObject.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarObject.php deleted file mode 100644 index 3a0758104..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarObject.php +++ /dev/null @@ -1,279 +0,0 @@ -caldavBackend = $caldavBackend; - - if (!isset($objectData['calendarid'])) { - throw new \InvalidArgumentException('The objectData argument must contain a \'calendarid\' property'); - } - if (!isset($objectData['uri'])) { - throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); - } - - $this->calendarInfo = $calendarInfo; - $this->objectData = $objectData; - - } - - /** - * Returns the uri for this object - * - * @return string - */ - public function getName() { - - return $this->objectData['uri']; - - } - - /** - * Returns the ICalendar-formatted object - * - * @return string - */ - public function get() { - - // Pre-populating the 'calendardata' is optional, if we don't have it - // already we fetch it from the backend. - if (!isset($this->objectData['calendardata'])) { - $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']); - } - return $this->objectData['calendardata']; - - } - - /** - * Updates the ICalendar-formatted object - * - * @param string|resource $calendarData - * @return string - */ - public function put($calendarData) { - - if (is_resource($calendarData)) { - $calendarData = stream_get_contents($calendarData); - } - $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); - $this->objectData['calendardata'] = $calendarData; - $this->objectData['etag'] = $etag; - - return $etag; - - } - - /** - * Deletes the calendar object - * - * @return void - */ - public function delete() { - - $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); - - } - - /** - * Returns the mime content-type - * - * @return string - */ - public function getContentType() { - - return 'text/calendar; charset=utf-8'; - - } - - /** - * Returns an ETag for this object. - * - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * @return string - */ - public function getETag() { - - if (isset($this->objectData['etag'])) { - return $this->objectData['etag']; - } else { - return '"' . md5($this->get()). '"'; - } - - } - - /** - * Returns the last modification date as a unix timestamp - * - * @return int - */ - public function getLastModified() { - - return $this->objectData['lastmodified']; - - } - - /** - * Returns the size of this object in bytes - * - * @return int - */ - public function getSize() { - - if (array_key_exists('size',$this->objectData)) { - return $this->objectData['size']; - } else { - return strlen($this->get()); - } - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->calendarInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - // An alternative acl may be specified in the object data. - if (isset($this->objectData['acl'])) { - return $this->objectData['acl']; - } - - // The default ACL - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new \Sabre\DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryParser.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryParser.php deleted file mode 100644 index 1b9339835..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryParser.php +++ /dev/null @@ -1,298 +0,0 @@ -dom = $dom; - $this->xpath = new \DOMXPath($dom); - $this->xpath->registerNameSpace('cal',Plugin::NS_CALDAV); - $this->xpath->registerNameSpace('dav','urn:DAV'); - - } - - /** - * Parses the request. - * - * @return void - */ - public function parse() { - - $filterNode = null; - - $filter = $this->xpath->query('/cal:calendar-query/cal:filter'); - if ($filter->length !== 1) { - throw new \Sabre\DAV\Exception\BadRequest('Only one filter element is allowed'); - } - - $compFilters = $this->parseCompFilters($filter->item(0)); - if (count($compFilters)!==1) { - throw new \Sabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.'); - } - - $this->filters = $compFilters[0]; - $this->requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($this->dom->firstChild)); - - $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); - if ($expand->length>0) { - $this->expand = $this->parseExpand($expand->item(0)); - } - - - } - - /** - * Parses all the 'comp-filter' elements from a node - * - * @param \DOMElement $parentNode - * @return array - */ - protected function parseCompFilters(\DOMElement $parentNode) { - - $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode); - $result = array(); - - for($ii=0; $ii < $compFilterNodes->length; $ii++) { - - $compFilterNode = $compFilterNodes->item($ii); - - $compFilter = array(); - $compFilter['name'] = $compFilterNode->getAttribute('name'); - $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0; - $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode); - $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode); - $compFilter['time-range'] = $this->parseTimeRange($compFilterNode); - - if ($compFilter['time-range'] && !in_array($compFilter['name'],array( - 'VEVENT', - 'VTODO', - 'VJOURNAL', - 'VFREEBUSY', - 'VALARM', - ))) { - throw new \Sabre\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); - }; - - $result[] = $compFilter; - - } - - return $result; - - } - - /** - * Parses all the prop-filter elements from a node - * - * @param \DOMElement $parentNode - * @return array - */ - protected function parsePropFilters(\DOMElement $parentNode) { - - $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode); - $result = array(); - - for ($ii=0; $ii < $propFilterNodes->length; $ii++) { - - $propFilterNode = $propFilterNodes->item($ii); - $propFilter = array(); - $propFilter['name'] = $propFilterNode->getAttribute('name'); - $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0; - $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode); - $propFilter['text-match'] = $this->parseTextMatch($propFilterNode); - $propFilter['time-range'] = $this->parseTimeRange($propFilterNode); - - $result[] = $propFilter; - - } - - return $result; - - } - - /** - * Parses the param-filter element - * - * @param \DOMElement $parentNode - * @return array - */ - protected function parseParamFilters(\DOMElement $parentNode) { - - $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode); - $result = array(); - - for($ii=0;$ii<$paramFilterNodes->length;$ii++) { - - $paramFilterNode = $paramFilterNodes->item($ii); - $paramFilter = array(); - $paramFilter['name'] = $paramFilterNode->getAttribute('name'); - $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0; - $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode); - - $result[] = $paramFilter; - - } - - return $result; - - } - - /** - * Parses the text-match element - * - * @param \DOMElement $parentNode - * @return array|null - */ - protected function parseTextMatch(\DOMElement $parentNode) { - - $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode); - - if ($textMatchNodes->length === 0) - return null; - - $textMatchNode = $textMatchNodes->item(0); - $negateCondition = $textMatchNode->getAttribute('negate-condition'); - $negateCondition = $negateCondition==='yes'; - $collation = $textMatchNode->getAttribute('collation'); - if (!$collation) $collation = 'i;ascii-casemap'; - - return array( - 'negate-condition' => $negateCondition, - 'collation' => $collation, - 'value' => $textMatchNode->nodeValue - ); - - } - - /** - * Parses the time-range element - * - * @param \DOMElement $parentNode - * @return array|null - */ - protected function parseTimeRange(\DOMElement $parentNode) { - - $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode); - if ($timeRangeNodes->length === 0) { - return null; - } - - $timeRangeNode = $timeRangeNodes->item(0); - - if ($start = $timeRangeNode->getAttribute('start')) { - $start = VObject\DateTimeParser::parseDateTime($start); - } else { - $start = null; - } - if ($end = $timeRangeNode->getAttribute('end')) { - $end = VObject\DateTimeParser::parseDateTime($end); - } else { - $end = null; - } - - if (!is_null($start) && !is_null($end) && $end <= $start) { - throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter'); - } - - return array( - 'start' => $start, - 'end' => $end, - ); - - } - - /** - * Parses the CALDAV:expand element - * - * @param \DOMElement $parentNode - * @return void - */ - protected function parseExpand(\DOMElement $parentNode) { - - $start = $parentNode->getAttribute('start'); - if(!$start) { - throw new \Sabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element'); - } - $start = VObject\DateTimeParser::parseDateTime($start); - - $end = $parentNode->getAttribute('end'); - if(!$end) { - throw new \Sabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element'); - } - - $end = VObject\DateTimeParser::parseDateTime($end); - - if ($end <= $start) { - throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); - } - - return array( - 'start' => $start, - 'end' => $end, - ); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php deleted file mode 100644 index c9cb46338..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarQueryValidator.php +++ /dev/null @@ -1,392 +0,0 @@ -name !== $filters['name']) { - return false; - } - - return - $this->validateCompFilters($vObject, $filters['comp-filters']) && - $this->validatePropFilters($vObject, $filters['prop-filters']); - - - } - - /** - * This method checks the validity of comp-filters. - * - * A list of comp-filters needs to be specified. Also the parent of the - * component we're checking should be specified, not the component to check - * itself. - * - * @param VObject\Component $parent - * @param array $filters - * @return bool - */ - protected function validateCompFilters(VObject\Component $parent, array $filters) { - - foreach($filters as $filter) { - - $isDefined = isset($parent->$filter['name']); - - if ($filter['is-not-defined']) { - - if ($isDefined) { - return false; - } else { - continue; - } - - } - if (!$isDefined) { - return false; - } - - if ($filter['time-range']) { - foreach($parent->$filter['name'] as $subComponent) { - if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { - continue 2; - } - } - return false; - } - - if (!$filter['comp-filters'] && !$filter['prop-filters']) { - continue; - } - - // If there are sub-filters, we need to find at least one component - // for which the subfilters hold true. - foreach($parent->$filter['name'] as $subComponent) { - - if ( - $this->validateCompFilters($subComponent, $filter['comp-filters']) && - $this->validatePropFilters($subComponent, $filter['prop-filters'])) { - // We had a match, so this comp-filter succeeds - continue 2; - } - - } - - // If we got here it means there were sub-comp-filters or - // sub-prop-filters and there was no match. This means this filter - // needs to return false. - return false; - - } - - // If we got here it means we got through all comp-filters alive so the - // filters were all true. - return true; - - } - - /** - * This method checks the validity of prop-filters. - * - * A list of prop-filters needs to be specified. Also the parent of the - * property we're checking should be specified, not the property to check - * itself. - * - * @param VObject\Component $parent - * @param array $filters - * @return bool - */ - protected function validatePropFilters(VObject\Component $parent, array $filters) { - - foreach($filters as $filter) { - - $isDefined = isset($parent->$filter['name']); - - if ($filter['is-not-defined']) { - - if ($isDefined) { - return false; - } else { - continue; - } - - } - if (!$isDefined) { - return false; - } - - if ($filter['time-range']) { - foreach($parent->$filter['name'] as $subComponent) { - if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { - continue 2; - } - } - return false; - } - - if (!$filter['param-filters'] && !$filter['text-match']) { - continue; - } - - // If there are sub-filters, we need to find at least one property - // for which the subfilters hold true. - foreach($parent->$filter['name'] as $subComponent) { - - if( - $this->validateParamFilters($subComponent, $filter['param-filters']) && - (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match'])) - ) { - // We had a match, so this prop-filter succeeds - continue 2; - } - - } - - // If we got here it means there were sub-param-filters or - // text-match filters and there was no match. This means the - // filter needs to return false. - return false; - - } - - // If we got here it means we got through all prop-filters alive so the - // filters were all true. - return true; - - } - - /** - * This method checks the validity of param-filters. - * - * A list of param-filters needs to be specified. Also the parent of the - * parameter we're checking should be specified, not the parameter to check - * itself. - * - * @param VObject\Property $parent - * @param array $filters - * @return bool - */ - protected function validateParamFilters(VObject\Property $parent, array $filters) { - - foreach($filters as $filter) { - - $isDefined = isset($parent[$filter['name']]); - - if ($filter['is-not-defined']) { - - if ($isDefined) { - return false; - } else { - continue; - } - - } - if (!$isDefined) { - return false; - } - - if (!$filter['text-match']) { - continue; - } - - if (version_compare(VObject\Version::VERSION, '3.0.0beta1', '>=')) { - - // If there are sub-filters, we need to find at least one parameter - // for which the subfilters hold true. - foreach($parent[$filter['name']]->getParts() as $subParam) { - - if($this->validateTextMatch($subParam,$filter['text-match'])) { - // We had a match, so this param-filter succeeds - continue 2; - } - - } - - } else { - - // If there are sub-filters, we need to find at least one parameter - // for which the subfilters hold true. - foreach($parent[$filter['name']] as $subParam) { - - if($this->validateTextMatch($subParam,$filter['text-match'])) { - // We had a match, so this param-filter succeeds - continue 2; - } - - } - - } - - // If we got here it means there was a text-match filter and there - // were no matches. This means the filter needs to return false. - return false; - - } - - // If we got here it means we got through all param-filters alive so the - // filters were all true. - return true; - - } - - /** - * This method checks the validity of a text-match. - * - * A single text-match should be specified as well as the specific property - * or parameter we need to validate. - * - * @param VObject\Node|string $check Value to check against. - * @param array $textMatch - * @return bool - */ - protected function validateTextMatch($check, array $textMatch) { - - if ($check instanceof VObject\Node) { - $check = (string)$check; - } - - $isMatching = \Sabre\DAV\StringUtil::textMatch($check, $textMatch['value'], $textMatch['collation']); - - return ($textMatch['negate-condition'] xor $isMatching); - - } - - /** - * Validates if a component matches the given time range. - * - * This is all based on the rules specified in rfc4791, which are quite - * complex. - * - * @param VObject\Node $component - * @param DateTime $start - * @param DateTime $end - * @return bool - */ - protected function validateTimeRange(VObject\Node $component, $start, $end) { - - if (is_null($start)) { - $start = new DateTime('1900-01-01'); - } - if (is_null($end)) { - $end = new DateTime('3000-01-01'); - } - - switch($component->name) { - - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : - - return $component->isInTimeRange($start, $end); - - case 'VALARM' : - - // If the valarm is wrapped in a recurring event, we need to - // expand the recursions, and validate each. - // - // Our datamodel doesn't easily allow us to do this straight - // in the VALARM component code, so this is a hack, and an - // expensive one too. - if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { - - // Fire up the iterator! - $it = new VObject\RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); - while($it->valid()) { - $expandedEvent = $it->getEventObject(); - - // We need to check from these expanded alarms, which - // one is the first to trigger. Based on this, we can - // determine if we can 'give up' expanding events. - $firstAlarm = null; - if ($expandedEvent->VALARM !== null) { - foreach($expandedEvent->VALARM as $expandedAlarm) { - - $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); - if ($expandedAlarm->isInTimeRange($start, $end)) { - return true; - } - - if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { - // This is an alarm with a non-relative trigger - // time, likely created by a buggy client. The - // implication is that every alarm in this - // recurring event trigger at the exact same - // time. It doesn't make sense to traverse - // further. - } else { - // We store the first alarm as a means to - // figure out when we can stop traversing. - if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { - $firstAlarm = $effectiveTrigger; - } - } - } - } - if (is_null($firstAlarm)) { - // No alarm was found. - // - // Or technically: No alarm that will change for - // every instance of the recurrence was found, - // which means we can assume there was no match. - return false; - } - if ($firstAlarm > $end) { - return false; - } - $it->next(); - } - return false; - } else { - return $component->isInTimeRange($start, $end); - } - - case 'VFREEBUSY' : - throw new \Sabre\DAV\Exception\NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); - - case 'COMPLETED' : - case 'CREATED' : - case 'DTEND' : - case 'DTSTAMP' : - case 'DTSTART' : - case 'DUE' : - case 'LAST-MODIFIED' : - return ($start <= $component->getDateTime() && $end >= $component->getDateTime()); - - - - default : - throw new \Sabre\DAV\Exception\BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php deleted file mode 100644 index 905155a60..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/CalendarRootNode.php +++ /dev/null @@ -1,77 +0,0 @@ -caldavBackend = $caldavBackend; - - } - - /** - * Returns the nodename - * - * We're overriding this, because the default will be the 'principalPrefix', - * and we want it to be Sabre\CalDAV\Plugin::CALENDAR_ROOT - * - * @return string - */ - public function getName() { - - return Plugin::CALENDAR_ROOT; - - } - - /** - * This method returns a node for a principal. - * - * The passed array contains principal information, and is guaranteed to - * at least contain a uri item. Other properties may or may not be - * supplied by the authentication backend. - * - * @param array $principal - * @return \Sabre\DAV\INode - */ - public function getChildForPrincipal(array $principal) { - - return new UserCalendars($this->caldavBackend, $principal); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Exception/InvalidComponentType.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Exception/InvalidComponentType.php deleted file mode 100644 index 9c92672b6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Exception/InvalidComponentType.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV,'cal:supported-calendar-component'); - $errorNode->appendChild($np); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICSExportPlugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICSExportPlugin.php deleted file mode 100644 index 1cb0799c6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICSExportPlugin.php +++ /dev/null @@ -1,142 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); - - } - - /** - * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?export - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if ($method!='GET') return; - if ($this->server->httpRequest->getQueryString()!='export') return; - - // splitting uri - list($uri) = explode('?',$uri,2); - - $node = $this->server->tree->getNodeForPath($uri); - - if (!($node instanceof Calendar)) return; - - // Checking ACL, if available. - if ($aclPlugin = $this->server->getPlugin('acl')) { - $aclPlugin->checkPrivileges($uri, '{DAV:}read'); - } - - $this->server->httpResponse->setHeader('Content-Type','text/calendar'); - $this->server->httpResponse->sendStatus(200); - - $nodes = $this->server->getPropertiesForPath($uri, array( - '{' . Plugin::NS_CALDAV . '}calendar-data', - ),1); - - $this->server->httpResponse->sendBody($this->generateICS($nodes)); - - // Returning false to break the event chain - return false; - - } - - /** - * Merges all calendar objects, and builds one big ics export - * - * @param array $nodes - * @return string - */ - public function generateICS(array $nodes) { - - $calendar = new VObject\Component\VCalendar(); - $calendar->version = '2.0'; - if (DAV\Server::$exposeVersion) { - $calendar->prodid = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; - } else { - $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; - } - $calendar->calscale = 'GREGORIAN'; - - $collectedTimezones = array(); - - $timezones = array(); - $objects = array(); - - foreach($nodes as $node) { - - if (!isset($node[200]['{' . Plugin::NS_CALDAV . '}calendar-data'])) { - continue; - } - $nodeData = $node[200]['{' . Plugin::NS_CALDAV . '}calendar-data']; - - $nodeComp = VObject\Reader::read($nodeData); - - foreach($nodeComp->children() as $child) { - - switch($child->name) { - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : - $objects[] = $child; - break; - - // VTIMEZONE is special, because we need to filter out the duplicates - case 'VTIMEZONE' : - // Naively just checking tzid. - if (!in_array((string)$child->TZID, $collectedTimezones)) { - $timezones[] = $child; - $collectedTimezones[] = $child->TZID; - } - break; - - } - - } - - } - - foreach($timezones as $tz) $calendar->add($tz); - foreach($objects as $obj) $calendar->add($obj); - - return $calendar->serialize(); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICalendar.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICalendar.php deleted file mode 100644 index c56a81a1b..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ICalendar.php +++ /dev/null @@ -1,36 +0,0 @@ -caldavBackend = $caldavBackend; - $this->principalUri = $principalUri; - - } - - /** - * Returns all notifications for a principal - * - * @return array - */ - public function getChildren() { - - $children = array(); - $notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri); - - foreach($notifications as $notification) { - - $children[] = new Node( - $this->caldavBackend, - $this->principalUri, - $notification - ); - } - - return $children; - - } - - /** - * Returns the name of this object - * - * @return string - */ - public function getName() { - - return 'notifications'; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}read', - 'protected' => true, - ), - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}write', - 'protected' => true, - ) - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's as an array argument. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/ICollection.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/ICollection.php deleted file mode 100644 index 99324648b..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/ICollection.php +++ /dev/null @@ -1,24 +0,0 @@ -caldavBackend = $caldavBackend; - $this->principalUri = $principalUri; - $this->notification = $notification; - - } - - /** - * Returns the path name for this notification - * - * @return id - */ - public function getName() { - - return $this->notification->getId() . '.xml'; - - } - - /** - * Returns the etag for the notification. - * - * The etag must be surrounded by litteral double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->notification->getETag(); - - } - - /** - * This method must return an xml element, using the - * Sabre\CalDAV\Notifications\INotificationType classes. - * - * @return INotificationType - */ - public function getNotificationType() { - - return $this->notification; - - } - - /** - * Deletes this notification - * - * @return void - */ - public function delete() { - - $this->caldavBackend->deleteNotification($this->getOwner(), $this->notification); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}read', - 'protected' => true, - ), - array( - 'principal' => $this->getOwner(), - 'privilege' => '{DAV:}write', - 'protected' => true, - ) - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's as an array argument. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/Invite.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/Invite.php deleted file mode 100644 index 72f82e2b1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/Invite.php +++ /dev/null @@ -1,324 +0,0 @@ -$value) { - if (!property_exists($this, $key)) { - throw new \InvalidArgumentException('Unknown option: ' . $key); - } - $this->$key = $value; - } - - } - - /** - * Serializes the notification as a single property. - * - * You should usually just encode the single top-level element of the - * notification. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $prop = $node->ownerDocument->createElement('cs:invite-notification'); - $node->appendChild($prop); - - } - - /** - * This method serializes the entire notification, as it is used in the - * response body. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serializeBody(DAV\Server $server, \DOMElement $node) { - - $doc = $node->ownerDocument; - - $dt = $doc->createElement('cs:dtstamp'); - $this->dtStamp->setTimezone(new \DateTimezone('GMT')); - $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); - $node->appendChild($dt); - - $prop = $doc->createElement('cs:invite-notification'); - $node->appendChild($prop); - - $uid = $doc->createElement('cs:uid'); - $uid->appendChild( $doc->createTextNode($this->id) ); - $prop->appendChild($uid); - - $href = $doc->createElement('d:href'); - $href->appendChild( $doc->createTextNode( $this->href ) ); - $prop->appendChild($href); - - $nodeName = null; - switch($this->type) { - - case SharingPlugin::STATUS_ACCEPTED : - $nodeName = 'cs:invite-accepted'; - break; - case SharingPlugin::STATUS_DECLINED : - $nodeName = 'cs:invite-declined'; - break; - case SharingPlugin::STATUS_DELETED : - $nodeName = 'cs:invite-deleted'; - break; - case SharingPlugin::STATUS_NORESPONSE : - $nodeName = 'cs:invite-noresponse'; - break; - - } - $prop->appendChild( - $doc->createElement($nodeName) - ); - $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); - $hostUrl = $doc->createElement('cs:hosturl'); - $hostUrl->appendChild($hostHref); - $prop->appendChild($hostUrl); - - $access = $doc->createElement('cs:access'); - if ($this->readOnly) { - $access->appendChild($doc->createElement('cs:read')); - } else { - $access->appendChild($doc->createElement('cs:read-write')); - } - $prop->appendChild($access); - - $organizerUrl = $doc->createElement('cs:organizer'); - // If the organizer contains a 'mailto:' part, it means it should be - // treated as absolute. - if (strtolower(substr($this->organizer,0,7))==='mailto:') { - $organizerHref = new DAV\Property\Href($this->organizer, false); - } else { - $organizerHref = new DAV\Property\Href($this->organizer, true); - } - $organizerHref->serialize($server, $organizerUrl); - - if ($this->commonName) { - $commonName = $doc->createElement('cs:common-name'); - $commonName->appendChild($doc->createTextNode($this->commonName)); - $organizerUrl->appendChild($commonName); - - $commonNameOld = $doc->createElement('cs:organizer-cn'); - $commonNameOld->appendChild($doc->createTextNode($this->commonName)); - $prop->appendChild($commonNameOld); - - } - if ($this->firstName) { - $firstName = $doc->createElement('cs:first-name'); - $firstName->appendChild($doc->createTextNode($this->firstName)); - $organizerUrl->appendChild($firstName); - - $firstNameOld = $doc->createElement('cs:organizer-first'); - $firstNameOld->appendChild($doc->createTextNode($this->firstName)); - $prop->appendChild($firstNameOld); - } - if ($this->lastName) { - $lastName = $doc->createElement('cs:last-name'); - $lastName->appendChild($doc->createTextNode($this->lastName)); - $organizerUrl->appendChild($lastName); - - $lastNameOld = $doc->createElement('cs:organizer-last'); - $lastNameOld->appendChild($doc->createTextNode($this->lastName)); - $prop->appendChild($lastNameOld); - } - $prop->appendChild($organizerUrl); - - if ($this->summary) { - $summary = $doc->createElement('cs:summary'); - $summary->appendChild($doc->createTextNode($this->summary)); - $prop->appendChild($summary); - } - if ($this->supportedComponents) { - - $xcomp = $doc->createElement('cal:supported-calendar-component-set'); - $this->supportedComponents->serialize($server, $xcomp); - $prop->appendChild($xcomp); - - } - - } - - /** - * Returns a unique id for this notification - * - * This is just the base url. This should generally be some kind of unique - * id. - * - * @return string - */ - public function getId() { - - return $this->id; - - } - - /** - * Returns the ETag for this notification. - * - * The ETag must be surrounded by literal double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->etag; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/InviteReply.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/InviteReply.php deleted file mode 100644 index a7ec277ea..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/InviteReply.php +++ /dev/null @@ -1,218 +0,0 @@ -$value) { - if (!property_exists($this, $key)) { - throw new \InvalidArgumentException('Unknown option: ' . $key); - } - $this->$key = $value; - } - - } - - /** - * Serializes the notification as a single property. - * - * You should usually just encode the single top-level element of the - * notification. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $prop = $node->ownerDocument->createElement('cs:invite-reply'); - $node->appendChild($prop); - - } - - /** - * This method serializes the entire notification, as it is used in the - * response body. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serializeBody(DAV\Server $server, \DOMElement $node) { - - $doc = $node->ownerDocument; - - $dt = $doc->createElement('cs:dtstamp'); - $this->dtStamp->setTimezone(new \DateTimezone('GMT')); - $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); - $node->appendChild($dt); - - $prop = $doc->createElement('cs:invite-reply'); - $node->appendChild($prop); - - $uid = $doc->createElement('cs:uid'); - $uid->appendChild($doc->createTextNode($this->id)); - $prop->appendChild($uid); - - $inReplyTo = $doc->createElement('cs:in-reply-to'); - $inReplyTo->appendChild( $doc->createTextNode($this->inReplyTo) ); - $prop->appendChild($inReplyTo); - - $href = $doc->createElement('d:href'); - $href->appendChild( $doc->createTextNode($this->href) ); - $prop->appendChild($href); - - $nodeName = null; - switch($this->type) { - - case SharingPlugin::STATUS_ACCEPTED : - $nodeName = 'cs:invite-accepted'; - break; - case SharingPlugin::STATUS_DECLINED : - $nodeName = 'cs:invite-declined'; - break; - - } - $prop->appendChild( - $doc->createElement($nodeName) - ); - $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); - $hostUrl = $doc->createElement('cs:hosturl'); - $hostUrl->appendChild($hostHref); - $prop->appendChild($hostUrl); - - if ($this->summary) { - $summary = $doc->createElement('cs:summary'); - $summary->appendChild($doc->createTextNode($this->summary)); - $prop->appendChild($summary); - } - - } - - /** - * Returns a unique id for this notification - * - * This is just the base url. This should generally be some kind of unique - * id. - * - * @return string - */ - public function getId() { - - return $this->id; - - } - - /** - * Returns the ETag for this notification. - * - * The ETag must be surrounded by literal double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->etag; - - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/SystemStatus.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/SystemStatus.php deleted file mode 100644 index 4c162c032..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Notifications/Notification/SystemStatus.php +++ /dev/null @@ -1,182 +0,0 @@ -id = $id; - $this->type = $type; - $this->description = $description; - $this->href = $href; - $this->etag = $etag; - - } - - /** - * Serializes the notification as a single property. - * - * You should usually just encode the single top-level element of the - * notification. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - switch($this->type) { - case self::TYPE_LOW : - $type = 'low'; - break; - case self::TYPE_MEDIUM : - $type = 'medium'; - break; - default : - case self::TYPE_HIGH : - $type = 'high'; - break; - } - - $prop = $node->ownerDocument->createElement('cs:systemstatus'); - $prop->setAttribute('type', $type); - - $node->appendChild($prop); - - } - - /** - * This method serializes the entire notification, as it is used in the - * response body. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serializeBody(DAV\Server $server, \DOMElement $node) { - - switch($this->type) { - case self::TYPE_LOW : - $type = 'low'; - break; - case self::TYPE_MEDIUM : - $type = 'medium'; - break; - default : - case self::TYPE_HIGH : - $type = 'high'; - break; - } - - $prop = $node->ownerDocument->createElement('cs:systemstatus'); - $prop->setAttribute('type', $type); - - if ($this->description) { - $text = $node->ownerDocument->createTextNode($this->description); - $desc = $node->ownerDocument->createElement('cs:description'); - $desc->appendChild($text); - $prop->appendChild($desc); - } - if ($this->href) { - $text = $node->ownerDocument->createTextNode($this->href); - $href = $node->ownerDocument->createElement('d:href'); - $href->appendChild($text); - $prop->appendChild($href); - } - - $node->appendChild($prop); - - } - - /** - * Returns a unique id for this notification - * - * This is just the base url. This should generally be some kind of unique - * id. - * - * @return string - */ - public function getId() { - - return $this->id; - - } - - /* - * Returns the ETag for this notification. - * - * The ETag must be surrounded by literal double-quotes. - * - * @return string - */ - public function getETag() { - - return $this->etag; - - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Plugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Plugin.php deleted file mode 100644 index 1b1a1dd6d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Plugin.php +++ /dev/null @@ -1,1338 +0,0 @@ -imipHandler = $imipHandler; - - } - - /** - * Use this method to tell the server this plugin defines additional - * HTTP methods. - * - * This method is passed a uri. It should only return HTTP methods that are - * available for the specified uri. - * - * @param string $uri - * @return array - */ - public function getHTTPMethods($uri) { - - // The MKCALENDAR is only available on unmapped uri's, whose - // parents extend IExtendedCollection - list($parent, $name) = DAV\URLUtil::splitPath($uri); - - $node = $this->server->tree->getNodeForPath($parent); - - if ($node instanceof DAV\IExtendedCollection) { - try { - $node->getChild($name); - } catch (DAV\Exception\NotFound $e) { - return array('MKCALENDAR'); - } - } - return array(); - - } - - /** - * Returns a list of features for the DAV: HTTP header. - * - * @return array - */ - public function getFeatures() { - - return array('calendar-access', 'calendar-proxy'); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'caldav'; - - } - - /** - * Returns a list of reports this plugin supports. - * - * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * - * @param string $uri - * @return array - */ - public function getSupportedReportSet($uri) { - - $node = $this->server->tree->getNodeForPath($uri); - - $reports = array(); - if ($node instanceof ICalendar || $node instanceof ICalendarObject) { - $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; - $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; - } - if ($node instanceof ICalendar) { - $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; - } - return $reports; - - } - - /** - * Initializes the plugin - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - - $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); - //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); - $server->subscribeEvent('report',array($this,'report')); - $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); - $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); - $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); - $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); - $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); - $server->subscribeEvent('beforeMethod', array($this,'beforeMethod')); - - $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; - $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; - - $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Property\\SupportedCalendarComponentSet'; - $server->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Property\\ScheduleCalendarTransp'; - - $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; - - array_push($server->protectedProperties, - - '{' . self::NS_CALDAV . '}supported-calendar-component-set', - '{' . self::NS_CALDAV . '}supported-calendar-data', - '{' . self::NS_CALDAV . '}max-resource-size', - '{' . self::NS_CALDAV . '}min-date-time', - '{' . self::NS_CALDAV . '}max-date-time', - '{' . self::NS_CALDAV . '}max-instances', - '{' . self::NS_CALDAV . '}max-attendees-per-instance', - '{' . self::NS_CALDAV . '}calendar-home-set', - '{' . self::NS_CALDAV . '}supported-collation-set', - '{' . self::NS_CALDAV . '}calendar-data', - - // scheduling extension - '{' . self::NS_CALDAV . '}schedule-inbox-URL', - '{' . self::NS_CALDAV . '}schedule-outbox-URL', - '{' . self::NS_CALDAV . '}calendar-user-address-set', - '{' . self::NS_CALDAV . '}calendar-user-type', - - // CalendarServer extensions - '{' . self::NS_CALENDARSERVER . '}getctag', - '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', - '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for', - '{' . self::NS_CALENDARSERVER . '}notification-URL', - '{' . self::NS_CALENDARSERVER . '}notificationtype' - - ); - } - - /** - * This function handles support for the MKCALENDAR method - * - * @param string $method - * @param string $uri - * @return bool - */ - public function unknownMethod($method, $uri) { - - switch ($method) { - case 'MKCALENDAR' : - $this->httpMkCalendar($uri); - // false is returned to stop the propagation of the - // unknownMethod event. - return false; - case 'POST' : - - // Checking if this is a text/calendar content type - $contentType = $this->server->httpRequest->getHeader('Content-Type'); - if (strpos($contentType, 'text/calendar')!==0) { - return; - } - - // Checking if we're talking to an outbox - try { - $node = $this->server->tree->getNodeForPath($uri); - } catch (DAV\Exception\NotFound $e) { - return; - } - if (!$node instanceof Schedule\IOutbox) - return; - - $this->outboxRequest($node, $uri); - return false; - - } - - } - - /** - * This functions handles REPORT requests specific to CalDAV - * - * @param string $reportName - * @param \DOMNode $dom - * @return bool - */ - public function report($reportName,$dom) { - - switch($reportName) { - case '{'.self::NS_CALDAV.'}calendar-multiget' : - $this->calendarMultiGetReport($dom); - return false; - case '{'.self::NS_CALDAV.'}calendar-query' : - $this->calendarQueryReport($dom); - return false; - case '{'.self::NS_CALDAV.'}free-busy-query' : - $this->freeBusyQueryReport($dom); - return false; - - } - - - } - - /** - * This function handles the MKCALENDAR HTTP method, which creates - * a new calendar. - * - * @param string $uri - * @return void - */ - public function httpMkCalendar($uri) { - - // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support - // for clients matching iCal in the user agent - //$ua = $this->server->httpRequest->getHeader('User-Agent'); - //if (strpos($ua,'iCal/')!==false) { - // throw new \Sabre\DAV\Exception\Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); - //} - - $body = $this->server->httpRequest->getBody(true); - $properties = array(); - - if ($body) { - - $dom = DAV\XMLUtil::loadDOMDocument($body); - - foreach($dom->firstChild->childNodes as $child) { - - if (DAV\XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; - foreach(DAV\XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { - $properties[$k] = $prop; - } - - } - } - - $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); - - $this->server->createCollection($uri,$resourceType,$properties); - - $this->server->httpResponse->sendStatus(201); - $this->server->httpResponse->setHeader('Content-Length',0); - } - - /** - * beforeGetProperties - * - * This method handler is invoked before any after properties for a - * resource are fetched. This allows us to add in any CalDAV specific - * properties. - * - * @param string $path - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @return void - */ - public function beforeGetProperties($path, DAV\INode $node, &$requestedProperties, &$returnedProperties) { - - if ($node instanceof DAVACL\IPrincipal) { - - // calendar-home-set property - $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; - if (in_array($calHome,$requestedProperties)) { - $principalId = $node->getName(); - $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; - - unset($requestedProperties[array_search($calHome, $requestedProperties)]); - $returnedProperties[200][$calHome] = new DAV\Property\Href($calendarHomePath); - - } - - // schedule-outbox-URL property - $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL'; - if (in_array($scheduleProp,$requestedProperties)) { - $principalId = $node->getName(); - $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox'; - - unset($requestedProperties[array_search($scheduleProp, $requestedProperties)]); - $returnedProperties[200][$scheduleProp] = new DAV\Property\Href($outboxPath); - - } - - // calendar-user-address-set property - $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; - if (in_array($calProp,$requestedProperties)) { - - $addresses = $node->getAlternateUriSet(); - $addresses[] = $this->server->getBaseUri() . DAV\URLUtil::encodePath($node->getPrincipalUrl() . '/'); - unset($requestedProperties[array_search($calProp, $requestedProperties)]); - $returnedProperties[200][$calProp] = new DAV\Property\HrefList($addresses, false); - - } - - // These two properties are shortcuts for ical to easily find - // other principals this principal has access to. - $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; - $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; - if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) { - - $aclPlugin = $this->server->getPlugin('acl'); - $membership = $aclPlugin->getPrincipalMembership($path); - $readList = array(); - $writeList = array(); - - foreach($membership as $group) { - - $groupNode = $this->server->tree->getNodeForPath($group); - - // If the node is either ap proxy-read or proxy-write - // group, we grab the parent principal and add it to the - // list. - if ($groupNode instanceof Principal\IProxyRead) { - list($readList[]) = DAV\URLUtil::splitPath($group); - } - if ($groupNode instanceof Principal\IProxyWrite) { - list($writeList[]) = DAV\URLUtil::splitPath($group); - } - - } - if (in_array($propRead,$requestedProperties)) { - unset($requestedProperties[$propRead]); - $returnedProperties[200][$propRead] = new DAV\Property\HrefList($readList); - } - if (in_array($propWrite,$requestedProperties)) { - unset($requestedProperties[$propWrite]); - $returnedProperties[200][$propWrite] = new DAV\Property\HrefList($writeList); - } - - } - - // notification-URL property - $notificationUrl = '{' . self::NS_CALENDARSERVER . '}notification-URL'; - if (($index = array_search($notificationUrl, $requestedProperties)) !== false) { - $principalId = $node->getName(); - $calendarHomePath = 'calendars/' . $principalId . '/notifications/'; - unset($requestedProperties[$index]); - $returnedProperties[200][$notificationUrl] = new DAV\Property\Href($calendarHomePath); - } - - } // instanceof IPrincipal - - if ($node instanceof Notifications\INode) { - - $propertyName = '{' . self::NS_CALENDARSERVER . '}notificationtype'; - if (($index = array_search($propertyName, $requestedProperties)) !== false) { - - $returnedProperties[200][$propertyName] = - $node->getNotificationType(); - - unset($requestedProperties[$index]); - - } - - } // instanceof Notifications_INode - - - if ($node instanceof ICalendarObject) { - // The calendar-data property is not supposed to be a 'real' - // property, but in large chunks of the spec it does act as such. - // Therefore we simply expose it as a property. - $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data'; - if (in_array($calDataProp, $requestedProperties)) { - unset($requestedProperties[$calDataProp]); - $val = $node->get(); - if (is_resource($val)) - $val = stream_get_contents($val); - - // Taking out \r to not screw up the xml output - $returnedProperties[200][$calDataProp] = str_replace("\r","", $val); - - } - } - - } - - /** - * This function handles the calendar-multiget REPORT. - * - * This report is used by the client to fetch the content of a series - * of urls. Effectively avoiding a lot of redundant requests. - * - * @param \DOMNode $dom - * @return void - */ - public function calendarMultiGetReport($dom) { - - $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)); - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); - - $xpath = new \DOMXPath($dom); - $xpath->registerNameSpace('cal',Plugin::NS_CALDAV); - $xpath->registerNameSpace('dav','urn:DAV'); - - $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); - if ($expand->length>0) { - $expandElem = $expand->item(0); - $start = $expandElem->getAttribute('start'); - $end = $expandElem->getAttribute('end'); - if(!$start || !$end) { - throw new DAV\Exception\BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); - } - $start = VObject\DateTimeParser::parseDateTime($start); - $end = VObject\DateTimeParser::parseDateTime($end); - - if ($end <= $start) { - throw new DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); - } - - $expand = true; - - } else { - - $expand = false; - - } - - foreach($hrefElems as $elem) { - $uri = $this->server->calculateUri($elem->nodeValue); - list($objProps) = $this->server->getPropertiesForPath($uri,$properties); - - if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { - $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); - $vObject->expand($start, $end); - $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); - } - - $propertyList[]=$objProps; - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); - - } - - /** - * This function handles the calendar-query REPORT - * - * This report is used by clients to request calendar objects based on - * complex conditions. - * - * @param \DOMNode $dom - * @return void - */ - public function calendarQueryReport($dom) { - - $parser = new CalendarQueryParser($dom); - $parser->parse(); - - $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); - $depth = $this->server->getHTTPDepth(0); - - // The default result is an empty array - $result = array(); - - // The calendarobject was requested directly. In this case we handle - // this locally. - if ($depth == 0 && $node instanceof ICalendarObject) { - - $requestedCalendarData = true; - $requestedProperties = $parser->requestedProperties; - - if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { - - // We always retrieve calendar-data, as we need it for filtering. - $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; - - // If calendar-data wasn't explicitly requested, we need to remove - // it after processing. - $requestedCalendarData = false; - } - - $properties = $this->server->getPropertiesForPath( - $this->server->getRequestUri(), - $requestedProperties, - 0 - ); - - // This array should have only 1 element, the first calendar - // object. - $properties = current($properties); - - // If there wasn't any calendar-data returned somehow, we ignore - // this. - if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { - - $validator = new CalendarQueryValidator(); - - $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - if ($validator->validate($vObject,$parser->filters)) { - - // If the client didn't require the calendar-data property, - // we won't give it back. - if (!$requestedCalendarData) { - unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - } else { - if ($parser->expand) { - $vObject->expand($parser->expand['start'], $parser->expand['end']); - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); - } - } - - $result = array($properties); - - } - - } - - } - // If we're dealing with a calendar, the calendar itself is responsible - // for the calendar-query. - if ($node instanceof ICalendar && $depth = 1) { - - $nodePaths = $node->calendarQuery($parser->filters); - - foreach($nodePaths as $path) { - - list($properties) = - $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); - - if ($parser->expand) { - // We need to do some post-processing - $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - $vObject->expand($parser->expand['start'], $parser->expand['end']); - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); - } - - $result[] = $properties; - - } - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); - - } - - /** - * This method is responsible for parsing the request and generating the - * response for the CALDAV:free-busy-query REPORT. - * - * @param \DOMNode $dom - * @return void - */ - protected function freeBusyQueryReport(\DOMNode $dom) { - - $start = null; - $end = null; - - foreach($dom->firstChild->childNodes as $childNode) { - - $clark = DAV\XMLUtil::toClarkNotation($childNode); - if ($clark == '{' . self::NS_CALDAV . '}time-range') { - $start = $childNode->getAttribute('start'); - $end = $childNode->getAttribute('end'); - break; - } - - } - if ($start) { - $start = VObject\DateTimeParser::parseDateTime($start); - } - if ($end) { - $end = VObject\DateTimeParser::parseDateTime($end); - } - - if (!$start && !$end) { - throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter'); - } - $acl = $this->server->getPlugin('acl'); - - if (!$acl) { - throw new DAV\Exception('The ACL plugin must be loaded for free-busy queries to work'); - } - $uri = $this->server->getRequestUri(); - $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); - - $calendar = $this->server->tree->getNodeForPath($uri); - if (!$calendar instanceof ICalendar) { - throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); - } - - // Doing a calendar-query first, to make sure we get the most - // performance. - $urls = $calendar->calendarQuery(array( - 'name' => 'VCALENDAR', - 'comp-filters' => array( - array( - 'name' => 'VEVENT', - 'comp-filters' => array(), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => array( - 'start' => $start, - 'end' => $end, - ), - ), - ), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => null, - )); - - $objects = array_map(function($url) use ($calendar) { - $obj = $calendar->getChild($url)->get(); - return $obj; - }, $urls); - - $generator = new VObject\FreeBusyGenerator(); - $generator->setObjects($objects); - $generator->setTimeRange($start, $end); - $result = $generator->getResult(); - $result = $result->serialize(); - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); - $this->server->httpResponse->setHeader('Content-Length', strlen($result)); - $this->server->httpResponse->sendBody($result); - - } - - /** - * This method is triggered before a file gets updated with new content. - * - * This plugin uses this method to ensure that CalDAV objects receive - * valid calendar data. - * - * @param string $path - * @param DAV\IFile $node - * @param resource $data - * @return void - */ - public function beforeWriteContent($path, DAV\IFile $node, &$data) { - - if (!$node instanceof ICalendarObject) - return; - - $this->validateICalendar($data, $path); - - } - - /** - * This method is triggered before a new file is created. - * - * This plugin uses this method to ensure that newly created calendar - * objects contain valid calendar data. - * - * @param string $path - * @param resource $data - * @param DAV\ICollection $parentNode - * @return void - */ - public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode) { - - if (!$parentNode instanceof Calendar) - return; - - $this->validateICalendar($data, $path); - - } - - /** - * This event is triggered before any HTTP request is handled. - * - * We use this to intercept GET calls to notification nodes, and return the - * proper response. - * - * @param string $method - * @param string $path - * @return void - */ - public function beforeMethod($method, $path) { - - if ($method!=='GET') return; - - try { - $node = $this->server->tree->getNodeForPath($path); - } catch (DAV\Exception\NotFound $e) { - return; - } - - if (!$node instanceof Notifications\INode) - return; - - if (!$this->server->checkPreconditions(true)) return false; - $dom = new \DOMDocument('1.0', 'UTF-8'); - - $dom->formatOutput = true; - - $root = $dom->createElement('cs:notification'); - foreach($this->server->xmlNamespaces as $namespace => $prefix) { - $root->setAttribute('xmlns:' . $prefix, $namespace); - } - - $dom->appendChild($root); - $node->getNotificationType()->serializeBody($this->server, $root); - - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->setHeader('ETag',$node->getETag()); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody($dom->saveXML()); - - return false; - - } - - /** - * Checks if the submitted iCalendar data is in fact, valid. - * - * An exception is thrown if it's not. - * - * @param resource|string $data - * @param string $path - * @return void - */ - protected function validateICalendar(&$data, $path) { - - // If it's a stream, we convert it to a string first. - if (is_resource($data)) { - $data = stream_get_contents($data); - } - - // Converting the data to unicode, if needed. - $data = DAV\StringUtil::ensureUTF8($data); - - try { - - $vobj = VObject\Reader::read($data); - - } catch (VObject\ParseException $e) { - - throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); - - } - - if ($vobj->name !== 'VCALENDAR') { - throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.'); - } - - // Get the Supported Components for the target calendar - list($parentPath,$object) = DAV\URLUtil::splitPath($path); - $calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set')); - $supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue(); - - $foundType = null; - $foundUID = null; - foreach($vobj->getComponents() as $component) { - switch($component->name) { - case 'VTIMEZONE' : - continue 2; - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : - if (is_null($foundType)) { - $foundType = $component->name; - if (!in_array($foundType, $supportedComponents)) { - throw new Exception\InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType); - } - if (!isset($component->UID)) { - throw new DAV\Exception\BadRequest('Every ' . $component->name . ' component must have an UID'); - } - $foundUID = (string)$component->UID; - } else { - if ($foundType !== $component->name) { - throw new DAV\Exception\BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); - } - if ($foundUID !== (string)$component->UID) { - throw new DAV\Exception\BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); - } - } - break; - default : - throw new DAV\Exception\BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); - - } - } - if (!$foundType) - throw new DAV\Exception\BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); - - } - - /** - * This method handles POST requests to the schedule-outbox. - * - * Currently, two types of requests are support: - * * FREEBUSY requests from RFC 6638 - * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04 - * - * The latter is from an expired early draft of the CalDAV scheduling - * extensions, but iCal depends on a feature from that spec, so we - * implement it. - * - * @param Schedule\IOutbox $outboxNode - * @param string $outboxUri - * @return void - */ - public function outboxRequest(Schedule\IOutbox $outboxNode, $outboxUri) { - - // Parsing the request body - try { - $vObject = VObject\Reader::read($this->server->httpRequest->getBody(true)); - } catch (VObject\ParseException $e) { - throw new DAV\Exception\BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); - } - - // The incoming iCalendar object must have a METHOD property, and a - // component. The combination of both determines what type of request - // this is. - $componentType = null; - foreach($vObject->getComponents() as $component) { - if ($component->name !== 'VTIMEZONE') { - $componentType = $component->name; - break; - } - } - if (is_null($componentType)) { - throw new DAV\Exception\BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); - } - - // Validating the METHOD - $method = strtoupper((string)$vObject->METHOD); - if (!$method) { - throw new DAV\Exception\BadRequest('A METHOD property must be specified in iTIP messages'); - } - - // So we support two types of requests: - // - // REQUEST with a VFREEBUSY component - // REQUEST, REPLY, ADD, CANCEL on VEVENT components - - $acl = $this->server->getPlugin('acl'); - - if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') { - - $acl && $acl->checkPrivileges($outboxUri,'{' . Plugin::NS_CALDAV . '}schedule-query-freebusy'); - $this->handleFreeBusyRequest($outboxNode, $vObject); - - } elseif ($componentType === 'VEVENT' && in_array($method, array('REQUEST','REPLY','ADD','CANCEL'))) { - - $acl && $acl->checkPrivileges($outboxUri,'{' . Plugin::NS_CALDAV . '}schedule-post-vevent'); - $this->handleEventNotification($outboxNode, $vObject); - - } else { - - throw new DAV\Exception\NotImplemented('SabreDAV supports only VFREEBUSY (REQUEST) and VEVENT (REQUEST, REPLY, ADD, CANCEL)'); - - } - - } - - /** - * This method handles the REQUEST, REPLY, ADD and CANCEL methods for - * VEVENT iTip messages. - * - * @return void - */ - protected function handleEventNotification(Schedule\IOutbox $outboxNode, VObject\Component $vObject) { - - $originator = $this->server->httpRequest->getHeader('Originator'); - $recipients = $this->server->httpRequest->getHeader('Recipient'); - - if (!$originator) { - throw new DAV\Exception\BadRequest('The Originator: header must be specified when making POST requests'); - } - if (!$recipients) { - throw new DAV\Exception\BadRequest('The Recipient: header must be specified when making POST requests'); - } - - $recipients = explode(',',$recipients); - foreach($recipients as $k=>$recipient) { - - $recipient = trim($recipient); - if (!preg_match('/^mailto:(.*)@(.*)$/i', $recipient)) { - throw new DAV\Exception\BadRequest('Recipients must start with mailto: and must be valid email address'); - } - $recipient = substr($recipient, 7); - $recipients[$k] = $recipient; - } - - // We need to make sure that 'originator' matches one of the email - // addresses of the selected principal. - $principal = $outboxNode->getOwner(); - $props = $this->server->getProperties($principal,array( - '{' . self::NS_CALDAV . '}calendar-user-address-set', - )); - - $addresses = array(); - if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) { - $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs(); - } - - $found = false; - foreach($addresses as $address) { - - // Trimming the / on both sides, just in case.. - if (rtrim(strtolower($originator),'/') === rtrim(strtolower($address),'/')) { - $found = true; - break; - } - - } - - if (!$found) { - throw new DAV\Exception\Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); - } - - // If the Originator header was a url, and not a mailto: address.. - // we're going to try to pull the mailto: from the vobject body. - if (strtolower(substr($originator,0,7)) !== 'mailto:') { - $originator = (string)$vObject->VEVENT->ORGANIZER; - - } - if (strtolower(substr($originator,0,7)) !== 'mailto:') { - throw new DAV\Exception\Forbidden('Could not find mailto: address in both the Orignator header, and the ORGANIZER property in the VEVENT'); - } - $originator = substr($originator,7); - - $result = $this->iMIPMessage($originator, $recipients, $vObject, $principal); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($this->generateScheduleResponse($result)); - - } - - /** - * Sends an iMIP message by email. - * - * This method must return an array with status codes per recipient. - * This should look something like: - * - * array( - * 'user1@example.org' => '2.0;Success' - * ) - * - * Formatting for this status code can be found at: - * https://tools.ietf.org/html/rfc5545#section-3.8.8.3 - * - * A list of valid status codes can be found at: - * https://tools.ietf.org/html/rfc5546#section-3.6 - * - * @param string $originator - * @param array $recipients - * @param VObject\Component $vObject - * @param string $principal Principal url - * @return array - */ - protected function iMIPMessage($originator, array $recipients, VObject\Component $vObject, $principal) { - - if (!$this->imipHandler) { - $resultStatus = '5.2;This server does not support this operation'; - } else { - $this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal); - $resultStatus = '2.0;Success'; - } - - $result = array(); - foreach($recipients as $recipient) { - $result[$recipient] = $resultStatus; - } - - return $result; - - } - - /** - * Generates a schedule-response XML body - * - * The recipients array is a key->value list, containing email addresses - * and iTip status codes. See the iMIPMessage method for a description of - * the value. - * - * @param array $recipients - * @return string - */ - public function generateScheduleResponse(array $recipients) { - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $xscheduleResponse = $dom->createElement('cal:schedule-response'); - $dom->appendChild($xscheduleResponse); - - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $xscheduleResponse->setAttribute('xmlns:' . $prefix, $namespace); - - } - - foreach($recipients as $recipient=>$status) { - $xresponse = $dom->createElement('cal:response'); - - $xrecipient = $dom->createElement('cal:recipient'); - $xrecipient->appendChild($dom->createTextNode($recipient)); - $xresponse->appendChild($xrecipient); - - $xrequestStatus = $dom->createElement('cal:request-status'); - $xrequestStatus->appendChild($dom->createTextNode($status)); - $xresponse->appendChild($xrequestStatus); - - $xscheduleResponse->appendChild($xresponse); - - } - - return $dom->saveXML(); - - } - - /** - * This method is responsible for parsing a free-busy query request and - * returning it's result. - * - * @param Schedule\IOutbox $outbox - * @param string $request - * @return string - */ - protected function handleFreeBusyRequest(Schedule\IOutbox $outbox, VObject\Component $vObject) { - - $vFreeBusy = $vObject->VFREEBUSY; - $organizer = $vFreeBusy->organizer; - - $organizer = (string)$organizer; - - // Validating if the organizer matches the owner of the inbox. - $owner = $outbox->getOwner(); - - $caldavNS = '{' . Plugin::NS_CALDAV . '}'; - - $uas = $caldavNS . 'calendar-user-address-set'; - $props = $this->server->getProperties($owner,array($uas)); - - if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) { - throw new DAV\Exception\Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox'); - } - - if (!isset($vFreeBusy->ATTENDEE)) { - throw new DAV\Exception\BadRequest('You must at least specify 1 attendee'); - } - - $attendees = array(); - foreach($vFreeBusy->ATTENDEE as $attendee) { - $attendees[]= (string)$attendee; - } - - - if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) { - throw new DAV\Exception\BadRequest('DTSTART and DTEND must both be specified'); - } - - $startRange = $vFreeBusy->DTSTART->getDateTime(); - $endRange = $vFreeBusy->DTEND->getDateTime(); - - $results = array(); - foreach($attendees as $attendee) { - $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject); - } - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $scheduleResponse = $dom->createElement('cal:schedule-response'); - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $scheduleResponse->setAttribute('xmlns:' . $prefix,$namespace); - - } - $dom->appendChild($scheduleResponse); - - foreach($results as $result) { - $response = $dom->createElement('cal:response'); - - $recipient = $dom->createElement('cal:recipient'); - $recipientHref = $dom->createElement('d:href'); - - $recipientHref->appendChild($dom->createTextNode($result['href'])); - $recipient->appendChild($recipientHref); - $response->appendChild($recipient); - - $reqStatus = $dom->createElement('cal:request-status'); - $reqStatus->appendChild($dom->createTextNode($result['request-status'])); - $response->appendChild($reqStatus); - - if (isset($result['calendar-data'])) { - - $calendardata = $dom->createElement('cal:calendar-data'); - $calendardata->appendChild($dom->createTextNode(str_replace("\r\n","\n",$result['calendar-data']->serialize()))); - $response->appendChild($calendardata); - - } - $scheduleResponse->appendChild($response); - } - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($dom->saveXML()); - - } - - /** - * Returns free-busy information for a specific address. The returned - * data is an array containing the following properties: - * - * calendar-data : A VFREEBUSY VObject - * request-status : an iTip status code. - * href: The principal's email address, as requested - * - * The following request status codes may be returned: - * * 2.0;description - * * 3.7;description - * - * @param string $email address - * @param \DateTime $start - * @param \DateTime $end - * @param VObject\Component $request - * @return array - */ - protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) { - - $caldavNS = '{' . Plugin::NS_CALDAV . '}'; - - $aclPlugin = $this->server->getPlugin('acl'); - if (substr($email,0,7)==='mailto:') $email = substr($email,7); - - $result = $aclPlugin->principalSearch( - array('{http://sabredav.org/ns}email-address' => $email), - array( - '{DAV:}principal-URL', $caldavNS . 'calendar-home-set', - '{http://sabredav.org/ns}email-address', - ) - ); - - if (!count($result)) { - return array( - 'request-status' => '3.7;Could not find principal', - 'href' => 'mailto:' . $email, - ); - } - - if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { - return array( - 'request-status' => '3.7;No calendar-home-set property found', - 'href' => 'mailto:' . $email, - ); - } - $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); - - // Grabbing the calendar list - $objects = array(); - foreach($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { - if (!$node instanceof ICalendar) { - continue; - } - $aclPlugin->checkPrivileges($homeSet . $node->getName() ,$caldavNS . 'read-free-busy'); - - // Getting the list of object uris within the time-range - $urls = $node->calendarQuery(array( - 'name' => 'VCALENDAR', - 'comp-filters' => array( - array( - 'name' => 'VEVENT', - 'comp-filters' => array(), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => array( - 'start' => $start, - 'end' => $end, - ), - ), - ), - 'prop-filters' => array(), - 'is-not-defined' => false, - 'time-range' => null, - )); - - $calObjects = array_map(function($url) use ($node) { - $obj = $node->getChild($url)->get(); - return $obj; - }, $urls); - - $objects = array_merge($objects,$calObjects); - - } - - $vcalendar = new VObject\Component\VCalendar(); - $vcalendar->VERSION = '2.0'; - $vcalendar->METHOD = 'REPLY'; - $vcalendar->CALSCALE = 'GREGORIAN'; - $vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; - - $generator = new VObject\FreeBusyGenerator(); - $generator->setObjects($objects); - $generator->setTimeRange($start, $end); - $generator->setBaseObject($vcalendar); - - $result = $generator->getResult(); - - $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; - $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID; - $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; - - return array( - 'calendar-data' => $result, - 'request-status' => '2.0;Success', - 'href' => 'mailto:' . $email, - ); - } - - /** - * This method is used to generate HTML output for the - * DAV\Browser\Plugin. This allows us to generate an interface users - * can use to create new calendars. - * - * @param DAV\INode $node - * @param string $output - * @return bool - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof UserCalendars) - return; - - $output.= ' -

Create new calendar

- -
-
- - - '; - - return false; - - } - - /** - * This method allows us to intercept the 'mkcalendar' sabreAction. This - * action enables the user to create new calendars from the browser plugin. - * - * @param string $uri - * @param string $action - * @param array $postVars - * @return bool - */ - public function browserPostAction($uri, $action, array $postVars) { - - if ($action!=='mkcalendar') - return; - - $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); - $properties = array(); - if (isset($postVars['{DAV:}displayname'])) { - $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; - } - $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); - return false; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php deleted file mode 100644 index 8de530bcb..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/Collection.php +++ /dev/null @@ -1,32 +0,0 @@ -principalBackend, $principalInfo); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/IProxyRead.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/IProxyRead.php deleted file mode 100644 index 7fabc00ab..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/IProxyRead.php +++ /dev/null @@ -1,19 +0,0 @@ -principalInfo = $principalInfo; - $this->principalBackend = $principalBackend; - - } - - /** - * Returns this principals name. - * - * @return string - */ - public function getName() { - - return 'calendar-proxy-read'; - - } - - /** - * Returns the last modification time - * - * @return null - */ - public function getLastModified() { - - return null; - - } - - /** - * Deletes the current node - * - * @throws DAV\Exception\Forbidden - * @return void - */ - public function delete() { - - throw new DAV\Exception\Forbidden('Permission denied to delete node'); - - } - - /** - * Renames the node - * - * @throws DAV\Exception\Forbidden - * @param string $name The new name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\Forbidden('Permission denied to rename file'); - - } - - - /** - * Returns a list of alternative urls for a principal - * - * This can for example be an email address, or ldap url. - * - * @return array - */ - public function getAlternateUriSet() { - - return array(); - - } - - /** - * Returns the full principal url - * - * @return string - */ - public function getPrincipalUrl() { - - return $this->principalInfo['uri'] . '/' . $this->getName(); - - } - - /** - * Returns the list of group members - * - * If this principal is a group, this function should return - * all member principal uri's for the group. - * - * @return array - */ - public function getGroupMemberSet() { - - return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); - - } - - /** - * Returns the list of groups this principal is member of - * - * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array - */ - public function getGroupMembership() { - - return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); - - } - - /** - * Sets a list of group members - * - * If this principal is a group, this method sets all the group members. - * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void - */ - public function setGroupMemberSet(array $principals) { - - $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); - - } - - /** - * Returns the displayname - * - * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string - */ - public function getDisplayName() { - - return $this->getName(); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/ProxyWrite.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/ProxyWrite.php deleted file mode 100644 index 2a9c6630e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/ProxyWrite.php +++ /dev/null @@ -1,180 +0,0 @@ -principalInfo = $principalInfo; - $this->principalBackend = $principalBackend; - - } - - /** - * Returns this principals name. - * - * @return string - */ - public function getName() { - - return 'calendar-proxy-write'; - - } - - /** - * Returns the last modification time - * - * @return null - */ - public function getLastModified() { - - return null; - - } - - /** - * Deletes the current node - * - * @throws DAV\Exception\Forbidden - * @return void - */ - public function delete() { - - throw new DAV\Exception\Forbidden('Permission denied to delete node'); - - } - - /** - * Renames the node - * - * @throws DAV\Exception\Forbidden - * @param string $name The new name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\Forbidden('Permission denied to rename file'); - - } - - - /** - * Returns a list of alternative urls for a principal - * - * This can for example be an email address, or ldap url. - * - * @return array - */ - public function getAlternateUriSet() { - - return array(); - - } - - /** - * Returns the full principal url - * - * @return string - */ - public function getPrincipalUrl() { - - return $this->principalInfo['uri'] . '/' . $this->getName(); - - } - - /** - * Returns the list of group members - * - * If this principal is a group, this function should return - * all member principal uri's for the group. - * - * @return array - */ - public function getGroupMemberSet() { - - return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); - - } - - /** - * Returns the list of groups this principal is member of - * - * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array - */ - public function getGroupMembership() { - - return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); - - } - - /** - * Sets a list of group members - * - * If this principal is a group, this method sets all the group members. - * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void - */ - public function setGroupMemberSet(array $principals) { - - $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); - - } - - /** - * Returns the displayname - * - * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string - */ - public function getDisplayName() { - - return $this->getName(); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/User.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/User.php deleted file mode 100644 index cdb04dd2c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Principal/User.php +++ /dev/null @@ -1,134 +0,0 @@ -principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); - if (!$principal) { - throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); - } - if ($name === 'calendar-proxy-read') - return new ProxyRead($this->principalBackend, $this->principalProperties); - - if ($name === 'calendar-proxy-write') - return new ProxyWrite($this->principalBackend, $this->principalProperties); - - throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); - - } - - /** - * Returns an array with all the child nodes - * - * @return DAV\INode[] - */ - public function getChildren() { - - $r = array(); - if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { - $r[] = new ProxyRead($this->principalBackend, $this->principalProperties); - } - if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { - $r[] = new ProxyWrite($this->principalBackend, $this->principalProperties); - } - - return $r; - - } - - /** - * Returns whether or not the child node exists - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - try { - $this->getChild($name); - return true; - } catch (DAV\Exception\NotFound $e) { - return false; - } - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - $acl = parent::getACL(); - $acl[] = array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', - 'protected' => true, - ); - $acl[] = array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', - 'protected' => true, - ); - return $acl; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/AllowedSharingModes.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/AllowedSharingModes.php deleted file mode 100644 index 08bf24482..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/AllowedSharingModes.php +++ /dev/null @@ -1,74 +0,0 @@ -canBeShared = $canBeShared; - $this->canBePublished = $canBePublished; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $doc = $node->ownerDocument; - if ($this->canBeShared) { - $xcomp = $doc->createElement('cs:can-be-shared'); - $node->appendChild($xcomp); - } - if ($this->canBePublished) { - $xcomp = $doc->createElement('cs:can-be-published'); - $node->appendChild($xcomp); - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/Invite.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/Invite.php deleted file mode 100644 index 7b91c3352..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/Invite.php +++ /dev/null @@ -1,227 +0,0 @@ -users = $users; - $this->organizer = $organizer; - - } - - /** - * Returns the list of users, as it was passed to the constructor. - * - * @return array - */ - public function getValue() { - - return $this->users; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - - if (!is_null($this->organizer)) { - - $xorganizer = $doc->createElement('cs:organizer'); - - $href = $doc->createElement('d:href'); - $href->appendChild($doc->createTextNode($this->organizer['href'])); - $xorganizer->appendChild($href); - - if (isset($this->organizer['commonName']) && $this->organizer['commonName']) { - $commonName = $doc->createElement('cs:common-name'); - $commonName->appendChild($doc->createTextNode($this->organizer['commonName'])); - $xorganizer->appendChild($commonName); - } - if (isset($this->organizer['firstName']) && $this->organizer['firstName']) { - $firstName = $doc->createElement('cs:first-name'); - $firstName->appendChild($doc->createTextNode($this->organizer['firstName'])); - $xorganizer->appendChild($firstName); - } - if (isset($this->organizer['lastName']) && $this->organizer['lastName']) { - $lastName = $doc->createElement('cs:last-name'); - $lastName->appendChild($doc->createTextNode($this->organizer['lastName'])); - $xorganizer->appendChild($lastName); - } - - $node->appendChild($xorganizer); - - - } - - foreach($this->users as $user) { - - $xuser = $doc->createElement('cs:user'); - - $href = $doc->createElement('d:href'); - $href->appendChild($doc->createTextNode($user['href'])); - $xuser->appendChild($href); - - if (isset($user['commonName']) && $user['commonName']) { - $commonName = $doc->createElement('cs:common-name'); - $commonName->appendChild($doc->createTextNode($user['commonName'])); - $xuser->appendChild($commonName); - } - - switch($user['status']) { - - case SharingPlugin::STATUS_ACCEPTED : - $status = $doc->createElement('cs:invite-accepted'); - $xuser->appendChild($status); - break; - case SharingPlugin::STATUS_DECLINED : - $status = $doc->createElement('cs:invite-declined'); - $xuser->appendChild($status); - break; - case SharingPlugin::STATUS_NORESPONSE : - $status = $doc->createElement('cs:invite-noresponse'); - $xuser->appendChild($status); - break; - case SharingPlugin::STATUS_INVALID : - $status = $doc->createElement('cs:invite-invalid'); - $xuser->appendChild($status); - break; - - } - - $xaccess = $doc->createElement('cs:access'); - - if ($user['readOnly']) { - $xaccess->appendChild( - $doc->createElement('cs:read') - ); - } else { - $xaccess->appendChild( - $doc->createElement('cs:read-write') - ); - } - $xuser->appendChild($xaccess); - - if (isset($user['summary']) && $user['summary']) { - $summary = $doc->createElement('cs:summary'); - $summary->appendChild($doc->createTextNode($user['summary'])); - $xuser->appendChild($summary); - } - - $node->appendChild($xuser); - - } - - - } - - /** - * Unserializes the property. - * - * This static method should return a an instance of this object. - * - * @param \DOMElement $prop - * @return DAV\IProperty - */ - static function unserialize(\DOMElement $prop) { - - $xpath = new \DOMXPath($prop->ownerDocument); - $xpath->registerNamespace('cs', CalDAV\Plugin::NS_CALENDARSERVER); - $xpath->registerNamespace('d', 'urn:DAV'); - - $users = array(); - - foreach($xpath->query('cs:user', $prop) as $user) { - - $status = null; - if ($xpath->evaluate('boolean(cs:invite-accepted)', $user)) { - $status = SharingPlugin::STATUS_ACCEPTED; - } elseif ($xpath->evaluate('boolean(cs:invite-declined)', $user)) { - $status = SharingPlugin::STATUS_DECLINED; - } elseif ($xpath->evaluate('boolean(cs:invite-noresponse)', $user)) { - $status = SharingPlugin::STATUS_NORESPONSE; - } elseif ($xpath->evaluate('boolean(cs:invite-invalid)', $user)) { - $status = SharingPlugin::STATUS_INVALID; - } else { - throw new DAV\Exception('Every cs:user property must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid'); - } - $users[] = array( - 'href' => $xpath->evaluate('string(d:href)', $user), - 'commonName' => $xpath->evaluate('string(cs:common-name)', $user), - 'readOnly' => $xpath->evaluate('boolean(cs:access/cs:read)', $user), - 'summary' => $xpath->evaluate('string(cs:summary)', $user), - 'status' => $status, - ); - - } - - return new self($users); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/ScheduleCalendarTransp.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/ScheduleCalendarTransp.php deleted file mode 100644 index 00f541746..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/ScheduleCalendarTransp.php +++ /dev/null @@ -1,102 +0,0 @@ -value = $value; - - } - - /** - * Returns the current value - * - * @return string - */ - public function getValue() { - - return $this->value; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - switch($this->value) { - case self::TRANSPARENT : - $xval = $doc->createElement('cal:transparent'); - break; - case self::OPAQUE : - $xval = $doc->createElement('cal:opaque'); - break; - } - - $node->appendChild($xval); - - } - - /** - * Unserializes the DOMElement back into a Property class. - * - * @param \DOMElement $node - * @return ScheduleCalendarTransp - */ - static function unserialize(\DOMElement $node) { - - $value = null; - foreach($node->childNodes as $childNode) { - switch(DAV\XMLUtil::toClarkNotation($childNode)) { - case '{' . CalDAV\Plugin::NS_CALDAV . '}opaque' : - $value = self::OPAQUE; - break; - case '{' . CalDAV\Plugin::NS_CALDAV . '}transparent' : - $value = self::TRANSPARENT; - break; - } - } - if (is_null($value)) - return null; - - return new self($value); - - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php deleted file mode 100644 index d526bab1c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php +++ /dev/null @@ -1,88 +0,0 @@ -components = $components; - - } - - /** - * Returns the list of supported components - * - * @return array - */ - public function getValue() { - - return $this->components; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - foreach($this->components as $component) { - - $xcomp = $doc->createElement('cal:comp'); - $xcomp->setAttribute('name',$component); - $node->appendChild($xcomp); - - } - - } - - /** - * Unserializes the DOMElement back into a Property class. - * - * @param \DOMElement $node - * @return Property_SupportedCalendarComponentSet - */ - static function unserialize(\DOMElement $node) { - - $components = array(); - foreach($node->childNodes as $childNode) { - if (DAV\XMLUtil::toClarkNotation($childNode)==='{' . CalDAV\Plugin::NS_CALDAV . '}comp') { - $components[] = $childNode->getAttribute('name'); - } - } - return new self($components); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php deleted file mode 100644 index 5c105f888..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php +++ /dev/null @@ -1,40 +0,0 @@ -ownerDocument; - - $prefix = isset($server->xmlNamespaces[Plugin::NS_CALDAV])?$server->xmlNamespaces[Plugin::NS_CALDAV]:'cal'; - - $caldata = $doc->createElement($prefix . ':calendar-data'); - $caldata->setAttribute('content-type','text/calendar'); - $caldata->setAttribute('version','2.0'); - - $node->appendChild($caldata); - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php deleted file mode 100644 index b05e376c1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php +++ /dev/null @@ -1,45 +0,0 @@ -ownerDocument; - - $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); - if (!$prefix) $prefix = 'cal'; - - $node->appendChild( - $doc->createElement($prefix . ':supported-collation','i;ascii-casemap') - ); - $node->appendChild( - $doc->createElement($prefix . ':supported-collation','i;octet') - ); - $node->appendChild( - $doc->createElement($prefix . ':supported-collation','i;unicode-casemap') - ); - - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IMip.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IMip.php deleted file mode 100644 index 8be0f5c84..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IMip.php +++ /dev/null @@ -1,111 +0,0 @@ -senderEmail = $senderEmail; - - } - - /** - * Sends one or more iTip messages through email. - * - * @param string $originator Originator Email - * @param array $recipients Array of email addresses - * @param VObject\Component $vObject - * @param string $principal Principal Url of the originator - * @return void - */ - public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) { - - foreach($recipients as $recipient) { - - $to = $recipient; - $replyTo = $originator; - $subject = 'SabreDAV iTIP message'; - - switch(strtoupper($vObject->METHOD)) { - case 'REPLY' : - $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY; - break; - case 'REQUEST' : - $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY; - break; - case 'CANCEL' : - $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY; - break; - } - - $headers = array(); - $headers[] = 'Reply-To: ' . $replyTo; - $headers[] = 'From: ' . $this->senderEmail; - $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8'; - if (DAV\Server::$exposeVersion) { - $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY; - } - - $vcalBody = $vObject->serialize(); - - $this->mail($to, $subject, $vcalBody, $headers); - - } - - } - - // @codeCoverageIgnoreStart - // This is deemed untestable in a reasonable manner - - /** - * This function is reponsible for sending the actual email. - * - * @param string $to Recipient email address - * @param string $subject Subject of the email - * @param string $body iCalendar body - * @param array $headers List of headers - * @return void - */ - protected function mail($to, $subject, $body, array $headers) { - - - mail($to, $subject, $body, implode("\r\n", $headers)); - - } - - // @codeCoverageIgnoreEnd - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IOutbox.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IOutbox.php deleted file mode 100644 index d8ce1729d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Schedule/IOutbox.php +++ /dev/null @@ -1,16 +0,0 @@ -principalUri = $principalUri; - - } - - /** - * Returns the name of the node. - * - * This is used to generate the url. - * - * @return string - */ - public function getName() { - - return 'outbox'; - - } - - /** - * Returns an array with all the child nodes - * - * @return \Sabre\DAV\INode[] - */ - public function getChildren() { - - return array(); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner(), - 'protected' => true, - ), - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('You\'re not allowed to update the ACL'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet(); - $default['aggregates'][] = array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', - ); - $default['aggregates'][] = array( - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', - ); - - return $default; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ShareableCalendar.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ShareableCalendar.php deleted file mode 100644 index 51ae8082f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/ShareableCalendar.php +++ /dev/null @@ -1,72 +0,0 @@ -caldavBackend->updateShares($this->calendarInfo['id'], $add, $remove); - - } - - /** - * Returns the list of people whom this calendar is shared with. - * - * Every element in this array should have the following properties: - * * href - Often a mailto: address - * * commonName - Optional, for example a first + last name - * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. - * * readOnly - boolean - * * summary - Optional, a description for the share - * - * @return array - */ - public function getShares() { - - return $this->caldavBackend->getShares($this->calendarInfo['id']); - - } - - /** - * Marks this calendar as published. - * - * Publishing a calendar should automatically create a read-only, public, - * subscribable calendar. - * - * @param bool $value - * @return void - */ - public function setPublishStatus($value) { - - $this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharedCalendar.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharedCalendar.php deleted file mode 100644 index 021d7d231..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharedCalendar.php +++ /dev/null @@ -1,116 +0,0 @@ -calendarInfo['{http://calendarserver.org/ns/}shared-url']; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->calendarInfo['{http://sabredav.org/ns}owner-principal']; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - // The top-level ACL only contains access information for the true - // owner of the calendar, so we need to add the information for the - // sharee. - $acl = parent::getACL(); - $acl[] = array( - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ); - if (!$this->calendarInfo['{http://sabredav.org/ns}read-only']) { - $acl[] = array( - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ); - } - return $acl; - - } - - /** - * Returns the list of people whom this calendar is shared with. - * - * Every element in this array should have the following properties: - * * href - Often a mailto: address - * * commonName - Optional, for example a first + last name - * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. - * * readOnly - boolean - * * summary - Optional, a description for the share - * - * @return array - */ - public function getShares() { - - return $this->caldavBackend->getShares($this->calendarInfo['id']); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharingPlugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharingPlugin.php deleted file mode 100644 index fe340b936..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/SharingPlugin.php +++ /dev/null @@ -1,526 +0,0 @@ -server = $server; - $server->resourceTypeMapping['Sabre\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared'; - - array_push( - $this->server->protectedProperties, - '{' . Plugin::NS_CALENDARSERVER . '}invite', - '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', - '{' . Plugin::NS_CALENDARSERVER . '}shared-url' - ); - - $this->server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); - $this->server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); - $this->server->subscribeEvent('updateProperties', array($this, 'updateProperties')); - $this->server->subscribeEvent('unknownMethod', array($this,'unknownMethod')); - - } - - /** - * This event is triggered when properties are requested for a certain - * node. - * - * This allows us to inject any properties early. - * - * @param string $path - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @return void - */ - public function beforeGetProperties($path, DAV\INode $node, &$requestedProperties, &$returnedProperties) { - - if ($node instanceof IShareableCalendar) { - if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}invite'] = - new Property\Invite( - $node->getShares() - ); - - } - - } - - if ($node instanceof ISharedCalendar) { - - if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}shared-url', $requestedProperties))!==false) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}shared-url'] = - new DAV\Property\Href( - $node->getSharedUrl() - ); - - } - // The 'invite' property is slightly different for the 'shared' - // instance of the calendar, as it also contains the owner - // information. - if (($index = array_search('{' . Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) { - - unset($requestedProperties[$index]); - - // Fetching owner information - $props = $this->server->getPropertiesForPath($node->getOwner(), array( - '{http://sabredav.org/ns}email-address', - '{DAV:}displayname', - ), 1); - - $ownerInfo = array( - 'href' => $node->getOwner(), - ); - - if (isset($props[0][200])) { - - // We're mapping the internal webdav properties to the - // elements caldav-sharing expects. - if (isset($props[0][200]['{http://sabredav.org/ns}email-address'])) { - $ownerInfo['href'] = 'mailto:' . $props[0][200]['{http://sabredav.org/ns}email-address']; - } - if (isset($props[0][200]['{DAV:}displayname'])) { - $ownerInfo['commonName'] = $props[0][200]['{DAV:}displayname']; - } - - } - - $returnedProperties[200]['{' . Plugin::NS_CALENDARSERVER . '}invite'] = - new Property\Invite( - $node->getShares(), - $ownerInfo - ); - - } - - - } - - } - - /** - * This method is triggered *after* all properties have been retrieved. - * This allows us to inject the correct resourcetype for calendars that - * have been shared. - * - * @param string $path - * @param array $properties - * @param DAV\INode $node - * @return void - */ - public function afterGetProperties($path, &$properties, DAV\INode $node) { - - if ($node instanceof IShareableCalendar) { - if (isset($properties[200]['{DAV:}resourcetype'])) { - if (count($node->getShares())>0) { - $properties[200]['{DAV:}resourcetype']->add( - '{' . Plugin::NS_CALENDARSERVER . '}shared-owner' - ); - } - } - $propName = '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes'; - if (array_key_exists($propName, $properties[404])) { - unset($properties[404][$propName]); - $properties[200][$propName] = new Property\AllowedSharingModes(true,false); - } - - } - - } - - /** - * This method is trigged when a user attempts to update a node's - * properties. - * - * A previous draft of the sharing spec stated that it was possible to use - * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing - * the calendar. - * - * Even though this is no longer in the current spec, we keep this around - * because OS X 10.7 may still make use of this feature. - * - * @param array $mutations - * @param array $result - * @param DAV\INode $node - * @return void - */ - public function updateProperties(array &$mutations, array &$result, DAV\INode $node) { - - if (!$node instanceof IShareableCalendar) - return; - - if (!isset($mutations['{DAV:}resourcetype'])) { - return; - } - - // Only doing something if shared-owner is indeed not in the list. - if($mutations['{DAV:}resourcetype']->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return; - - $shares = $node->getShares(); - $remove = array(); - foreach($shares as $share) { - $remove[] = $share['href']; - } - $node->updateShares(array(), $remove); - - // We're marking this update as 200 OK - $result[200]['{DAV:}resourcetype'] = null; - - // Removing it from the mutations list - unset($mutations['{DAV:}resourcetype']); - - } - - /** - * This event is triggered when the server didn't know how to handle a - * certain request. - * - * We intercept this to handle POST requests on calendars. - * - * @param string $method - * @param string $uri - * @return null|bool - */ - public function unknownMethod($method, $uri) { - - if ($method!=='POST') { - return; - } - - // Only handling xml - $contentType = $this->server->httpRequest->getHeader('Content-Type'); - if (strpos($contentType,'application/xml')===false && strpos($contentType,'text/xml')===false) - return; - - // Making sure the node exists - try { - $node = $this->server->tree->getNodeForPath($uri); - } catch (DAV\Exception\NotFound $e) { - return; - } - - $requestBody = $this->server->httpRequest->getBody(true); - - // If this request handler could not deal with this POST request, it - // will return 'null' and other plugins get a chance to handle the - // request. - // - // However, we already requested the full body. This is a problem, - // because a body can only be read once. This is why we preemptively - // re-populated the request body with the existing data. - $this->server->httpRequest->setBody($requestBody); - - $dom = DAV\XMLUtil::loadDOMDocument($requestBody); - - $documentType = DAV\XMLUtil::toClarkNotation($dom->firstChild); - - switch($documentType) { - - // Dealing with the 'share' document, which modified invitees on a - // calendar. - case '{' . Plugin::NS_CALENDARSERVER . '}share' : - - // We can only deal with IShareableCalendar objects - if (!$node instanceof IShareableCalendar) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $mutations = $this->parseShareRequest($dom); - - $node->updateShares($mutations[0], $mutations[1]); - - $this->server->httpResponse->sendStatus(200); - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - // Breaking the event chain - return false; - - // The invite-reply document is sent when the user replies to an - // invitation of a calendar share. - case '{'. Plugin::NS_CALENDARSERVER.'}invite-reply' : - - // This only works on the calendar-home-root node. - if (!$node instanceof UserCalendars) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $message = $this->parseInviteReplyRequest($dom); - - $url = $node->shareReply( - $message['href'], - $message['status'], - $message['calendarUri'], - $message['inReplyTo'], - $message['summary'] - ); - - $this->server->httpResponse->sendStatus(200); - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - if ($url) { - $dom = new \DOMDocument('1.0', 'UTF-8'); - $dom->formatOutput = true; - - $root = $dom->createElement('cs:shared-as'); - foreach($this->server->xmlNamespaces as $namespace => $prefix) { - $root->setAttribute('xmlns:' . $prefix, $namespace); - } - - $dom->appendChild($root); - $href = new DAV\Property\Href($url); - - $href->serialize($this->server, $root); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($dom->saveXML()); - - } - - // Breaking the event chain - return false; - - case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' : - - // We can only deal with IShareableCalendar objects - if (!$node instanceof IShareableCalendar) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $node->setPublishStatus(true); - - // iCloud sends back the 202, so we will too. - $this->server->httpResponse->sendStatus(202); - - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - // Breaking the event chain - return false; - - case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' : - - // We can only deal with IShareableCalendar objects - if (!$node instanceof IShareableCalendar) { - return; - } - - // Getting ACL info - $acl = $this->server->getPlugin('acl'); - - // If there's no ACL support, we allow everything - if ($acl) { - $acl->checkPrivileges($uri, '{DAV:}write'); - } - - $node->setPublishStatus(false); - - $this->server->httpResponse->sendStatus(200); - - // Adding this because sending a response body may cause issues, - // and I wanted some type of indicator the response was handled. - $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); - - // Breaking the event chain - return false; - - } - - - - } - - /** - * Parses the 'share' POST request. - * - * This method returns an array, containing two arrays. - * The first array is a list of new sharees. Every element is a struct - * containing a: - * * href element. (usually a mailto: address) - * * commonName element (often a first and lastname, but can also be - * false) - * * readOnly (true or false) - * * summary (A description of the share, can also be false) - * - * The second array is a list of sharees that are to be removed. This is - * just a simple array with 'hrefs'. - * - * @param \DOMDocument $dom - * @return array - */ - protected function parseShareRequest(\DOMDocument $dom) { - - $xpath = new \DOMXPath($dom); - $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER); - $xpath->registerNamespace('d', 'urn:DAV'); - - $set = array(); - $elems = $xpath->query('cs:set'); - - for($i=0; $i < $elems->length; $i++) { - - $xset = $elems->item($i); - $set[] = array( - 'href' => $xpath->evaluate('string(d:href)', $xset), - 'commonName' => $xpath->evaluate('string(cs:common-name)', $xset), - 'summary' => $xpath->evaluate('string(cs:summary)', $xset), - 'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset)!==false - ); - - } - - $remove = array(); - $elems = $xpath->query('cs:remove'); - - for($i=0; $i < $elems->length; $i++) { - - $xremove = $elems->item($i); - $remove[] = $xpath->evaluate('string(d:href)', $xremove); - - } - - return array($set, $remove); - - } - - /** - * Parses the 'invite-reply' POST request. - * - * This method returns an array, containing the following properties: - * * href - The sharee who is replying - * * status - One of the self::STATUS_* constants - * * calendarUri - The url of the shared calendar - * * inReplyTo - The unique id of the share invitation. - * * summary - Optional description of the reply. - * - * @param \DOMDocument $dom - * @return array - */ - protected function parseInviteReplyRequest(\DOMDocument $dom) { - - $xpath = new \DOMXPath($dom); - $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER); - $xpath->registerNamespace('d', 'urn:DAV'); - - $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)'); - if (!$hostHref) { - throw new DAV\Exception\BadRequest('The {' . Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required'); - } - - return array( - 'href' => $xpath->evaluate('string(d:href)'), - 'calendarUri' => $this->server->calculateUri($hostHref), - 'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'), - 'summary' => $xpath->evaluate('string(cs:summary)'), - 'status' => $xpath->evaluate('boolean(cs:invite-accepted)')?self::STATUS_ACCEPTED:self::STATUS_DECLINED - ); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php deleted file mode 100644 index e2bc36f36..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/UserCalendars.php +++ /dev/null @@ -1,342 +0,0 @@ -caldavBackend = $caldavBackend; - $this->principalInfo = $principalInfo; - - } - - /** - * Returns the name of this object - * - * @return string - */ - public function getName() { - - list(,$name) = DAV\URLUtil::splitPath($this->principalInfo['uri']); - return $name; - - } - - /** - * Updates the name of this object - * - * @param string $name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\Forbidden(); - - } - - /** - * Deletes this object - * - * @return void - */ - public function delete() { - - throw new DAV\Exception\Forbidden(); - - } - - /** - * Returns the last modification date - * - * @return int - */ - public function getLastModified() { - - return null; - - } - - /** - * Creates a new file under this object. - * - * This is currently not allowed - * - * @param string $filename - * @param resource $data - * @return void - */ - public function createFile($filename, $data=null) { - - throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); - - } - - /** - * Creates a new directory under this object. - * - * This is currently not allowed. - * - * @param string $filename - * @return void - */ - public function createDirectory($filename) { - - throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); - - } - - /** - * Returns a single calendar, by name - * - * @param string $name - * @todo needs optimizing - * @return Calendar - */ - public function getChild($name) { - - foreach($this->getChildren() as $child) { - if ($name==$child->getName()) - return $child; - - } - throw new DAV\Exception\NotFound('Calendar with name \'' . $name . '\' could not be found'); - - } - - /** - * Checks if a calendar exists. - * - * @param string $name - * @todo needs optimizing - * @return bool - */ - public function childExists($name) { - - foreach($this->getChildren() as $child) { - if ($name==$child->getName()) - return true; - - } - return false; - - } - - /** - * Returns a list of calendars - * - * @return array - */ - public function getChildren() { - - $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); - $objs = array(); - foreach($calendars as $calendar) { - if ($this->caldavBackend instanceof Backend\SharingSupport) { - if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) { - $objs[] = new SharedCalendar($this->caldavBackend, $calendar); - } else { - $objs[] = new ShareableCalendar($this->caldavBackend, $calendar); - } - } else { - $objs[] = new Calendar($this->caldavBackend, $calendar); - } - } - $objs[] = new Schedule\Outbox($this->principalInfo['uri']); - - // We're adding a notifications node, if it's supported by the backend. - if ($this->caldavBackend instanceof Backend\NotificationSupport) { - $objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); - } - return $objs; - - } - - /** - * Creates a new calendar - * - * @param string $name - * @param array $resourceType - * @param array $properties - * @return void - */ - public function createExtendedCollection($name, array $resourceType, array $properties) { - - $isCalendar = false; - foreach($resourceType as $rt) { - switch ($rt) { - case '{DAV:}collection' : - case '{http://calendarserver.org/ns/}shared-owner' : - // ignore - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $isCalendar = true; - break; - default : - throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt); - } - } - if (!$isCalendar) { - throw new DAV\Exception\InvalidResourceType('You can only create calendars in this collection'); - } - $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalInfo['uri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->principalInfo['uri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - - /** - * This method is called when a user replied to a request to share. - * - * This method should return the url of the newly created calendar if the - * share was accepted. - * - * @param string href The sharee who is replying (often a mailto: address) - * @param int status One of the SharingPlugin::STATUS_* constants - * @param string $calendarUri The url to the calendar thats being shared - * @param string $inReplyTo The unique id this message is a response to - * @param string $summary A description of the reply - * @return null|string - */ - public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { - - if (!$this->caldavBackend instanceof Backend\SharingSupport) { - throw new DAV\Exception\NotImplemented('Sharing support is not implemented by this backend.'); - } - - return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Version.php b/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Version.php deleted file mode 100644 index 971326ab6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CalDAV/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -carddavBackend = $carddavBackend; - $this->addressBookInfo = $addressBookInfo; - - } - - /** - * Returns the name of the addressbook - * - * @return string - */ - public function getName() { - - return $this->addressBookInfo['uri']; - - } - - /** - * Returns a card - * - * @param string $name - * @return \ICard - */ - public function getChild($name) { - - $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name); - if (!$obj) throw new DAV\Exception\NotFound('Card not found'); - return new Card($this->carddavBackend,$this->addressBookInfo,$obj); - - } - - /** - * Returns the full list of cards - * - * @return array - */ - public function getChildren() { - - $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']); - $children = array(); - foreach($objs as $obj) { - $children[] = new Card($this->carddavBackend,$this->addressBookInfo,$obj); - } - return $children; - - } - - /** - * Creates a new directory - * - * We actually block this, as subdirectories are not allowed in addressbooks. - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - throw new DAV\Exception\MethodNotAllowed('Creating collections in addressbooks is not allowed'); - - } - - /** - * Creates a new file - * - * The contents of the new file must be a valid VCARD. - * - * This method may return an ETag. - * - * @param string $name - * @param resource $vcardData - * @return string|null - */ - public function createFile($name,$vcardData = null) { - - if (is_resource($vcardData)) { - $vcardData = stream_get_contents($vcardData); - } - // Converting to UTF-8, if needed - $vcardData = DAV\StringUtil::ensureUTF8($vcardData); - - return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData); - - } - - /** - * Deletes the entire addressbook. - * - * @return void - */ - public function delete() { - - $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']); - - } - - /** - * Renames the addressbook - * - * @param string $newName - * @return void - */ - public function setName($newName) { - - throw new DAV\Exception\MethodNotAllowed('Renaming addressbooks is not yet supported'); - - } - - /** - * Returns the last modification date as a unix timestamp. - * - * @return void - */ - public function getLastModified() { - - return null; - - } - - /** - * Updates properties on this node, - * - * The properties array uses the propertyName in clark-notation as key, - * and the array value for the property value. In the case a property - * should be deleted, the property value will be null. - * - * This method must be atomic. If one property cannot be changed, the - * entire operation must fail. - * - * If the operation was successful, true can be returned. - * If the operation failed, false can be returned. - * - * Deletion of a non-existent property is always successful. - * - * Lastly, it is optional to return detailed information about any - * failures. In this case an array should be returned with the following - * structure: - * - * array( - * 403 => array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param array $mutations - * @return bool|array - */ - public function updateProperties($mutations) { - - return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); - - } - - /** - * Returns a list of properties for this nodes. - * - * The properties list is a list of propertynames the client requested, - * encoded in clark-notation {xmlnamespace}tagname - * - * If the array is empty, it means 'all properties' were requested. - * - * @param array $properties - * @return array - */ - public function getProperties($properties) { - - $response = array(); - foreach($properties as $propertyName) { - - if (isset($this->addressBookInfo[$propertyName])) { - - $response[$propertyName] = $this->addressBookInfo[$propertyName]; - - } - - } - - return $response; - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->addressBookInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookQueryParser.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookQueryParser.php deleted file mode 100644 index 0fefba621..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookQueryParser.php +++ /dev/null @@ -1,221 +0,0 @@ -dom = $dom; - - $this->xpath = new \DOMXPath($dom); - $this->xpath->registerNameSpace('card',Plugin::NS_CARDDAV); - - } - - /** - * Parses the request. - * - * @return void - */ - public function parse() { - - $filterNode = null; - - $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)'); - if (is_nan($limit)) $limit = null; - - $filter = $this->xpath->query('/card:addressbook-query/card:filter'); - - // According to the CardDAV spec there needs to be exactly 1 filter - // element. However, KDE 4.8.2 contains a bug that will encode 0 filter - // elements, so this is a workaround for that. - // - // See: https://bugs.kde.org/show_bug.cgi?id=300047 - if ($filter->length === 0) { - $test = null; - $filter = null; - } elseif ($filter->length === 1) { - $filter = $filter->item(0); - $test = $this->xpath->evaluate('string(@test)', $filter); - } else { - throw new DAV\Exception\BadRequest('Only one filter element is allowed'); - } - - if (!$test) $test = self::TEST_ANYOF; - if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) { - throw new DAV\Exception\BadRequest('The test attribute must either hold "anyof" or "allof"'); - } - - $propFilters = array(); - - $propFilterNodes = $this->xpath->query('card:prop-filter', $filter); - for($ii=0; $ii < $propFilterNodes->length; $ii++) { - - $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii)); - - - } - - $this->filters = $propFilters; - $this->limit = $limit; - $this->requestedProperties = array_keys(DAV\XMLUtil::parseProperties($this->dom->firstChild)); - $this->test = $test; - - } - - /** - * Parses the prop-filter xml element - * - * @param \DOMElement $propFilterNode - * @return array - */ - protected function parsePropFilterNode(\DOMElement $propFilterNode) { - - $propFilter = array(); - $propFilter['name'] = $propFilterNode->getAttribute('name'); - $propFilter['test'] = $propFilterNode->getAttribute('test'); - if (!$propFilter['test']) $propFilter['test'] = 'anyof'; - - $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0; - - $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode); - - $propFilter['param-filters'] = array(); - - - for($ii=0;$ii<$paramFilterNodes->length;$ii++) { - - $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii)); - - } - $propFilter['text-matches'] = array(); - $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode); - - for($ii=0;$ii<$textMatchNodes->length;$ii++) { - - $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii)); - - } - - return $propFilter; - - } - - /** - * Parses the param-filter element - * - * @param \DOMElement $paramFilterNode - * @return array - */ - public function parseParamFilterNode(\DOMElement $paramFilterNode) { - - $paramFilter = array(); - $paramFilter['name'] = $paramFilterNode->getAttribute('name'); - $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0; - $paramFilter['text-match'] = null; - - $textMatch = $this->xpath->query('card:text-match', $paramFilterNode); - if ($textMatch->length>0) { - $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0)); - } - - return $paramFilter; - - } - - /** - * Text match - * - * @param \DOMElement $textMatchNode - * @return array - */ - public function parseTextMatchNode(\DOMElement $textMatchNode) { - - $matchType = $textMatchNode->getAttribute('match-type'); - if (!$matchType) $matchType = 'contains'; - - if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) { - throw new DAV\Exception\BadRequest('Unknown match-type: ' . $matchType); - } - - $negateCondition = $textMatchNode->getAttribute('negate-condition'); - $negateCondition = $negateCondition==='yes'; - $collation = $textMatchNode->getAttribute('collation'); - if (!$collation) $collation = 'i;unicode-casemap'; - - return array( - 'negate-condition' => $negateCondition, - 'collation' => $collation, - 'match-type' => $matchType, - 'value' => $textMatchNode->nodeValue - ); - - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookRoot.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookRoot.php deleted file mode 100644 index b58648b89..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/AddressBookRoot.php +++ /dev/null @@ -1,80 +0,0 @@ -carddavBackend = $carddavBackend; - parent::__construct($principalBackend, $principalPrefix); - - } - - /** - * Returns the name of the node - * - * @return string - */ - public function getName() { - - return Plugin::ADDRESSBOOK_ROOT; - - } - - /** - * This method returns a node for a principal. - * - * The passed array contains principal information, and is guaranteed to - * at least contain a uri item. Other properties may or may not be - * supplied by the authentication backend. - * - * @param array $principal - * @return \Sabre\DAV\INode - */ - public function getChildForPrincipal(array $principal) { - - return new UserAddressBooks($this->carddavBackend, $principal['uri']); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Backend/AbstractBackend.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Backend/AbstractBackend.php deleted file mode 100644 index f8addd0d4..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Backend/AbstractBackend.php +++ /dev/null @@ -1,18 +0,0 @@ -pdo = $pdo; - $this->addressBooksTableName = $addressBooksTableName; - $this->cardsTableName = $cardsTableName; - - } - - /** - * Returns the list of addressbooks for a specific user. - * - * @param string $principalUri - * @return array - */ - public function getAddressBooksForUser($principalUri) { - - $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM '.$this->addressBooksTableName.' WHERE principaluri = ?'); - $stmt->execute(array($principalUri)); - - $addressBooks = array(); - - foreach($stmt->fetchAll() as $row) { - - $addressBooks[] = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - 'principaluri' => $row['principaluri'], - '{DAV:}displayname' => $row['displayname'], - '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], - '{http://calendarserver.org/ns/}getctag' => $row['ctag'], - '{' . CardDAV\Plugin::NS_CARDDAV . '}supported-address-data' => - new CardDAV\Property\SupportedAddressData(), - ); - - } - - return $addressBooks; - - } - - - /** - * Updates an addressbook's properties - * - * See Sabre\DAV\IProperties for a description of the mutations array, as - * well as the return value. - * - * @param mixed $addressBookId - * @param array $mutations - * @see Sabre\DAV\IProperties::updateProperties - * @return bool|array - */ - public function updateAddressBook($addressBookId, array $mutations) { - - $updates = array(); - - foreach($mutations as $property=>$newValue) { - - switch($property) { - case '{DAV:}displayname' : - $updates['displayname'] = $newValue; - break; - case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : - $updates['description'] = $newValue; - break; - default : - // If any unsupported values were being updated, we must - // let the entire request fail. - return false; - } - - } - - // No values are being updated? - if (!$updates) { - return false; - } - - $query = 'UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 '; - foreach($updates as $key=>$value) { - $query.=', `' . $key . '` = :' . $key . ' '; - } - $query.=' WHERE id = :addressbookid'; - - $stmt = $this->pdo->prepare($query); - $updates['addressbookid'] = $addressBookId; - - $stmt->execute($updates); - - return true; - - } - - /** - * Creates a new address book - * - * @param string $principalUri - * @param string $url Just the 'basename' of the url. - * @param array $properties - * @return void - */ - public function createAddressBook($principalUri, $url, array $properties) { - - $values = array( - 'displayname' => null, - 'description' => null, - 'principaluri' => $principalUri, - 'uri' => $url, - ); - - foreach($properties as $property=>$newValue) { - - switch($property) { - case '{DAV:}displayname' : - $values['displayname'] = $newValue; - break; - case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : - $values['description'] = $newValue; - break; - default : - throw new DAV\Exception\BadRequest('Unknown property: ' . $property); - } - - } - - $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)'; - $stmt = $this->pdo->prepare($query); - $stmt->execute($values); - - } - - /** - * Deletes an entire addressbook and all its contents - * - * @param int $addressBookId - * @return void - */ - public function deleteAddressBook($addressBookId) { - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); - $stmt->execute(array($addressBookId)); - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); - $stmt->execute(array($addressBookId)); - - } - - /** - * Returns all cards for a specific addressbook id. - * - * This method should return the following properties for each card: - * * carddata - raw vcard data - * * uri - Some unique url - * * lastmodified - A unix timestamp - * - * It's recommended to also return the following properties: - * * etag - A unique etag. This must change every time the card changes. - * * size - The size of the card in bytes. - * - * If these last two properties are provided, less time will be spent - * calculating them. If they are specified, you can also ommit carddata. - * This may speed up certain requests, especially with large cards. - * - * @param mixed $addressbookId - * @return array - */ - public function getCards($addressbookId) { - - $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); - $stmt->execute(array($addressbookId)); - - return $stmt->fetchAll(\PDO::FETCH_ASSOC); - - - } - - /** - * Returns a specfic card. - * - * The same set of properties must be returned as with getCards. The only - * exception is that 'carddata' is absolutely required. - * - * @param mixed $addressBookId - * @param string $cardUri - * @return array - */ - public function getCard($addressBookId, $cardUri) { - - $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1'); - $stmt->execute(array($addressBookId, $cardUri)); - - $result = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - return (count($result)>0?$result[0]:false); - - } - - /** - * Creates a new card. - * - * The addressbook id will be passed as the first argument. This is the - * same id as it is returned from the getAddressbooksForUser method. - * - * The cardUri is a base uri, and doesn't include the full path. The - * cardData argument is the vcard body, and is passed as a string. - * - * It is possible to return an ETag from this method. This ETag is for the - * newly created resource, and must be enclosed with double quotes (that - * is, the string itself must contain the double quotes). - * - * You should only return the ETag if you store the carddata as-is. If a - * subsequent GET request on the same card does not have the same body, - * byte-by-byte and you did return an ETag here, clients tend to get - * confused. - * - * If you don't return an ETag, you can just return null. - * - * @param mixed $addressBookId - * @param string $cardUri - * @param string $cardData - * @return string|null - */ - public function createCard($addressBookId, $cardUri, $cardData) { - - $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)'); - - $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId)); - - $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); - $stmt2->execute(array($addressBookId)); - - return '"' . md5($cardData) . '"'; - - } - - /** - * Updates a card. - * - * The addressbook id will be passed as the first argument. This is the - * same id as it is returned from the getAddressbooksForUser method. - * - * The cardUri is a base uri, and doesn't include the full path. The - * cardData argument is the vcard body, and is passed as a string. - * - * It is possible to return an ETag from this method. This ETag should - * match that of the updated resource, and must be enclosed with double - * quotes (that is: the string itself must contain the actual quotes). - * - * You should only return the ETag if you store the carddata as-is. If a - * subsequent GET request on the same card does not have the same body, - * byte-by-byte and you did return an ETag here, clients tend to get - * confused. - * - * If you don't return an ETag, you can just return null. - * - * @param mixed $addressBookId - * @param string $cardUri - * @param string $cardData - * @return string|null - */ - public function updateCard($addressBookId, $cardUri, $cardData) { - - $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); - $stmt->execute(array($cardData, time(), $cardUri, $addressBookId)); - - $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); - $stmt2->execute(array($addressBookId)); - - return '"' . md5($cardData) . '"'; - - } - - /** - * Deletes a card - * - * @param mixed $addressBookId - * @param string $cardUri - * @return bool - */ - public function deleteCard($addressBookId, $cardUri) { - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); - $stmt->execute(array($addressBookId, $cardUri)); - - $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); - $stmt2->execute(array($addressBookId)); - - return $stmt->rowCount()===1; - - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Card.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Card.php deleted file mode 100644 index 16834ea38..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Card.php +++ /dev/null @@ -1,260 +0,0 @@ -carddavBackend = $carddavBackend; - $this->addressBookInfo = $addressBookInfo; - $this->cardData = $cardData; - - } - - /** - * Returns the uri for this object - * - * @return string - */ - public function getName() { - - return $this->cardData['uri']; - - } - - /** - * Returns the VCard-formatted object - * - * @return string - */ - public function get() { - - // Pre-populating 'carddata' is optional. If we don't yet have it - // already, we fetch it from the backend. - if (!isset($this->cardData['carddata'])) { - $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']); - } - return $this->cardData['carddata']; - - } - - /** - * Updates the VCard-formatted object - * - * @param string $cardData - * @return string|null - */ - public function put($cardData) { - - if (is_resource($cardData)) - $cardData = stream_get_contents($cardData); - - // Converting to UTF-8, if needed - $cardData = DAV\StringUtil::ensureUTF8($cardData); - - $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData); - $this->cardData['carddata'] = $cardData; - $this->cardData['etag'] = $etag; - - return $etag; - - } - - /** - * Deletes the card - * - * @return void - */ - public function delete() { - - $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']); - - } - - /** - * Returns the mime content-type - * - * @return string - */ - public function getContentType() { - - return 'text/x-vcard; charset=utf-8'; - - } - - /** - * Returns an ETag for this object - * - * @return string - */ - public function getETag() { - - if (isset($this->cardData['etag'])) { - return $this->cardData['etag']; - } else { - $data = $this->get(); - if (is_string($data)) { - return '"' . md5($data) . '"'; - } else { - // We refuse to calculate the md5 if it's a stream. - return null; - } - } - - } - - /** - * Returns the last modification date as a unix timestamp - * - * @return int - */ - public function getLastModified() { - - return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null; - - } - - /** - * Returns the size of this object in bytes - * - * @return int - */ - public function getSize() { - - if (array_key_exists('size', $this->cardData)) { - return $this->cardData['size']; - } else { - return strlen($this->get()); - } - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->addressBookInfo['principaluri']; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->addressBookInfo['principaluri'], - 'protected' => true, - ), - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/IAddressBook.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/IAddressBook.php deleted file mode 100644 index af9e88d84..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/IAddressBook.php +++ /dev/null @@ -1,20 +0,0 @@ -subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); - $server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); - $server->subscribeEvent('updateProperties', array($this, 'updateProperties')); - $server->subscribeEvent('report', array($this,'report')); - $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); - $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); - $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); - $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); - - /* Namespaces */ - $server->xmlNamespaces[self::NS_CARDDAV] = 'card'; - - /* Mapping Interfaces to {DAV:}resourcetype values */ - $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; - $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; - - /* Adding properties that may never be changed */ - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size'; - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; - $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; - - $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Property\\Href'; - - $this->server = $server; - - } - - /** - * Returns a list of supported features. - * - * This is used in the DAV: header in the OPTIONS and PROPFIND requests. - * - * @return array - */ - public function getFeatures() { - - return array('addressbook'); - - } - - /** - * Returns a list of reports this plugin supports. - * - * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * - * @param string $uri - * @return array - */ - public function getSupportedReportSet($uri) { - - $node = $this->server->tree->getNodeForPath($uri); - if ($node instanceof IAddressBook || $node instanceof ICard) { - return array( - '{' . self::NS_CARDDAV . '}addressbook-multiget', - '{' . self::NS_CARDDAV . '}addressbook-query', - ); - } - return array(); - - } - - - /** - * Adds all CardDAV-specific properties - * - * @param string $path - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @return void - */ - public function beforeGetProperties($path, DAV\INode $node, array &$requestedProperties, array &$returnedProperties) { - - if ($node instanceof DAVACL\IPrincipal) { - - // calendar-home-set property - $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set'; - if (in_array($addHome,$requestedProperties)) { - $principalId = $node->getName(); - $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/'; - unset($requestedProperties[array_search($addHome, $requestedProperties)]); - $returnedProperties[200][$addHome] = new DAV\Property\Href($addressbookHomePath); - } - - $directories = '{' . self::NS_CARDDAV . '}directory-gateway'; - if ($this->directories && in_array($directories, $requestedProperties)) { - unset($requestedProperties[array_search($directories, $requestedProperties)]); - $returnedProperties[200][$directories] = new DAV\Property\HrefList($this->directories); - } - - } - - if ($node instanceof ICard) { - - // The address-data property is not supposed to be a 'real' - // property, but in large chunks of the spec it does act as such. - // Therefore we simply expose it as a property. - $addressDataProp = '{' . self::NS_CARDDAV . '}address-data'; - if (in_array($addressDataProp, $requestedProperties)) { - unset($requestedProperties[$addressDataProp]); - $val = $node->get(); - if (is_resource($val)) - $val = stream_get_contents($val); - - $returnedProperties[200][$addressDataProp] = $val; - - } - } - - if ($node instanceof UserAddressBooks) { - - $meCardProp = '{http://calendarserver.org/ns/}me-card'; - if (in_array($meCardProp, $requestedProperties)) { - - $props = $this->server->getProperties($node->getOwner(), array('{http://sabredav.org/ns}vcard-url')); - if (isset($props['{http://sabredav.org/ns}vcard-url'])) { - - $returnedProperties[200][$meCardProp] = new DAV\Property\Href( - $props['{http://sabredav.org/ns}vcard-url'] - ); - $pos = array_search($meCardProp, $requestedProperties); - unset($requestedProperties[$pos]); - - } - - } - - } - - } - - /** - * This event is triggered when a PROPPATCH method is executed - * - * @param array $mutations - * @param array $result - * @param DAV\INode $node - * @return bool - */ - public function updateProperties(&$mutations, &$result, DAV\INode $node) { - - if (!$node instanceof UserAddressBooks) { - return true; - } - - $meCard = '{http://calendarserver.org/ns/}me-card'; - - // The only property we care about - if (!isset($mutations[$meCard])) - return true; - - $value = $mutations[$meCard]; - unset($mutations[$meCard]); - - if ($value instanceof DAV\Property\IHref) { - $value = $value->getHref(); - $value = $this->server->calculateUri($value); - } elseif (!is_null($value)) { - $result[400][$meCard] = null; - return false; - } - - $innerResult = $this->server->updateProperties( - $node->getOwner(), - array( - '{http://sabredav.org/ns}vcard-url' => $value, - ) - ); - - $closureResult = false; - foreach($innerResult as $status => $props) { - if (is_array($props) && array_key_exists('{http://sabredav.org/ns}vcard-url', $props)) { - $result[$status][$meCard] = null; - $closureResult = ($status>=200 && $status<300); - } - - } - - return $result; - - } - - /** - * This functions handles REPORT requests specific to CardDAV - * - * @param string $reportName - * @param \DOMNode $dom - * @return bool - */ - public function report($reportName,$dom) { - - switch($reportName) { - case '{'.self::NS_CARDDAV.'}addressbook-multiget' : - $this->addressbookMultiGetReport($dom); - return false; - case '{'.self::NS_CARDDAV.'}addressbook-query' : - $this->addressBookQueryReport($dom); - return false; - default : - return; - - } - - - } - - /** - * This function handles the addressbook-multiget REPORT. - * - * This report is used by the client to fetch the content of a series - * of urls. Effectively avoiding a lot of redundant requests. - * - * @param \DOMNode $dom - * @return void - */ - public function addressbookMultiGetReport($dom) { - - $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)); - - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); - $propertyList = array(); - - foreach($hrefElems as $elem) { - - $uri = $this->server->calculateUri($elem->nodeValue); - list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties); - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); - - } - - /** - * This method is triggered before a file gets updated with new content. - * - * This plugin uses this method to ensure that Card nodes receive valid - * vcard data. - * - * @param string $path - * @param DAV\IFile $node - * @param resource $data - * @return void - */ - public function beforeWriteContent($path, DAV\IFile $node, &$data) { - - if (!$node instanceof ICard) - return; - - $this->validateVCard($data); - - } - - /** - * This method is triggered before a new file is created. - * - * This plugin uses this method to ensure that Card nodes receive valid - * vcard data. - * - * @param string $path - * @param resource $data - * @param DAV\ICollection $parentNode - * @return void - */ - public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode) { - - if (!$parentNode instanceof IAddressBook) - return; - - $this->validateVCard($data); - - } - - /** - * Checks if the submitted iCalendar data is in fact, valid. - * - * An exception is thrown if it's not. - * - * @param resource|string $data - * @return void - */ - protected function validateVCard(&$data) { - - // If it's a stream, we convert it to a string first. - if (is_resource($data)) { - $data = stream_get_contents($data); - } - - // Converting the data to unicode, if needed. - $data = DAV\StringUtil::ensureUTF8($data); - - try { - - $vobj = VObject\Reader::read($data); - - } catch (VObject\ParseException $e) { - - throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); - - } - - if ($vobj->name !== 'VCARD') { - throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.'); - } - - if (!isset($vobj->UID)) { - // No UID in vcards is invalid, but we'll just add it in anyway. - $vobj->add('UID', DAV\UUIDUtil::getUUID()); - $data = $vobj->serialize(); - } - - } - - - /** - * This function handles the addressbook-query REPORT - * - * This report is used by the client to filter an addressbook based on a - * complex query. - * - * @param \DOMNode $dom - * @return void - */ - protected function addressbookQueryReport($dom) { - - $query = new AddressBookQueryParser($dom); - $query->parse(); - - $depth = $this->server->getHTTPDepth(0); - - if ($depth==0) { - $candidateNodes = array( - $this->server->tree->getNodeForPath($this->server->getRequestUri()) - ); - } else { - $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); - } - - $validNodes = array(); - foreach($candidateNodes as $node) { - - if (!$node instanceof ICard) - continue; - - $blob = $node->get(); - if (is_resource($blob)) { - $blob = stream_get_contents($blob); - } - - if (!$this->validateFilters($blob, $query->filters, $query->test)) { - continue; - } - - $validNodes[] = $node; - - if ($query->limit && $query->limit <= count($validNodes)) { - // We hit the maximum number of items, we can stop now. - break; - } - - } - - $result = array(); - foreach($validNodes as $validNode) { - - if ($depth==0) { - $href = $this->server->getRequestUri(); - } else { - $href = $this->server->getRequestUri() . '/' . $validNode->getName(); - } - - list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); - - } - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); - - } - - /** - * Validates if a vcard makes it throught a list of filters. - * - * @param string $vcardData - * @param array $filters - * @param string $test anyof or allof (which means OR or AND) - * @return bool - */ - public function validateFilters($vcardData, array $filters, $test) { - - $vcard = VObject\Reader::read($vcardData); - - if (!$filters) return true; - - foreach($filters as $filter) { - - $isDefined = isset($vcard->{$filter['name']}); - if ($filter['is-not-defined']) { - if ($isDefined) { - $success = false; - } else { - $success = true; - } - } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) { - - // We only need to check for existence - $success = $isDefined; - - } else { - - $vProperties = $vcard->select($filter['name']); - - $results = array(); - if ($filter['param-filters']) { - $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']); - } - if ($filter['text-matches']) { - $texts = array(); - foreach($vProperties as $vProperty) - $texts[] = $vProperty->getValue(); - - $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']); - } - - if (count($results)===1) { - $success = $results[0]; - } else { - if ($filter['test'] === 'anyof') { - $success = $results[0] || $results[1]; - } else { - $success = $results[0] && $results[1]; - } - } - - } // else - - // There are two conditions where we can already determine whether - // or not this filter succeeds. - if ($test==='anyof' && $success) { - return true; - } - if ($test==='allof' && !$success) { - return false; - } - - } // foreach - - // If we got all the way here, it means we haven't been able to - // determine early if the test failed or not. - // - // This implies for 'anyof' that the test failed, and for 'allof' that - // we succeeded. Sounds weird, but makes sense. - return $test==='allof'; - - } - - /** - * Validates if a param-filter can be applied to a specific property. - * - * @todo currently we're only validating the first parameter of the passed - * property. Any subsequence parameters with the same name are - * ignored. - * @param array $vProperties - * @param array $filters - * @param string $test - * @return bool - */ - protected function validateParamFilters(array $vProperties, array $filters, $test) { - - foreach($filters as $filter) { - - $isDefined = false; - foreach($vProperties as $vProperty) { - $isDefined = isset($vProperty[$filter['name']]); - if ($isDefined) break; - } - - if ($filter['is-not-defined']) { - if ($isDefined) { - $success = false; - } else { - $success = true; - } - - // If there's no text-match, we can just check for existence - } elseif (!$filter['text-match'] || !$isDefined) { - - $success = $isDefined; - - } else { - - $success = false; - foreach($vProperties as $vProperty) { - // If we got all the way here, we'll need to validate the - // text-match filter. - $success = DAV\StringUtil::textMatch($vProperty[$filter['name']]->getValue(), $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']); - if ($success) break; - } - if ($filter['text-match']['negate-condition']) { - $success = !$success; - } - - } // else - - // There are two conditions where we can already determine whether - // or not this filter succeeds. - if ($test==='anyof' && $success) { - return true; - } - if ($test==='allof' && !$success) { - return false; - } - - } - - // If we got all the way here, it means we haven't been able to - // determine early if the test failed or not. - // - // This implies for 'anyof' that the test failed, and for 'allof' that - // we succeeded. Sounds weird, but makes sense. - return $test==='allof'; - - } - - /** - * Validates if a text-filter can be applied to a specific property. - * - * @param array $texts - * @param array $filters - * @param string $test - * @return bool - */ - protected function validateTextMatches(array $texts, array $filters, $test) { - - foreach($filters as $filter) { - - $success = false; - foreach($texts as $haystack) { - $success = DAV\StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']); - - // Breaking on the first match - if ($success) break; - } - if ($filter['negate-condition']) { - $success = !$success; - } - - if ($success && $test==='anyof') - return true; - - if (!$success && $test=='allof') - return false; - - - } - - // If we got all the way here, it means we haven't been able to - // determine early if the test failed or not. - // - // This implies for 'anyof' that the test failed, and for 'allof' that - // we succeeded. Sounds weird, but makes sense. - return $test==='allof'; - - } - - /** - * This event is triggered after webdav-properties have been retrieved. - * - * @return bool - */ - public function afterGetProperties($uri, &$properties) { - - // If the request was made using the SOGO connector, we must rewrite - // the content-type property. By default SabreDAV will send back - // text/x-vcard; charset=utf-8, but for SOGO we must strip that last - // part. - if (!isset($properties[200]['{DAV:}getcontenttype'])) - return; - - if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) { - return; - } - - if (strpos($properties[200]['{DAV:}getcontenttype'],'text/x-vcard')===0) { - $properties[200]['{DAV:}getcontenttype'] = 'text/x-vcard'; - } - - } - - /** - * This method is used to generate HTML output for the - * Sabre\DAV\Browser\Plugin. This allows us to generate an interface users - * can use to create new calendars. - * - * @param DAV\INode $node - * @param string $output - * @return bool - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof UserAddressBooks) - return; - - $output.= '
-

Create new address book

- -
-
- -
- '; - - return false; - - } - - /** - * This method allows us to intercept the 'mkcalendar' sabreAction. This - * action enables the user to create new calendars from the browser plugin. - * - * @param string $uri - * @param string $action - * @param array $postVars - * @return bool - */ - public function browserPostAction($uri, $action, array $postVars) { - - if ($action!=='mkaddressbook') - return; - - $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook'); - $properties = array(); - if (isset($postVars['{DAV:}displayname'])) { - $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; - } - $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); - return false; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php deleted file mode 100644 index 39433234a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Property/SupportedAddressData.php +++ /dev/null @@ -1,72 +0,0 @@ - 'text/vcard', 'version' => '3.0'), - // array('contentType' => 'text/vcard', 'version' => '4.0'), - ); - } - - $this->supportedData = $supportedData; - - } - - /** - * Serializes the property in a DOMDocument - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - - $prefix = - isset($server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV]) ? - $server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV] : - 'card'; - - foreach($this->supportedData as $supported) { - - $caldata = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, $prefix . ':address-data-type'); - $caldata->setAttribute('content-type',$supported['contentType']); - $caldata->setAttribute('version',$supported['version']); - $node->appendChild($caldata); - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/UserAddressBooks.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/UserAddressBooks.php deleted file mode 100644 index a9d4100be..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/UserAddressBooks.php +++ /dev/null @@ -1,260 +0,0 @@ -carddavBackend = $carddavBackend; - $this->principalUri = $principalUri; - - } - - /** - * Returns the name of this object - * - * @return string - */ - public function getName() { - - list(,$name) = DAV\URLUtil::splitPath($this->principalUri); - return $name; - - } - - /** - * Updates the name of this object - * - * @param string $name - * @return void - */ - public function setName($name) { - - throw new DAV\Exception\MethodNotAllowed(); - - } - - /** - * Deletes this object - * - * @return void - */ - public function delete() { - - throw new DAV\Exception\MethodNotAllowed(); - - } - - /** - * Returns the last modification date - * - * @return int - */ - public function getLastModified() { - - return null; - - } - - /** - * Creates a new file under this object. - * - * This is currently not allowed - * - * @param string $filename - * @param resource $data - * @return void - */ - public function createFile($filename, $data=null) { - - throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); - - } - - /** - * Creates a new directory under this object. - * - * This is currently not allowed. - * - * @param string $filename - * @return void - */ - public function createDirectory($filename) { - - throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); - - } - - /** - * Returns a single calendar, by name - * - * @param string $name - * @todo needs optimizing - * @return \AddressBook - */ - public function getChild($name) { - - foreach($this->getChildren() as $child) { - if ($name==$child->getName()) - return $child; - - } - throw new DAV\Exception\NotFound('Addressbook with name \'' . $name . '\' could not be found'); - - } - - /** - * Returns a list of addressbooks - * - * @return array - */ - public function getChildren() { - - $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri); - $objs = array(); - foreach($addressbooks as $addressbook) { - $objs[] = new AddressBook($this->carddavBackend, $addressbook); - } - return $objs; - - } - - /** - * Creates a new addressbook - * - * @param string $name - * @param array $resourceType - * @param array $properties - * @return void - */ - public function createExtendedCollection($name, array $resourceType, array $properties) { - - if (!in_array('{'.Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) { - throw new DAV\Exception\InvalidResourceType('Unknown resourceType for this collection'); - } - $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalUri; - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalUri, - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}write', - 'principal' => $this->principalUri, - 'protected' => true, - ), - - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/VCFExportPlugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/VCFExportPlugin.php deleted file mode 100644 index cf22f2651..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/VCFExportPlugin.php +++ /dev/null @@ -1,108 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); - - } - - /** - * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?export - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if ($method!='GET') return; - if ($this->server->httpRequest->getQueryString()!='export') return; - - // splitting uri - list($uri) = explode('?',$uri,2); - - $node = $this->server->tree->getNodeForPath($uri); - - if (!($node instanceof IAddressBook)) return; - - // Checking ACL, if available. - if ($aclPlugin = $this->server->getPlugin('acl')) { - $aclPlugin->checkPrivileges($uri, '{DAV:}read'); - } - - $this->server->httpResponse->setHeader('Content-Type','text/directory'); - $this->server->httpResponse->sendStatus(200); - - $nodes = $this->server->getPropertiesForPath($uri, array( - '{' . Plugin::NS_CARDDAV . '}address-data', - ),1); - - $this->server->httpResponse->sendBody($this->generateVCF($nodes)); - - // Returning false to break the event chain - return false; - - } - - /** - * Merges all vcard objects, and builds one big vcf export - * - * @param array $nodes - * @return string - */ - public function generateVCF(array $nodes) { - - $output = ""; - - foreach($nodes as $node) { - - if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) { - continue; - } - $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data']; - - // Parsing this node so VObject can clean up the output. - $output .= - VObject\Reader::read($nodeData)->serialize(); - - } - - return $output; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Version.php b/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Version.php deleted file mode 100644 index 8ba88ce43..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/CardDAV/Version.php +++ /dev/null @@ -1,26 +0,0 @@ -currentUser; - } - - - /** - * Authenticates the user based on the current request. - * - * If authentication is successful, true must be returned. - * If authentication fails, an exception must be thrown. - * - * @param DAV\Server $server - * @param string $realm - * @throws DAV\Exception\NotAuthenticated - * @return bool - */ - public function authenticate(DAV\Server $server, $realm) { - - $auth = new HTTP\BasicAuth(); - $auth->setHTTPRequest($server->httpRequest); - $auth->setHTTPResponse($server->httpResponse); - $auth->setRealm($realm); - $userpass = $auth->getUserPass(); - if (!$userpass) { - $auth->requireLogin(); - throw new DAV\Exception\NotAuthenticated('No basic authentication headers were found'); - } - - // Authenticates the user - if (!$this->validateUserPass($userpass[0],$userpass[1])) { - $auth->requireLogin(); - throw new DAV\Exception\NotAuthenticated('Username or password does not match'); - } - $this->currentUser = $userpass[0]; - return true; - } - - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php deleted file mode 100644 index d5c23ce28..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php +++ /dev/null @@ -1,101 +0,0 @@ -setHTTPRequest($server->httpRequest); - $digest->setHTTPResponse($server->httpResponse); - - $digest->setRealm($realm); - $digest->init(); - - $username = $digest->getUsername(); - - // No username was given - if (!$username) { - $digest->requireLogin(); - throw new DAV\Exception\NotAuthenticated('No digest authentication headers were found'); - } - - $hash = $this->getDigestHash($realm, $username); - // If this was false, the user account didn't exist - if ($hash===false || is_null($hash)) { - $digest->requireLogin(); - throw new DAV\Exception\NotAuthenticated('The supplied username was not on file'); - } - if (!is_string($hash)) { - throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); - } - - // If this was false, the password or part of the hash was incorrect. - if (!$digest->validateA1($hash)) { - $digest->requireLogin(); - throw new DAV\Exception\NotAuthenticated('Incorrect username'); - } - - $this->currentUser = $username; - return true; - - } - - /** - * Returns the currently logged in username. - * - * @return string|null - */ - public function getCurrentUser() { - - return $this->currentUser; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/Apache.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/Apache.php deleted file mode 100644 index cd674e18e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/Apache.php +++ /dev/null @@ -1,63 +0,0 @@ -httpRequest->getRawServerValue('REMOTE_USER'); - if (is_null($remoteUser)) { - throw new DAV\Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured'); - } - - $this->remoteUser = $remoteUser; - return true; - - } - - /** - * Returns information about the currently logged in user. - * - * If nobody is currently logged in, this method should return null. - * - * @return array|null - */ - public function getCurrentUser() { - - return $this->remoteUser; - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/BackendInterface.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/BackendInterface.php deleted file mode 100644 index cb3395372..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/BackendInterface.php +++ /dev/null @@ -1,36 +0,0 @@ -loadFile($filename); - - } - - /** - * Loads an htdigest-formatted file. This method can be called multiple times if - * more than 1 file is used. - * - * @param string $filename - * @return void - */ - public function loadFile($filename) { - - foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { - - if (substr_count($line, ":") !== 2) - throw new DAV\Exception('Malformed htdigest file. Every line should contain 2 colons'); - - list($username,$realm,$A1) = explode(':',$line); - - if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) - throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash'); - - $this->users[$realm . ':' . $username] = $A1; - - } - - } - - /** - * Returns a users' information - * - * @param string $realm - * @param string $username - * @return string - */ - public function getDigestHash($realm, $username) { - - return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/PDO.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/PDO.php deleted file mode 100644 index 1a057e2f5..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Backend/PDO.php +++ /dev/null @@ -1,65 +0,0 @@ -pdo = $pdo; - $this->tableName = $tableName; - - } - - /** - * Returns the digest hash for a user. - * - * @param string $realm - * @param string $username - * @return string|null - */ - public function getDigestHash($realm,$username) { - - $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM '.$this->tableName.' WHERE username = ?'); - $stmt->execute(array($username)); - $result = $stmt->fetchAll(); - - if (!count($result)) return; - - return $result[0]['digesta1']; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Plugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Plugin.php deleted file mode 100644 index ae3ab80db..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Auth/Plugin.php +++ /dev/null @@ -1,112 +0,0 @@ -authBackend = $authBackend; - $this->realm = $realm; - - } - - /** - * Initializes the plugin. This function is automatically called by the server - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'auth'; - - } - - /** - * Returns the current users' principal uri. - * - * If nobody is logged in, this will return null. - * - * @return string|null - */ - public function getCurrentUser() { - - $userInfo = $this->authBackend->getCurrentUser(); - if (!$userInfo) return null; - - return $userInfo; - - } - - /** - * This method is called before any HTTP method and forces users to be authenticated - * - * @param string $method - * @param string $uri - * @throws Sabre\DAV\Exception\NotAuthenticated - * @return bool - */ - public function beforeMethod($method, $uri) { - - $this->authBackend->authenticate($this->server,$this->realm); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/GuessContentType.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/GuessContentType.php deleted file mode 100644 index 639dfbdc1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/GuessContentType.php +++ /dev/null @@ -1,99 +0,0 @@ - 'image/jpeg', - 'gif' => 'image/gif', - 'png' => 'image/png', - - // groupware - 'ics' => 'text/calendar', - 'vcf' => 'text/x-vcard', - - // text - 'txt' => 'text/plain', - - ); - - /** - * Initializes the plugin - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - // Using a relatively low priority (200) to allow other extensions - // to set the content-type first. - $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200); - - } - - /** - * Handler for teh afterGetProperties event - * - * @param string $path - * @param array $properties - * @return void - */ - public function afterGetProperties($path, &$properties) { - - if (array_key_exists('{DAV:}getcontenttype', $properties[404])) { - - list(, $fileName) = DAV\URLUtil::splitPath($path); - $contentType = $this->getContentType($fileName); - - if ($contentType) { - $properties[200]['{DAV:}getcontenttype'] = $contentType; - unset($properties[404]['{DAV:}getcontenttype']); - } - - } - - } - - /** - * Simple method to return the contenttype - * - * @param string $fileName - * @return string - */ - protected function getContentType($fileName) { - - // Just grabbing the extension - $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1)); - if (isset($this->extensionMap[$extension])) - return $this->extensionMap[$extension]; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/MapGetToPropFind.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/MapGetToPropFind.php deleted file mode 100644 index b50d96f04..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/MapGetToPropFind.php +++ /dev/null @@ -1,57 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); - } - - /** - * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request - * - * @param string $method - * @param string $uri - * @return bool - */ - public function httpGetInterceptor($method, $uri) { - - if ($method!='GET') return true; - - $node = $this->server->tree->getNodeForPath($uri); - if ($node instanceof DAV\IFile) return; - - $this->server->invokeMethod('PROPFIND',$uri); - return false; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/Plugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/Plugin.php deleted file mode 100644 index f8baf84d6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/Plugin.php +++ /dev/null @@ -1,491 +0,0 @@ - 'icons/file', - 'Sabre\\DAV\\ICollection' => 'icons/collection', - 'Sabre\\DAVACL\\IPrincipal' => 'icons/principal', - 'Sabre\\CalDAV\\ICalendar' => 'icons/calendar', - 'Sabre\\CardDAV\\IAddressBook' => 'icons/addressbook', - 'Sabre\\CardDAV\\ICard' => 'icons/card', - ); - - /** - * The file extension used for all icons - * - * @var string - */ - public $iconExtension = '.png'; - - /** - * reference to server class - * - * @var Sabre\DAV\Server - */ - protected $server; - - /** - * enablePost turns on the 'actions' panel, which allows people to create - * folders and upload files straight from a browser. - * - * @var bool - */ - protected $enablePost = true; - - /** - * By default the browser plugin will generate a favicon and other images. - * To turn this off, set this property to false. - * - * @var bool - */ - protected $enableAssets = true; - - /** - * Creates the object. - * - * By default it will allow file creation and uploads. - * Specify the first argument as false to disable this - * - * @param bool $enablePost - * @param bool $enableAssets - */ - public function __construct($enablePost=true, $enableAssets = true) { - - $this->enablePost = $enablePost; - $this->enableAssets = $enableAssets; - - } - - /** - * Initializes the plugin and subscribes to events - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); - $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200); - if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler')); - } - - /** - * This method intercepts GET requests to collections and returns the html - * - * @param string $method - * @param string $uri - * @return bool - */ - public function httpGetInterceptor($method, $uri) { - - if ($method !== 'GET') return true; - - // We're not using straight-up $_GET, because we want everything to be - // unit testable. - $getVars = array(); - parse_str($this->server->httpRequest->getQueryString(), $getVars); - - if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) { - $this->serveAsset($getVars['assetName']); - return false; - } - - try { - $node = $this->server->tree->getNodeForPath($uri); - } catch (DAV\Exception\NotFound $e) { - // We're simply stopping when the file isn't found to not interfere - // with other plugins. - return; - } - if ($node instanceof DAV\IFile) - return; - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8'); - - $this->server->httpResponse->sendBody( - $this->generateDirectoryIndex($uri) - ); - - return false; - - } - - /** - * Handles POST requests for tree operations. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function httpPOSTHandler($method, $uri) { - - if ($method!='POST') return; - $contentType = $this->server->httpRequest->getHeader('Content-Type'); - list($contentType) = explode(';', $contentType); - if ($contentType !== 'application/x-www-form-urlencoded' && - $contentType !== 'multipart/form-data') { - return; - } - $postVars = $this->server->httpRequest->getPostVars(); - - if (!isset($postVars['sabreAction'])) - return; - - if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) { - - switch($postVars['sabreAction']) { - - case 'mkcol' : - if (isset($postVars['name']) && trim($postVars['name'])) { - // Using basename() because we won't allow slashes - list(, $folderName) = DAV\URLUtil::splitPath(trim($postVars['name'])); - $this->server->createDirectory($uri . '/' . $folderName); - } - break; - case 'put' : - if ($_FILES) $file = current($_FILES); - else break; - - list(, $newName) = DAV\URLUtil::splitPath(trim($file['name'])); - if (isset($postVars['name']) && trim($postVars['name'])) - $newName = trim($postVars['name']); - - // Making sure we only have a 'basename' component - list(, $newName) = DAV\URLUtil::splitPath($newName); - - if (is_uploaded_file($file['tmp_name'])) { - $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r')); - } - break; - - } - - } - $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri()); - $this->server->httpResponse->sendStatus(302); - return false; - - } - - /** - * Escapes a string for html. - * - * @param string $value - * @return string - */ - public function escapeHTML($value) { - - return htmlspecialchars($value,ENT_QUOTES,'UTF-8'); - - } - - /** - * Generates the html directory index for a given url - * - * @param string $path - * @return string - */ - public function generateDirectoryIndex($path) { - - $version = ''; - if (DAV\Server::$exposeVersion) { - $version = DAV\Version::VERSION ."-". DAV\Version::STABILITY; - } - - $html = " - - Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . " - - "; - - if ($this->enableAssets) { - $html.=''; - } - - $html .= " - -

Index for " . $this->escapeHTML($path) . "/

- - - "; - - $files = $this->server->getPropertiesForPath($path,array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ),1); - - $parent = $this->server->tree->getNodeForPath($path); - - - if ($path) { - - list($parentUri) = DAV\URLUtil::splitPath($path); - $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - - $icon = $this->enableAssets?'Parent':''; - $html.= " - - - - - - "; - - } - - foreach($files as $file) { - - // This is the current directory, we can skip it - if (rtrim($file['href'],'/')==$path) continue; - - list(, $name) = DAV\URLUtil::splitPath($file['href']); - - $type = null; - - - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); - - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); - - foreach($type as $k=>$v) { - - // Some name mapping is preferred - switch($v) { - case '{DAV:}collection' : - $type[$k] = 'Collection'; - break; - case '{DAV:}principal' : - $type[$k] = 'Principal'; - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = 'Addressbook'; - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = 'Calendar'; - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = 'Schedule Inbox'; - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = 'Schedule Outbox'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-read' : - $type[$k] = 'Proxy-Read'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-write' : - $type[$k] = 'Proxy-Write'; - break; - } - - } - $type = implode(', ', $type); - } - - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; - } - if (!$type) $type = 'Unknown'; - - $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; - $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(\DateTime::ATOM):''; - - $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); - - $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $icon = ''; - - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path?$path.'/':'') . $name); - foreach(array_reverse($this->iconMap) as $class=>$iconName) { - - if ($node instanceof $class) { - $icon = ''; - break; - } - - - } - - } - - $html.= " - - - - - - "; - - } - - $html.= ""; - - $output = ''; - - if ($this->enablePost) { - $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output)); - } - - $html.=$output; - - $html.= "
NameTypeSizeLast modified

$icon..[parent]
$icon{$displayName}{$type}{$size}{$lastmodified}

-
Generated by SabreDAV " . $version . " (c)2007-2015 http://sabre.io/
- - "; - - return $html; - - } - - /** - * This method is used to generate the 'actions panel' output for - * collections. - * - * This specifically generates the interfaces for creating new files, and - * creating new directories. - * - * @param DAV\INode $node - * @param mixed $output - * @return void - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof DAV\ICollection) - return; - - // We also know fairly certain that if an object is a non-extended - // SimpleCollection, we won't need to show the panel either. - if (get_class($node)==='Sabre\\DAV\\SimpleCollection') - return; - - $output.= '
-

Create new folder

- - Name:
- -
-
-

Upload file

- - Name (optional):
- File:
- -
- '; - - } - - /** - * This method takes a path/name of an asset and turns it into url - * suiteable for http access. - * - * @param string $assetName - * @return string - */ - protected function getAssetUrl($assetName) { - - return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); - - } - - /** - * This method returns a local pathname to an asset. - * - * @param string $assetName - * @return string - */ - protected function getLocalAssetPath($assetName) { - - $assetDir = __DIR__ . '/assets/'; - $path = $assetDir . $assetName; - - // Making sure people aren't trying to escape from the base path. - if (strpos(realpath($path), realpath($assetDir)) === 0) { - return $path; - } - throw new DAV\Exception\Forbidden('Path does not exist, or escaping from the base path was detected'); - } - - /** - * This method reads an asset from disk and generates a full http response. - * - * @param string $assetName - * @return void - */ - protected function serveAsset($assetName) { - - $assetPath = $this->getLocalAssetPath($assetName); - if (!file_exists($assetPath)) { - throw new DAV\Exception\NotFound('Could not find an asset with this name'); - } - // Rudimentary mime type detection - switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) { - - case 'ico' : - $mime = 'image/vnd.microsoft.icon'; - break; - - case 'png' : - $mime = 'image/png'; - break; - - default: - $mime = 'application/octet-stream'; - break; - - } - - $this->server->httpResponse->setHeader('Content-Type', $mime); - $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); - $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody(fopen($assetPath,'r')); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/favicon.ico b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/favicon.ico deleted file mode 100644 index 2b2c10a22cc7a57c4dc5d7156f184448f2bee92b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmc&&O-NKx6uz%14NNBzA}E}JHil3^6a|4&P_&6!RGSD}1rgCAC@`9dTC_@vqNoT8 z0%;dQR0K_{iUM;bf#3*%o1h^C2N7@IH_nmc?Va~t5U6~f`_BE&`Of`&_n~tUev3uN zziw!)bL*XR-2hy!51_yCgT8fb3s`VC=e=KcI5&)PGGQlpSAh?}1mK&Pg8c>z0Y`y$ zAT_6qJ%yV?|0!S$5WO@z3+`QD17OyXL4PyiM}RavtA7Tu7p)pn^p7Ks@m6m7)A}X$ z4Y+@;NrHYq_;V@RoZ|;69MPx!46Ftg*Tc~711C+J`JMuUfYwNBzXPB9sZm3WK9272 z&x|>@f_EO{b3cubqjOyc~J3I$d_lHIpN}q z!{kjX{c{12XF=~Z$w$kazXHB!b53>u!rx}_$e&dD`xNgv+MR&p2yN1xb0>&9t@28Z zV&5u#j_D=P9mI#){2s8@eGGj(?>gooo<%RT14>`VSZ&_l6GlGnan=^bemD56rRN{? zSAqZD$i;oS9SF6#f5I`#^C&hW@13s_lc3LUl(PWmHcop2{vr^kO`kP(*4!m=3Hn3e#Oc!a2;iDn+FbXzcOHEQ zbXZ)u93cj1WA=KS+M>jZ=oYyXq}1?ZdsjsX0A zkJXCvi~cfO@2ffd7r^;>=SsL-3U%l5HRoEZ#0r%`7%&% ziLTXJqU*JeXt3H5`AS#h(dpfl+`Ox|)*~QS%h&VO!d#)!>r3U5_YsDi2fY6Sd&vw% diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/addressbook.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/addressbook.png deleted file mode 100644 index c9acc84172dad59708b6b298b310b8d29eeeb671..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7232 zcmeHMXH=70vktu%m8u{iNEJu|p@k-dEbh;oQp! z004N*&5Ug6PsrAvofQCJ0J8kPi!O*#jGZWUL@!DZii8CiV2GYrpt(N^hqc9`Fu^B& z$Li3XlkoOV6epx598L6BMs3+BQ~d+z-T;7(J~aS^_Qg_wo>&~7pbMI-s0IP?7+sK~ z8WMsGKw!P`W~WG4yHi&7=u^IEEeuFsk5h*Vrvvz7DJUS--;Y3sQ*}YxxN!P<>ophz z+%}>3>Vm!{<%F~bB8Vg`5T>l6tfGX5sH+0iRFzfLRMb^qia-?zL=z0r0INcjpqg-q z8XN`%e*b~=IDtAOj2GP2$mDxCx}*#8rceUlU~o`SkaCc!GLeJ>L$$QDzz`L%ii#55 zLWvwqprEKq1hUi?#5W8hEE!G02T<@t0&oixr=z>6WJ@7j?2K@s&Aduv@jf_Eq zv3^*8EP+A>LzSW6o%VDlZ1Fg63i*c{f&86iI^SR_DuC_+0h6|E{^l9rO{5UX-o$`^ z_WYsV_TL%OJb;3R(c^9r`oovLEA)1 zN5s+d$KcTvEQUfv6TQ5!SY-@$JAofL!3_c_-b51Fnn=cPu}SA}i)5e<1`YqV)ot+` z>jr+5Z_+o>55Gk<+z&;->4K&f4Xf4 z*@?Opg@UK}VRyv%Go$a__mho-f4Ygk@O1vF+EFt7eA{D5{^b9!NI%8a+1W>M#5WER zMEbcxQ_Klo#O-7AcN@F`hGa~opfIFAkJbOyBk+{qpKERDlW4o4eu8d|CSvGq|Ls8h z12~2BGjMyXpCge(pGp7dYwVB0|0n%X(x2Mxf_>}29Rr14jc@PhgNi;Q!9RxNw=(VM z?f=Sho2~x}@($2{gX|#V*UNwD`ZY&8EdHfy2N}O!{!7=dIoe_IFI_vx`1SH%x_-^k z4vYUp7w2EsEG&V3w+f&Lv``pA3oHbg@?#%M;1t1p-E2-|c|#H-_R1@JzcNBq&XcTyD{j<6UEo zI0B^10yQ<)ne~ii-I(2&&uI#3*c>cE#4F`4kjf9Yf_ZrA9}M*e%!^mAR}E$zo9Zu3 zORdt;YtL*^N_oifn?F+u*|JG0gxkI3G?lUOvJIf`xLe&LM~@;G2ok9*GlYr@Go9q^ zEz`#}@G`i`7&#X(r(J-?mH-)D<>Cgao26JlhL$0DkE{zbmf-K(&|l=DD*83q#uQGU zBfS~GLtVKIm&%cnvH3DXpH6YX+nd@SWMv({*9pmf$($M!medu{2S+`*XCh6t-wjlCNsFC~wA ztcbPif81a*Yr3ti?%Lg-nYeKf;M-OF)`;J@?PY=n6fA4v!4Y!-RK&GE!e|%E#rOE^ zoFOK;>>L^svjrm2h{sHx(ZWK2#aNC-Iyotq$HA~D8yWs;GH{Czl&W$vSX9co>CY_Jx^p$*HJ(W=11 z2PK+Q^PIpqE{Q$`e^qZ}9+2ghQRIQ~E)V}@K_6kS`C%KoxHO#-#55!NTr9BH z$Gf;zV1y&|YokQx-X0?N;CZI65bn4U4me(v9v zt~$4NQ0?3rdSimd1&!>(WtET&)ww0Fc3t+>j11iq;<6g?d`;6m(?o-v--hwhsT@)D z0tIfwG=F`%g`|b}Fj5F8rtn%b=Q*N4c476cdco@zbJ{%maeVAa3Aft*j9lATg{8YasCtKb*)A6 z&p)mOy?*~>Rz>8ALTwmBci{43pegY-Rz z@eqaDI>U++PdYkRyrs|SG9%{4tSuE@XNM|3erf14y>^dbo%`=<#-XIL;5_gIA^7h8{BBuh8QFCLWCMz zjybw8t#D>`diqPK?x2S16YVFy=Uha*f^b!zgR4JJ1ZT}vGx=G3Onb-tVMfdR7D0dD z`glZQZoz|4cc%WQut~kEYa)eZXSH#>_lm0s76*KCRHhC+4HUTh7P+wZx$aU#-1PwD zSw(J?U7Ia5Q(8A&im4#q-#hZkl`FX)bHqF}g8`PDLh%P9%#Y3|7lfHZ&tmX5;&hWT z<5fx_3!1T4E*<_n0Pc)>)%JSuOcLRfI-%tzZ;gP!lL*7d())_#@^@3z?2K^hXQMez zmDt>^G}ZL42){nsmzQ~bTugJ`1R|Rsv0yNHf_;8B=k-ptG#j-&17x9xZl-tKyA+#6 ztpovzDJU_K53r$#T&zg?0a^a6-O5L(t?|buEi^7SuU@r^ia5%0=)partDxHpgbYVJ zf7*m%AIoR4_ci_L>MqZ+*&Gus1R1Cu%z!(p(e54?)BHFeZReqE|p;9|xOx#=mi9BR2G+bFNE z-l%Yn{d&pegCmMktsLdiG9F(%KkR?98FB?ye>(quR3lC}e?WDi>cbomAm@FBzd$g^ zV;Gwwu)@-|QaU>tzvM-TZ+$#1$T~4m^(w?f*!&T{%)ZSN@d_#2fI@5Rm8uEfOL0E93XfHpv@9<(lpR4mCW=Wvos2vP=RffHC{UD;WbfFVr5`q+#U0o}O{-IJS~eAv(asK`d^)1aHQ ziy$S$GkI8i4v*0tgFSBcLh-pWdJsuP?pNabJmX@Z_2OKk=(*Z@o+mKR5yN;JSh0YO z;_LHf?tcg;o|L=R`eA^auo>D~-ub>gnZ&aogPZNsR-M?G+g#spUS#6M*q%}`oxWOC zeSONjTSg*?%f^N&jiG)pQcJJ-=+ayFwkTSC&8&4Qaq3x))DfvNqQv#nS&JAl^B7b(Tia2WaO$md0D6JDpTV=dCihh zQ4IexnVGqpv4g8h+U=%}z6rv`flBeFYR&i{J@Lk9WM+$=Dcj=aFB+24EdQMu1Z35i3S>ORB({g=d7Sfc(FkLfW`1UO z{)U`pu+|%A1{9(x2s#>kQGEY6mjQOyW35MuUrjoDTz&Cr=j$yJ%f!y>222$-wGR#^ zPN>LNhJplQ3fd3rD)m~H=?JTcNmm6(GWiC#@iqmIk{%gyHD^6lw7tt zhMhcptW>Ps#OeK11)H|fL)fJ|=F`I?@r=te)Adow=3hu^^>w4s zs9r~e!tV^N-`jWDP0kaCJGkt1WnrBrilEV4XHRx~iV?{UMKtsA@Ek2{pXlE0kR$8K zvUgsr*wQ{^erT2u61=e2S;I*CS-d4bT4LYe&2Z2R+NOWD&k|R#};cqIY8^5>EQfcq87f$;c)(zU)I@Er+1@JoG9p za9q{*!gAy(yOu~ku$Py95#Ec`?GJ;Omy3r6Q~g@+Je)1x%Tl~Q%D9&QWWh;Vh-wC- zOV=yUyC*dJXWMyT-U3!~it`e2?`E=SeW8nCPUFMoJ(mdqi$ZMzi+PbsS?Ln`$rt&C z22GC)MK|$t@NY4UO}@4o{^JomDd#w}qr&tsN2Ce$^FHtM``!2b+`s4fUDtck$-!DgP+AZK z0*Tlh*ze5wM{NA~`9L5p$hHm%5Qz5?(ba?IVQ+`VQ$k@l0>vMI(L=*HQ6P{Jh8~8) zhX6E)KM+VH8$;Q3O;8AtU<`HFwMW>8SpY%A12I&KFqB+kSui;S0W(Y0B7;3gb2=TCYf>=vNBJfmV7>&pw-N3~8 zQzB``P$*{}@?$x;FlS<55G~>-1v%mm<2V+=>9{bsHVgr$ZpOg>migav{v1re|BMZb zq>?rlK)}NR5)cZIX%QR_?JaN);g%k>JK*m^!_hVaekS{qD1jV#1R|aW5NH%UB_IF* zU<6>3i<67C=ahtiqv7^*GL4}eiw(38+FD4Yt2P3S&_ipZG!WWo1Y*-8h!Fvg#!~?t zjY8e<><`ymfbgx+mWd>yi6e;^1yCWb(KsrB5*-mjG=gu~$(h;8+8q5zGlKsOb%TZQ zpGy3R$&5t%E7L|%&?Fo=&=^YBA^-unND>Wd!Y*in*y9KQgi}Tw=LxT%tVlOA+`IvF zJSj4QBM%Zlp+a0jaS=g8av&!t5Enxv1OFuS2kWNLzYE(C8xiRr4B-Eewz(P2ae;po zYC^=^_85;xCr|hjlCTPp6P0W$PX1baQ$O{AY9F41TsJfXwMh zR8I4mLhO%;Pg5k5nOR-Tdcx6XN4R2$6lk6VHpVUe3;E-_E^l z;WvaTDoU-bF1J9GmD?cd>W@L*>GEeVKAk$;TWsH{xI5X?bzYQL47CPO7l5 z5ZvG24+e41nenL!Cz(oqPcyHbc%VVUvkR@m&uhl=QQqwp{9VZNc>ELdR;+XGIVS8i z7X#ASnX~?4lH%(Av>N}mv_pJ5_ObB!%i%mNHT41lHFHmWO%BsNnV~Y)XJ=OOf-3fk zwwSuw#$6q~oE2CBR_p;MG4d3O3TZC3b=)f z?%4ef>b~PcF(TJb?iLkfuMHWDe+eKSFisLGms@6*-%SzSU4m|ZI*9vfV$35Z?P9s_ z5}2=Ib}^yc^_~Bd8Rpj|zjJ$K=iY!+uo)i|VX^x~iHZpVyDZP7x*X2twh$DKJKp)( z2kw6GgE8L7esT)Qb>Tr( z9~+tndeS6(A`%>gHQC0hnDpG4a`j{Aduz4YDCCG^iN3zz)g)Fyfx|L90rp1bdXl%Y zyUK~Ei6NRKl;(cL^HNGscg=^^sLE%FyI~!%3h=7B zzszfnFp9c81oKP0C;anLqlfT^WPY1Z@7{s4JZJc5don3}Xh*wKc9qr01boh0X+-zr z-qf9*1w-|Y*FK@7JEW($S3f3=?#nlTGX>o zt(ig!wA>)%ps~lJw{NHHdL&-|VuBEU;_^bKN){=e-{_*9Qhv9FU;F#;R|PkE4)WVM z=HwJY51A>IesjE?+ILM(w!LRQOy5=DdDTD#_rgkCirx#5w|MSzRP~eG*YVei^U7Xc z@2{#@#=_rszahJG@g*g&G=ccRN0CQLUk1ly`R!v#T*v|A`7e}>QcL5Ds}Y5&?%#%{ zzTCZX>D}3tp_2ZU=(^l05g}L9Q|&iO`OUnzx40nvcAq(>PhSw~;ph5%l=1mEf#$g7 z@?r@~+^U}K&dhgLQ=A8Rcl#fy6*uz<=qY-=drm~sPhpAAyl(2XCGD-PLIw-wlM1PL zW}AeEFkz_4g;nyns7_l7;f!T0?t)?Ttnql>%J@p-XEwfP{r$*-6e+(pXH{FgGKt!o zDC*O6Zc(6eCLuU4zDA7u{L-6OLap6}QbDP{FmVwZWURR%d zA)jN{@_V&v=4G!I=!boXA}=H9HQX za=Ol`$kPr_kk=(;)$e(AbsZcL%<9CZ9xHXVJ(>DdYTOfc9wxuSKQ2y+Kz5ef;}}j= zm-RY!>Anw#Akxz&Xy5Mh+ZLT25<=s7lW`A0Df-2gvUYqeJ)%I0`IQ!l>D`Y^RCb^8 zynUl9z@@s{OY88>K3(Hz880-S-r9N#Nt77PFL(qtN)?V&kT3EcErwIw^NP{ zeNb4ET>U~W2{+9xQO-KaOq-TCxJcOEhB$M}C+RoSz8aPCSmmr9^>zRkDVLAUY<7|o@!c4td_gCN@+g! zCSG5xp{c)j!CKiPdoR)~7CrtTtp=nuH4MO}^Hv#&f_JF8fr(3ko`nr6DHXwim#9i- zcH?gJNB-vYRtT%6nZ*Phy@^U*31-w)(-{I|MfFTP&tt$|LzN7FwOz^9R|@3QHI-5G zTG!6AgaQo(YUuzM#{h)@4JzPIgn!;bQ1U2WFyl8(ZqYWEu|Lq3YR_~Zwlau0bM zVQ((GR&hm0E&l8dR*NMD^K#jJ+qp&Elk1>PBPOb#0?ebqQ4ulFIg;sj=3v3uvCm*T9iJ>!a>etUgsb!o&U zspnei)pt2djm+ zC|Tti+{zZKG{1J%A>Zgn6zH_Lif%tEkaeT7L! zR3$sXM`KHcZb3!$O!dLd!eK^6Mt8FI><G75RR_~_31AY4R>1)BBS#x|MpEfUkf*zmlzFecJsHpfVWoCLB zyw@?Gt6=OnP7ynk$d&&31>XnubtAEnk{~MexewOfWvE@IZh|FmWMZPnEB3jjvC-Gh zAPT@@o4o`FK^7-@8Z)wkX=wutKbHozw4qn=U0q$@7~t_;^UD|H^OlWg2f*WT&tJV_ zk|-2!nbR2=-rMDFl4F#U7}e1lJmkcFBVND25%a<>(FzG+E!|S9v=aFFR#24it$UAjtSmRU4DWYZp&MOpC4>2 VG`ly3;d~;1Y%Cr2-!R7}{u}gUbfEwM diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/card.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/card.png deleted file mode 100644 index 2ce954866d853edb737a7e281de221f7846846f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5695 zcmeHLdpMN&7ay0fZjDGHnIU_zPvqiXLJR^Cf~{zk;|Xg)J1@|U9t5%pOaNj{q6Y#nCn|vq-~j?D zD!diI@|=%S+`T|A7iSESPSqnU+URkp44yXxg0qmazu zo<=T67lthmOmU260&daU-HFkmL{k#n(n1o;!SDd607!sws9`h~hGP!r<6?O0#n%Wp zjBf&ln!}fp@^W#7+0vN+%uvrj&p?-mG)BRUP_3L%N#^ii5M*Ew2sWFo$42SVnPh~%si`RfX@D>=(B)a^ zvZ81pful=fZCr#{!oUG6B9p=ZDRdfa5t9%|j{wc#aGoCa5u8L^#%4q?!}!P~A_52l zr~nOQA@ue15rXzSCh!z;FvwbVqp?1+%;OuuAuxC@NCcB_^O+|jm=4le!F0yodoHW_ z{(>Q$7$DJ*7k81+WnbQ|i2P((APFI8!FT7^X({@0!Wd5=&q%(Ol z>2H1Qs07MC={=aAwETiCb)djN;ZvN}EdGfu$-k~y0F8IIV)HIh z5{E|(ArJ{WC!DoA=P{UeStb?<6;XwS#%;;LNbQ}{CM7#|?9Xh32Dh%x3TtD+4p}NX z=P33;*+id>O0iGPc83m}%^N~*wua<4LyK_8p+k!J`?_)c}(WuGwGA@!ezR_oo+Od{B|q6241IjlJgdO zt0-DJLWQ6S$lE}Psp!!rN*<=Kx7=v*sanVQU?~tKKQ9K)+6f(AnY>P8K1pf#TqeBS z$Vqb#QNlAhwEXRph1E7nj+({eIjq7oeeFw^ARD9$ScuTq;*aYf_cHWln_$v*hrBQ& zVybQRkK{?ER}xT2L#||ZYfIr%*xgm%ohRO9jr5sc06l^D+Jk(!J-KhFTSUGjL^WK!sA+18ml0HpUiCAi%da1ixGAcTjB}ST<6|c^O zb;Vjdzop!GveBdU$c$xEG{o0xpU>7~^yrB?6!Iu*93$O?E8aIC^GhRcLjuKH(doGC z>g>#i`DLb~d%7dmo&#@oN8I$V@l#0CH&zpB(caUa5C@Z_AA>t;`qo}S#BKDns84A& zktlJp&Cn}0>mFqbanu~f=u7SW=ts7#i9_@eilO9@bm=aYe?khhFtg%k-MKB2QxH5&9dJ%BoUAv9<>+K z&!i+MxZT|9@WNE%=1ECpD+IjSo$Fm`lAXsSzc$_`Q8D7beUrKS=9H#9tA5cz@-zSJ ztyNdD&YeeuJ|~@lF^h(OGlz>j(wK~pb2<{61@xBK&W$9|T{>SKSO2bb(`g&OtP%DA zSDX9ZwR_qkoml4}`3K`XTB<&#>#7D6*Wi)|UD<%YW&@pE`LJijg{Ef5S5@gJ>|$)n zj%rk$h8I0@tT!%BTo{C!<@=lp`1OV<&F`siSjOE0BNma|_VK=(xxE|Ls0yV+7B-=O z=m_3H7&6yJKijn16Ir=>-HiidZ=^#X(rR`nK>FoG zN~@eDM8L5!cP+`l7kRHz1{K!D7eQik+D6=!^V?NCwRY zd4*hGrygYw%hs9nJLmK_ z&D@&!ISrcJ+5EvCQkavY^)y$uLcCzGQXyPa;);)+Z%Jp=Bz8hax`~|In?Enk3BF@? z&%35i)iK4Q_;pj@Wr^2gt08j@=Z9+iE4#Uqz4DfzNv<;}_ReO{+l-83CtIBIPdTy=p?FxkVsl{i?s8V`{<&J} zd&Sef7bsM?L{moBE_j?(U<-m*@mDS{9d0Ww#M?RUh}QgLv-llgMDdZ)(OIJZMiEXAELrv z`K@ze{)cl57I`gKme=kVBbx&L#O06>H(N9R#EEb$-IT@vZ7O7i3cHSe)Sxw-sPw@l~2F-b$rpmHb^+G_g^smmN*w zLHndv&MEb0CKi{MkFs>@YPc5ma<1QH>m3Dc%7ndO2G8%|j+5UUAI9V)btp4?bEBy2 z8n(6fn3%Cp_vy5vLD9^&Ej}v)gcG2br#O4hDyNySR+ySvq&LeydAJ*jABP)&$qY^P zW^2yc2^uAkCea|`C9#I5`xL!XU5M9nr!PFUcqO@;g4J)(rE4uS4W-RH!KrD>nonw?_3 z-{*F+?e3#yLgQT_RW;EA`%Bcm@0wRkKe2TLxc=m+MvcR^Hai_#>zD$46R(reH$M=t z>)F<6JuzkG3uWEEzaGrDt?frTyKHh4IrgT}e6RdZPsT&%9hpZIj@^s&IrVr-6P=$U&LPIV iv*os~CbdYl&#K_=OkBmrhgdFt(si=ij;pWxEzMhwDg6VRB9Bbk!O z6tW@c#N~iA9v>u-KoWsq0uoKBtI5n{}YsCay6 zXecgpHIB45La^gu2Pk;h~+gfLUcWpMrcU>L`Qr@4?^ zsuNNYCM1NckxX+eVll;tKr|we+=&D#flMVD8xV+80%6)C(2U8TGWakg9duh6)1MX80*Pk(rsS>Cv||GyLCBr&ySORpJLGTA=V} zrm3P(10fE}94j(n!hTS2pb%>@c&Zw7f|xLflo3Ln7|V56h-ho4e_#8jQ-s& zzd$I28_fSNvx09LcmJct?A`0Q3wm*e z`r?VnFDmKiCBuXFJooSP4*}5gRtywBz3qeGUmH}Mtd5LTuaQb6O%3@sYx9blBsI;A zlExZ^^pW9bCUwadsyU_q`ZA={G*Tl`6spRN&H3$BZ*Qcc;iN?*V?=h*$S;1fRzOvg zjx40UKgPN3tm+bUKvGRcm|>Yy13P6EO366rn9RIDhtr4DCd@2ZL505eHF3AQ{I=N7 z_jjkWkMSlR7@ryWaG*i!S3!YTDG*XJ@b9UP)|cl+qwZ`%t<(=+x(`xU&|`0nk3JZ^ znm1clS#tZfLY}W5a~(Z*$qio`fl$hywQpZacC%KC^Qx;3SD2J7Z`+$cow$>C+IqvA z?RR-)3IsXds1@arpAx(cVtXzr)_BcVd~V-bUKY>5^sNxQ(tKZ<tM9$5tdWz%ej0ez^ya?f6Uo&{%mXC{m5-D|#M})nH&YH-de-MQ_7o1- z#CD#)h-GilV$|^-wK4g~F|==SXVAR;kpJOJ1QJh|oFtLRE%fX| z6QYi#L~SE1SFVxVR-KodqG8#ke`e#DwxLZ(TjPcNUV+ccwwC;%P~eLCW!nG}$^T5* z2KCN+25ld-C9`res#RIF2P5tNTFFQKZl)nOJ3ud(wWUs zigV_31!`Y>wXYA;a?fF$(=%0GqwWNR=hK#(^eg1=iA+9BvdZsrMKSZFgBQm?$NQu` z8ZI~ectr(JHJI%V2a*>8RF)v5JGOae_N$Wm${=X$hi-;9sN)kvM_4=Pfs=| zS6fzbZGnxWj`*s@zJp#mRncNO44E8)p~G)x#}$pp#DF0V_ifP^|3taUb;iljLK-u= zA6bT$8xsfkC2wEwO#~I!zKmC8__u1;#63TLz6lka$q6`+BpOakz5ZZrZ}=A#73X%3 z({QtIxVNXrh(9j^5Q#4!7NNWO(CB>_QSo7(eHhPOeUC z?V^-SvGAl$U%2WnSuJ;3uuJhX*QK-%=kK-0b(|&s4!GIP?Z1h-aVs>vOUAkGm^gR36sr&_zj%NG!0qfsPSSWYtzWaqIns?Yya)Zez_}3HkrU=7y$!KPW zZqo*AUyHJ*^w{93gBM1h74AJPqoQZOtK4%?yY{_CD1T+9OuhO?(MDGYTIw*KlH3>b zk{(_!kE{DF^KfdIM`vPMhm!umMB}U?x0K|j^mm>bN1vDvM4ldQnvU{wMbGop6+;X3O=sODx{wXdGn!#Iowd2S3j=3v%!vAF1s^TonKOpMr4QroDy zquS4he|@yd1iv=v5>ENHRZ|4FXT>M&JyMF^WMy~ zw<@-N9+-ID5_^>URu8zEs0$dnY|E(MuOEe5*(a%}q7^4R-GMnWm~Y3pugtL=B#07U zXtoU}A4b&&Fjh&IkTl%00j+p_z%KoqL-6H zsy0St$fopeoNbs^0c&p6~Uw>GXW; zOnr}u;fk}!A*9*X0m+VuA=cNgd}=IGkJO6S`nCFi_>?27XD9XAi>;K9$g&uxo*Iv% zN0vCW6uK=7Ymg@KEN`Tq0*&i*<@zl0-FwdV`f-&ABBYP9l zmiYvvoml12_ApZyNX5#l7|GAe=qHVnpNUhum)mVcXL~&dyBYC$&+{kjvMrxjRlfHa z=*~;8`n4bAQ>(4E_l(})VnJ}E-H#ZyGF=R`%OK2VpUn?#rMG@{Z-l*1a~S z1#>P&^t~(6`?%86zGjT^@;pwC}Q1RrXH8pScuyuG^&BCf5knD@5o zK&{USOy%2DN>RroTCx-MxT4oq7D$qmE~dBEt;$#{8h6o>mjZQNW$3K>`0h!3+Z^?0 zq#?|y0K3Zesv@7I_q+ol(sUCW*PZ(!&TCuIQtx%>IL6+g8JE!i^75e35Vq1nvVQz++X-2Na-jNuQTP-uom}bmOGfQTu9TlY;TkVum z;*bjI!YPtVa*0h_yQ|zQicoe*?XuSpyE>ioJZC-6thN62f8YDQ|NH&__e{Kp`${d% zxtag~Xt}yLd7@9U@`qCc01P;#b~*rHYxrzm#Mf;VgChv%45-EkfBHh`XNCAh=B`mkqWXc&RKp2cb zpgc?{k}>2g!Wb?CeOG=a5x}t!M8G20D+xhgHxJNJEQLWDkz&aqTP*=;Hbm-Dstw)7 z0(29LKzoT4BvU~unY;v~EM-{PFsyCB&lkZ~6J$!cAq-Ea6`vW=5sMItAQA?N6cG_Y zjIbh#r92XaPN$Q|R1%eHiAGq;6e0wYTZ&{RN{Dd`Cs@Xj@+Al#B~@ZV!Qya)MIfN_ z;KXtui6@^IipVA@M6%Dup%#+lkc31bl1b9B7}7VH|2yZ)U@m7eRuV21jxB)8A;Cg8 z3>G0Wl!G!3juMXRVfetoUI>JY1xzLf3&lKC9+%HSU@ju&h(khPn8=04xX@gN8(I=B zgg{PcCX0YtOt&OcEU8pBh0Gw^Feo&0GKE1Vk9h<#xf}*Z3PXrks`Tu$YhLiC@zJ=6 zLcZ;4A%8P01=$ghlq-&q3HVHs(oS?{JZo$;k;Wu_gQ{fV{!@uBnCykf*G$TyFockZ z$0Eorxo`*+E<^~n0~w{D8^nb{w2Tn?#xY)CBDY^Qc7x>{VYm#H2Zo5HpjQ|q3+0P= zXb=yIb@J5*PS=!iUbbxqY3$^8Q#3I?(=#zGZ2%*j5aOr=U zl}%_2`>w`Gl!+>Xh!`BN^VfjmqX}hWi}_Nxav|fp_Ww7$;*9ce(!pQ__#dSQzo+6W zOaEaV5B=g4qEg1cp{E<|Eu_ijf(|Cz6D&e|k`!$|{n#~4XyclLIQt@A;t&MgelRfJ zV_Z@5U{4t0DmK-^OaPeDuKi z(&CWpjmOsQtr2w2>c76os!MO>Zd~@+fphg(f}aGx)P_KmV|7e|d`h-{(CbSd9%vhF zD-g`C@IGm~^*x?y6f)b$$f(kL+o!)U$40xV@ob+MB!+$Q;zep%A2)_S`lfgGV>2B9 z2hQ>-i5k`pru^}+Y(wx;#cNYXHhY%IDy=k10QI-Puy3raN~UJp(EI4je@<`VZ1?HVUK1>j=z9~ zl{0=f)Ri^teW7EQA8LJxp6vPl;CvP5(@r}mAB(9vX%?D)md|UCdeRjAi)V_}+@8Lp z<--RKwdQm!dA#D~+`h&nBG9rObL#GJ`OLCY*<82y(=%=Ca^Kva>j<>o>W_nTc9+v$ zsNwXY*CZaHb&-yW@e%gyZIQ z)%t;po`#$EpHyEF*Xd?z;`)+cyR-cdmmg~j?&S?J&*!SFN`tMJ>kYRSAD3;5aQ#Ii zR{NI2+Xp~teVKTnKK^9hrkBZ>1kONW?f{S@C>#n|_`;ai=yQcbzlq zS~HTLQzFs0=pWLDpVNzG7v0@QCT}%0UKDCq8QM1b+p+FOU4xc{)j9aq!g%3Ri@MHd z3bpF1Bb03<&FQmrcKOn=%ih)$<$>b7F#jcPcR2cSf=#=#u@$u{ih+?cf|O@bZNnd7 zgKA!(%1y%tjCajQ(0yx{R}nH%;C6GMm|1x2rGl zWlUuHy>q<6=1a1)o@~uoR0lQK{6UTpm3ma}uX76XE8A|^kq#>M#}dueH?m4DTuN4r zx5~gRX5)G^25RslhBCNg*)i`y`%H2nt(LsUp>ijM#;*G4VZT~;Af@(#cA-jy_jK*I zUOQv_b7x@Q&<^*cspF=MEX1k`>{O8WH66uqwwAFHxtI6d1*D>Wy$EXVlTxk4Wqu8f z7pSbP89Cbr-Hi*kUw)Ki{CfEcgCxP`7w;Z!dA%oSTc)KECZ(XTSuFb{z@tEIWSp8j6-6W8K(M@5_M zzM(ap>AgjFUdfp&hob@i^-&!S%)^__m?ce)TcZPr$q<4Xd?BHHSI3IyO0DU()TXl(gK1eqCMlT5UAa%-vKs*e z{1f4!PyJ0*MUPbL)dU&7_9Lq@1gY)9cQZ`ZT7hdl{S1!vUE5%}_BJ6sJyyrpsY-CI z*LF*lK$;-lW$ky-w+=Ms&LPicKhRG>`;bXD&CR(v|9MK|Ky|y%;EDyN%xC)aYr1&`JU!`) zU`3-!RzOL*K6r~#TZ=CUG47?UtS$+c&%6n=q;a(zkC;{Ty<{g*)hWBOG^q9KmSxpt ze|j9eKR3^y1z4}zR2x?ALSLU0aFj~G5oFA%A#gxM$>ZYQhTOKRZqbjE>~73kxI^Tg zm}+D?;NE)t8bh(^J};-Md&W6t`*ob+{rStcW``Q%lQLXv%9Q_RU7g*X@*Fm7{~MA6 BY2g3> diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/parent.png b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Browser/assets/icons/parent.png deleted file mode 100644 index 156fa64fd50f15d9e838326d42d68feba2c23c3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3474 zcmb7H2{=@1A3sx*ElZmr6{pFPFk2dCvP~gNlqE}xoS8YsL^ES%F!9N9n-C#MNxEne zBBg~(j5Sk9R1{fSrMg$$D9Z93RJZPTzwddzInT^F?|J|K-|zSS{_p#Lo{8V=yg^Ap zLjeE)C3`z-SL9BZ`pU@w01BKVoeu!$Cbqkm(93BfmBHPOgP2@8j1%qVAyEKeW+~!9 zi~v{&(qR^xV~!oHsK$b9ra9JgjT6C%w;uLq+lBFAw=idSMpyuY!o*ryD42<;2*7Sw z2!W#AfgAxxEzMhwDg6VRB9Bbk!O z6tW@c#N~iA9v>u-KoWsq0uoKBtI5n{}YsCay6 zXecgpHIB45La^gu2Pk;h~+gfLUcWpMrcU>L`Qr@4?^ zsuNNYCM1NckxX+eVll;tKr|we+=&D#flMVD8xV+80%6)C(2U8TGWakg9duh6)1MX80*Pk(rsS>Cv||GyLCBr&ySORpJLGTA=V} zrm3P(10fE}94j(n!hTS2pb%>@c&Zw7f|xLflo3Ln7|V56h-ho4e_#8jQ-s& zzd$I28_fSNvx09LcmJct?A`0Q3wm*e z`r?VnFDmKiCBuXFJooSP4*}5gRtywBz3qeGUmH}Mtd5LTuaQb6O%3@sYx9blBsI;A zlExZ^^pW9bCUwadsyU_q`ZA={G*Tl`6spRN&H3$BZ*Qcc;iN?*V?=h*$S;1fRzOvg zjx40UKgPN3tm+bUKvGRcm|>Yy13P6EO366rn9RIDhtr4DCd@2ZL505eHF3AQ{I=N7 z_jjkWkMSlR7@ryWaG*i!S3!YTDG*XJ@b9UP)|cl+qwZ`%t<(=+x(`xU&|`0nk3JZ^ znm1clS#tZfLY}W5a~(Z*$qio`fl$hywQpZacC%KC^Qx;3SD2J7Z`+$cow$>C+IqvA z?RR-)3IsXds1@arpAx(cVtXzr)_BcVd~V-bUKY>5^sNxQ(tKZ<tM9$5tdWz%ej0ez^ya?f6Uo&{%mXC{m5-D|#M})nH&YH-de-MQ_7o1- z#CD#)h-GilV$|^-wK4g~F|==SXVAR;kpJOJ1QJh|oFtLRE%fX| z6QYi#L~SE1SFVxVR-KodqG8#ke`e#DwxLZ(TjPcNUV+ccwwC;%P~eLCW!nG}$^T5* z2KCN+25ld-C9`res#RIF2P5tNTFFQKZl)nOJ3ud(wWUs zigV_31!`Y>wXYA;a?fF$(=%0GqwWNR=hK#(^eg1=iA+9BvdZsrMKSZFgBQm?$NQu` z8ZI~ectr(JHJI%V2a*>8RF)v5JGOae_N$Wm${=X$hi-;9sN)kvM_4=Pfs=| zS6fzbZGnxWj`*s@zJp#mRncNO44E8)p~G)x#}$pp#DF0V_ifP^|3taUb;iljLK-u= zA6bT$8xsfkC2wEwO#~I!zKmC8__u1;#63TLz6lka$q6`+BpOakz5ZZrZ}=A#73X%3 z({QtIxVNXrh(9j^5Q#4!7NNWO(CB>_QSo7(eHhPOeUC z?V^-SvGAl$U%2WnSuJ;3uuJhX*QK-%=kK-0b(|&s4!GIP?Z1h-aVs>vOUAkGm^gR36sr&_zj%NG!0qfsPSSWYtzWaqIns?Yya)Zez_}3HkrU=7y$!KPW zZqo*AUyHJ*^w{93gBM1h74AJPqoQZOtK4%?yY{_CD1T+9OuhO?(MDGYTIw*KlH3>b zk{(_!kE{DF^KfdIM`vPMhm!umMB}U?x0K|j^mm>bN1vDvM4ldQnvU{wMbGop6+;X3O=sODx{wXdGn!#Iowd2S3j=3v%!vAF1s^TonKOpMr4QroDy zquS4he|@yd1iv=v5>ENHRZ|4FXT>M&JyMF^WMy~ zw<@-N9+-ID5_^>URu8zEs0$dnY|E(MuOEe5*(a%}q7^4R-GMnWm~Y3pugtL=B#07U zXtoU}A4b&&Fjh&IkTl%00j+p_z%KoqL-6H zsy0St$fopeoNbs^0c&p6~Uw>GXW; zOnr}u;fk}!A*9*X0m+VuA=cNgd}=IGkJO6S`nCFi_>?27XD9XAi>;K9$g&uxo*Iv% zN0vCW6uK=7Ymg@KEN`Tq0*&i*<@zl0-FwdV`f-&ABBYP9l zmiYvvoml12_ApZyNX5#l7|GAe=qHVnpNUhum)mVcXL~&dyBYC$&+{kjvMrxjRlfHa z=*~;8`n4bAQ>(4E_l(})VnJ}E-H#ZyGF=R`%OK2VpUn?#rMG@{Z-l*1a~S z1#>P&^t~(6`?%86zGjT^@;pwC}Q1RrXH8pScuyuG^&BCf5knD@5o zK&{USOy%2DN>RroTCx-MxT4oq7D$qmE~dBEt;$#{8h6o>mjZQNW$3K>`0h!3+Z^?0 zq#?|y0K3Zesv@7I_q+ol(sUCW*PZ(!&TCuIQtx%>IL6+g8JE!i^75``Y6gr#$Vf>RpCKLvN z^z@Wgh%9j~V?{(G(ZN9^8k~#+r8YQ0GFRda0Ax=A7o;UY2qpnyvN-P8;j`zl7#7_f zyL?eFA(-m}C9?c7cu;u8(g<2c63vy4_4Lpn3rG@xWC#H|IEN zMI=Xi%=;hKLjyzR(HW#L>f-m|B$7Ke5ka^lJU%Tg4VUJCgLzE6y{oG$oUVFczU!rZ_2oL0;H z&5t^eUu9VPeU&*d$vSj%P9WQSobC=a=D*AN7q~%aTI07QFjZNbuuwkYoe>#hX zKy(DA!3+ij;pmVof$5w`lvE@U=J7*dK1<4`ghMIG7&4tkn%b&NoMN5AMy8}Gkc;vsT7Ri^K?+A#O%>REy`Xn}4zK=*gQyluhl5<5v{5cF*c5FVjVNvKj zUjYKrc^{6|f9ri%NcyL>VUkHCYp744htOcUr0u5;#NU7;yib8gKzV~|BzLPc$t6mCu;ISHTOg@8{`1v`W!_A;zE{UtDEr zWmQzGgfqu3{F+s9ezhnZa2)Z9uFly_+2XbaSRA()h1LeQ%&&Ta2PA<^aue(oO@qP85~&ePYGTyx|%5fgoi~uQi^> z^R~lr#QqcJ9`=+ib7xnz%$~dZ(#8E~<6(XZPiMH$_!Ml?JJ6rtbw!X2C~!EOMyL41 z-v0ivmTOnq+Am#F4`QP_Mhe0h8NZ5|?KO3W-!!Cjs8aOMy5sXFJX8AT#T`)B&>#fW zxbD<`wpV0`12&Y+{k{Meex?3e$;QU!mcJ4v$8TxiUPS|M4XSc}dtiW{(Zr;vuD!$36|9bDk{df1+vF6yo{oS3n4FqdohLjw_`5~{?3hM@%4|Xg-TL<{!O54QXQz7JRVWFg}t9!8~JKOWQ zXT$280Ugww0mhTe$9;WZJdLKd-;~kNxBIDXL_kvgLD9N(f|+G{RN;<4&mB8hEZn+a z+3YxvY)Whl6NGL$-)81KwT}IPb3++5b4S$3MoP7|K-3wHW^b(cq+{x2V4&ZIg?(=#W?0>RtB8i22g=Mf#Bi7Z;Qg%I8uy z*SJIR4Cq4fut#HiS#`znyk{DP`QOH`zcljnPSW~OyLiaeYxg{rZNc>=*5$S#6 z$DhrKGs_ges@%z*nI(Rk)O@Yryvm;X-SyNTRcbufHvzWR=#hhc1O460))F6`PN|DY z3N-G<8bnv+8mVk<3D`e5IeDrxy2})>S8_tJLtXkDs_SdLBZDEVnwznb8v)mu!!o08 z7^@cU@7;4z%`E)kNXGcUL9#`|B&23EE;ehpb-DTai0G=h~gE@oaLKP&CrKmLvQXGSLUx*u87evamzB<=YVdFE$c6=%+IIj zb(*XE*lGwo=q*;Z1ER)M`pb3R5s5^UVf$+Om}t;B)R!2drR3M%)xq=tY%(cF401Bm znz2|^8m9-u7y=s&RCQ@I)%Zd4l*@;n%^BA+O8oYV; zmSUuz`b>|k@p)ISH#d9PaR=L0-KLNK{u<^UE@8}_72?Kkaasu|BFt3t;CyiGfmo|8 z(kAw1)_s##8y6c|6xVLbRqmhnZ1@VU&hK0arg4ab{>^E|*JUl1MFe%lJw=>GLdz5O zqOLY_RmOhP?FW+24ZmO?*^Hr+W!1tihruhm?RS;tp3u`h`8t|>`Ww6Qg=sQYTaFybZ9Z5Y@)k-y*mr@hLWqYfqA;VU2Q zHBNnUR%_UBz^IQtcD4Ra(_LZl;oJ^`p5m8BPp(6#hwq6xvtIJ3L8A|>e|^?8zem00 zJ}v)`(cV4{T4cWWn<_^C={vqR@1<>k^WGgle2cM~Z}`oF!WYridplU=KYwT0mV}rO zX5u#U!P=-pW9^}4eV(a$l|!cJ(pT5KcRxu8R2%DxtGdD2wM$d{a=lZP&BEZyfE7_c zor&e>lw>hoN`E&ponu-*TOm7XrJC1)R{9#Rb!W65F4!5Ar}WPIboMORSS924n9T=H zFK9bUq3sFy!&sxys8c8RRp1}>PijmWx~i8JZkQKto%Ps~doqk#*?Q^i*trmf%RqJU z;#=VlXJ@*Ot@Tm7cQh70`TUgTilj9ygRTDMD_Wna-`nRr)Vk*kX+&p+_hoCb$go`z z(mGzZL{lr@+q}G%)%W7iTIF2Zt6P@o>RvYSKjuC`!I)?+XJl_PxHBh4;f`g!!E>A4VP5$SQ5CLF_=0&A@lnwhleUz`(>k&GBZWCDT3kgv cngetChildren() as $child) { - - if ($child->getName()==$name) return $child; - - } - throw new Exception\NotFound('File not found: ' . $name); - - } - - /** - * Checks is a child-node exists. - * - * It is generally a good idea to try and override this. Usually it can be optimized. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - try { - - $this->getChild($name); - return true; - - } catch(Exception\NotFound $e) { - - return false; - - } - - } - - /** - * Creates a new file in the directory - * - * Data will either be supplied as a stream resource, or in certain cases - * as a string. Keep in mind that you may have to support either. - * - * After succesful creation of the file, you may choose to return the ETag - * of the new file here. - * - * The returned ETag must be surrounded by double-quotes (The quotes should - * be part of the actual string). - * - * If you cannot accurately determine the ETag, you should not return it. - * If you don't store the file exactly as-is (you're transforming it - * somehow) you should also not return an ETag. - * - * This means that if a subsequent GET to this new file does not exactly - * return the same contents of what was submitted here, you are strongly - * recommended to omit the ETag. - * - * @param string $name Name of the file - * @param resource|string $data Initial payload - * @return null|string - */ - public function createFile($name, $data = null) { - - throw new Exception\Forbidden('Permission denied to create file (filename ' . $name . ')'); - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @throws Exception\Forbidden - * @return void - */ - public function createDirectory($name) { - - throw new Exception\Forbidden('Permission denied to create directory'); - - } - - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ConflictingLock.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ConflictingLock.php deleted file mode 100644 index a551a1e37..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ConflictingLock.php +++ /dev/null @@ -1,37 +0,0 @@ -lock) { - $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); - $errorNode->appendChild($error); - if (!is_object($this->lock)) var_dump($this->lock); - $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/FileNotFound.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/FileNotFound.php deleted file mode 100644 index 863302bb7..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/FileNotFound.php +++ /dev/null @@ -1,19 +0,0 @@ -ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); - $errorNode->appendChild($error); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php deleted file mode 100644 index d2a5a8e82..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/LengthRequired.php +++ /dev/null @@ -1,30 +0,0 @@ -message = 'The locktoken supplied does not match any locks on this entity'; - - } - - /** - * This method allows the exception to include additional information into the WebDAV error response - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri'); - $errorNode->appendChild($error); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/Locked.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/Locked.php deleted file mode 100644 index dfba856e5..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/Locked.php +++ /dev/null @@ -1,73 +0,0 @@ -lock = $lock; - - } - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 423; - - } - - /** - * This method allows the exception to include additional information into the WebDAV error response - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - if ($this->lock) { - $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); - $errorNode->appendChild($error); - - $href = $errorNode->ownerDocument->createElementNS('DAV:','d:href'); - $href->appendChild($errorNode->ownerDocument->createTextNode($this->lock->uri)); - $error->appendChild( - $href - ); - } - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ReportNotSupported.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ReportNotSupported.php deleted file mode 100644 index 2bfa3366c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ReportNotSupported.php +++ /dev/null @@ -1,32 +0,0 @@ -ownerDocument->createElementNS('DAV:','d:supported-report'); - $errorNode->appendChild($error); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ServiceUnavailable.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ServiceUnavailable.php deleted file mode 100644 index 26a3f404e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/ServiceUnavailable.php +++ /dev/null @@ -1,30 +0,0 @@ - - * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). - * @license http://sabre.io/license/ Modified BSD License - */ -class ServiceUnavailable extends DAV\Exception { - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 503; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php deleted file mode 100644 index 990ca0ccb..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php +++ /dev/null @@ -1,28 +0,0 @@ -path . '/' . $name; - file_put_contents($newPath,$data); - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - $newPath = $this->path . '/' . $name; - mkdir($newPath); - - } - - /** - * Returns a specific child node, referenced by its name - * - * This method must throw DAV\Exception\NotFound if the node does not - * exist. - * - * @param string $name - * @throws DAV\Exception\NotFound - * @return DAV\INode - */ - public function getChild($name) { - - $path = $this->path . '/' . $name; - - if (!file_exists($path)) throw new DAV\Exception\NotFound('File with name ' . $path . ' could not be located'); - - if (is_dir($path)) { - - return new Directory($path); - - } else { - - return new File($path); - - } - - } - - /** - * Returns an array with all the child nodes - * - * @return DAV\INode[] - */ - public function getChildren() { - - $nodes = array(); - foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); - return $nodes; - - } - - /** - * Checks if a child exists. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - $path = $this->path . '/' . $name; - return file_exists($path); - - } - - /** - * Deletes all files in this directory, and then itself - * - * @return void - */ - public function delete() { - - foreach($this->getChildren() as $child) $child->delete(); - rmdir($this->path); - - } - - /** - * Returns available diskspace information - * - * @return array - */ - public function getQuotaInfo() { - - return array( - disk_total_space($this->path)-disk_free_space($this->path), - disk_free_space($this->path) - ); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/File.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/File.php deleted file mode 100644 index c51635df0..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/File.php +++ /dev/null @@ -1,91 +0,0 @@ -path,$data); - - } - - /** - * Returns the data - * - * @return string - */ - public function get() { - - return fopen($this->path,'r'); - - } - - /** - * Delete the current file - * - * @return void - */ - public function delete() { - - unlink($this->path); - - } - - /** - * Returns the size of the node, in bytes - * - * @return int - */ - public function getSize() { - - return filesize($this->path); - - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined - * - * @return mixed - */ - public function getETag() { - - return null; - - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * - * @return mixed - */ - public function getContentType() { - - return null; - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/Node.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/Node.php deleted file mode 100644 index e3f2674e1..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FS/Node.php +++ /dev/null @@ -1,82 +0,0 @@ -path = $path; - - } - - - - /** - * Returns the name of the node - * - * @return string - */ - public function getName() { - - list(, $name) = DAV\URLUtil::splitPath($this->path); - return $name; - - } - - /** - * Renames the node - * - * @param string $name The new name - * @return void - */ - public function setName($name) { - - list($parentPath, ) = DAV\URLUtil::splitPath($this->path); - list(, $newName) = DAV\URLUtil::splitPath($name); - - $newPath = $parentPath . '/' . $newName; - rename($this->path,$newPath); - - $this->path = $newPath; - - } - - - - /** - * Returns the last modification time, as a unix timestamp - * - * @return int - */ - public function getLastModified() { - - return filemtime($this->path); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Directory.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Directory.php deleted file mode 100644 index 3774adb72..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Directory.php +++ /dev/null @@ -1,159 +0,0 @@ -path . '/' . $name; - file_put_contents($newPath,$data); - - return '"' . md5_file($newPath) . '"'; - - } - - /** - * Creates a new subdirectory - * - * @param string $name - * @return void - */ - public function createDirectory($name) { - - // We're not allowing dots - if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - $newPath = $this->path . '/' . $name; - mkdir($newPath); - - } - - /** - * Returns a specific child node, referenced by its name - * - * This method must throw Sabre\DAV\Exception\NotFound if the node does not - * exist. - * - * @param string $name - * @throws DAV\Exception\NotFound - * @return DAV\INode - */ - public function getChild($name) { - - $path = $this->path . '/' . $name; - - if (!file_exists($path)) throw new DAV\Exception\NotFound('File could not be located'); - if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - - if (is_dir($path)) { - - return new Directory($path); - - } else { - - return new File($path); - - } - - } - - /** - * Checks if a child exists. - * - * @param string $name - * @return bool - */ - public function childExists($name) { - - if ($name=='.' || $name=='..') - throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - - $path = $this->path . '/' . $name; - return file_exists($path); - - } - - /** - * Returns an array with all the child nodes - * - * @return DAV\INode[] - */ - public function getChildren() { - - $nodes = array(); - foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node); - return $nodes; - - } - - /** - * Deletes all files in this directory, and then itself - * - * @return bool - */ - public function delete() { - - // Deleting all children - foreach($this->getChildren() as $child) $child->delete(); - - // Removing resource info, if its still around - if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav'); - - // Removing the directory itself - rmdir($this->path); - - return parent::delete(); - - } - - /** - * Returns available diskspace information - * - * @return array - */ - public function getQuotaInfo() { - - return array( - disk_total_space($this->path)-disk_free_space($this->path), - disk_free_space($this->path) - ); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/File.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/File.php deleted file mode 100644 index 1b7f0084b..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/File.php +++ /dev/null @@ -1,146 +0,0 @@ -path,$data); - return '"' . md5_file($this->path) . '"'; - - } - - /** - * Updates the file based on a range specification. - * - * The first argument is the data, which is either a readable stream - * resource or a string. - * - * The second argument is the type of update we're doing. - * This is either: - * * 1. append - * * 2. update based on a start byte - * * 3. update based on an end byte - *; - * The third argument is the start or end byte. - * - * After a successful put operation, you may choose to return an ETag. The - * etag must always be surrounded by double-quotes. These quotes must - * appear in the actual string you're returning. - * - * Clients may use the ETag from a PUT request to later on make sure that - * when they update the file, the contents haven't changed in the mean - * time. - * - * @param resource|string $data - * @param int $rangeType - * @param int $offset - * @return string|null - */ - public function patch($data, $rangeType, $offset = null) { - - switch($rangeType) { - case 1 : - $f = fopen($this->path, 'a'); - break; - case 2 : - $f = fopen($this->path, 'c'); - fseek($f,$offset); - break; - case 3 : - $f = fopen($this->path, 'c'); - fseek($f, $offset, SEEK_END); - break; - } - if (is_string($data)) { - fwrite($f, $data); - } else { - stream_copy_to_stream($data,$f); - } - fclose($f); - return '"' . md5_file($this->path) . '"'; - - } - - /** - * Returns the data - * - * @return resource - */ - public function get() { - - return fopen($this->path,'r'); - - } - - /** - * Delete the current file - * - * @return bool - */ - public function delete() { - - unlink($this->path); - return parent::delete(); - - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined - * - * @return string|null - */ - public function getETag() { - - return '"' . md5_file($this->path). '"'; - - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * - * @return string|null - */ - public function getContentType() { - - return null; - - } - - /** - * Returns the size of the file, in bytes - * - * @return int - */ - public function getSize() { - - return filesize($this->path); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php deleted file mode 100644 index 73467fffc..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/FSExt/Node.php +++ /dev/null @@ -1,214 +0,0 @@ -getResourceData(); - - foreach($properties as $propertyName=>$propertyValue) { - - // If it was null, we need to delete the property - if (is_null($propertyValue)) { - if (isset($resourceData['properties'][$propertyName])) { - unset($resourceData['properties'][$propertyName]); - } - } else { - $resourceData['properties'][$propertyName] = $propertyValue; - } - - } - - $this->putResourceData($resourceData); - return true; - } - - /** - * Returns a list of properties for this nodes.; - * - * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author - * If the array is empty, all properties should be returned - * - * @param array $properties - * @return array - */ - function getProperties($properties) { - - $resourceData = $this->getResourceData(); - - // if the array was empty, we need to return everything - if (!$properties) return $resourceData['properties']; - - $props = array(); - foreach($properties as $property) { - if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; - } - - return $props; - - } - - /** - * Returns the path to the resource file - * - * @return string - */ - protected function getResourceInfoPath() { - - list($parentDir) = DAV\URLUtil::splitPath($this->path); - return $parentDir . '/.sabredav'; - - } - - /** - * Returns all the stored resource information - * - * @return array - */ - protected function getResourceData() { - - $path = $this->getResourceInfoPath(); - if (!file_exists($path)) return array('properties' => array()); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'r'); - flock($handle,LOCK_SH); - $data = ''; - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // We're all good - fclose($handle); - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (!isset($data[$this->getName()])) { - return array('properties' => array()); - } - - $data = $data[$this->getName()]; - if (!isset($data['properties'])) $data['properties'] = array(); - return $data; - - } - - /** - * Updates the resource information - * - * @param array $newData - * @return void - */ - protected function putResourceData(array $newData) { - - $path = $this->getResourceInfoPath(); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'a+'); - flock($handle,LOCK_EX); - $data = ''; - - rewind($handle); - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - $data[$this->getName()] = $newData; - ftruncate($handle,0); - rewind($handle); - - fwrite($handle,serialize($data)); - fclose($handle); - - } - - /** - * Renames the node - * - * @param string $name The new name - * @return void - */ - public function setName($name) { - - list($parentPath, ) = DAV\URLUtil::splitPath($this->path); - list(, $newName) = DAV\URLUtil::splitPath($name); - $newPath = $parentPath . '/' . $newName; - - // We're deleting the existing resourcedata, and recreating it - // for the new path. - $resourceData = $this->getResourceData(); - $this->deleteResourceData(); - - rename($this->path,$newPath); - $this->path = $newPath; - $this->putResourceData($resourceData); - - - } - - /** - * @return bool - */ - public function deleteResourceData() { - - // When we're deleting this node, we also need to delete any resource information - $path = $this->getResourceInfoPath(); - if (!file_exists($path)) return true; - - // opening up the file, and creating a shared lock - $handle = fopen($path,'a+'); - flock($handle,LOCK_EX); - $data = ''; - - rewind($handle); - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (isset($data[$this->getName()])) unset($data[$this->getName()]); - ftruncate($handle,0); - rewind($handle); - fwrite($handle,serialize($data)); - fclose($handle); - - return true; - } - - public function delete() { - - return $this->deleteResourceData(); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/File.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/File.php deleted file mode 100644 index caf3a2cc6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/File.php +++ /dev/null @@ -1,85 +0,0 @@ - array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param array $mutations - * @return bool|array - */ - function updateProperties($mutations); - - /** - * Returns a list of properties for this nodes. - * - * The properties list is a list of propertynames the client requested, - * encoded in clark-notation {xmlnamespace}tagname - * - * If the array is empty, it means 'all properties' were requested. - * - * Note that it's fine to liberally give properties back, instead of - * conforming to the list of requested properties. - * The Server class will filter out the extra. - * - * @param array $properties - * @return void - */ - function getProperties($properties); - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/IQuota.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/IQuota.php deleted file mode 100644 index 74b759cc6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/IQuota.php +++ /dev/null @@ -1,27 +0,0 @@ -dataDir = $dataDir; - - } - - protected function getFileNameForUri($uri) { - - return $this->dataDir . '/sabredav_' . md5($uri) . '.locks'; - - } - - - /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects - * - * This method should return all the locks for a particular uri, including - * locks that might be set on a parent uri. - * - * If returnChildLocks is set to true, this method should also look for - * any locks in the subtree of the uri for locks. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks) { - - $lockList = array(); - $currentPath = ''; - - foreach(explode('/',$uri) as $uriPart) { - - // weird algorithm that can probably be improved, but we're traversing the path top down - if ($currentPath) $currentPath.='/'; - $currentPath.=$uriPart; - - $uriLocks = $this->getData($currentPath); - - foreach($uriLocks as $uriLock) { - - // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0 - if($uri==$currentPath || $uriLock->depth!=0) { - $uriLock->uri = $currentPath; - $lockList[] = $uriLock; - } - - } - - } - - // Checking if we can remove any of these locks - foreach($lockList as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); - } - return $lockList; - - } - - /** - * Locks a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lock($uri, LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 1800; - $lockInfo->created = time(); - - $locks = $this->getLocks($uri,false); - foreach($locks as $k=>$lock) { - if ($lock->token == $lockInfo->token) unset($locks[$k]); - } - $locks[] = $lockInfo; - $this->putData($uri,$locks); - return true; - - } - - /** - * Removes a lock from a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlock($uri, LockInfo $lockInfo) { - - $locks = $this->getLocks($uri,false); - foreach($locks as $k=>$lock) { - - if ($lock->token == $lockInfo->token) { - - unset($locks[$k]); - $this->putData($uri,$locks); - return true; - - } - } - return false; - - } - - /** - * Returns the stored data for a uri - * - * @param string $uri - * @return array - */ - protected function getData($uri) { - - $path = $this->getFilenameForUri($uri); - if (!file_exists($path)) return array(); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'r'); - flock($handle,LOCK_SH); - $data = ''; - - // Reading data until the eof - while(!feof($handle)) { - $data.=fread($handle,8192); - } - - // We're all good - fclose($handle); - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (!$data) return array(); - return $data; - - } - - /** - * Updates the lock information - * - * @param string $uri - * @param array $newData - * @return void - */ - protected function putData($uri,array $newData) { - - $path = $this->getFileNameForUri($uri); - - // opening up the file, and creating a shared lock - $handle = fopen($path,'a+'); - flock($handle,LOCK_EX); - ftruncate($handle,0); - rewind($handle); - - fwrite($handle,serialize($newData)); - fclose($handle); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/File.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/File.php deleted file mode 100644 index 891aa21a5..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/File.php +++ /dev/null @@ -1,183 +0,0 @@ -locksFile = $locksFile; - - } - - /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects - * - * This method should return all the locks for a particular uri, including - * locks that might be set on a parent uri. - * - * If returnChildLocks is set to true, this method should also look for - * any locks in the subtree of the uri for locks. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks) { - - $newLocks = array(); - - $locks = $this->getData(); - - foreach($locks as $lock) { - - if ($lock->uri === $uri || - //deep locks on parents - ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) || - - // locks on children - ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) { - - $newLocks[] = $lock; - - } - - } - - // Checking if we can remove any of these locks - foreach($newLocks as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); - } - return $newLocks; - - } - - /** - * Locks a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lock($uri, LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 1800; - $lockInfo->created = time(); - $lockInfo->uri = $uri; - - $locks = $this->getData(); - - foreach($locks as $k=>$lock) { - if ( - ($lock->token == $lockInfo->token) || - (time() > $lock->timeout + $lock->created) - ) { - unset($locks[$k]); - } - } - $locks[] = $lockInfo; - $this->putData($locks); - return true; - - } - - /** - * Removes a lock from a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlock($uri, LockInfo $lockInfo) { - - $locks = $this->getData(); - foreach($locks as $k=>$lock) { - - if ($lock->token == $lockInfo->token) { - - unset($locks[$k]); - $this->putData($locks); - return true; - - } - } - return false; - - } - - /** - * Loads the lockdata from the filesystem. - * - * @return array - */ - protected function getData() { - - if (!file_exists($this->locksFile)) return array(); - - // opening up the file, and creating a shared lock - $handle = fopen($this->locksFile,'r'); - flock($handle,LOCK_SH); - - // Reading data until the eof - $data = stream_get_contents($handle); - - // We're all good - fclose($handle); - - // Unserializing and checking if the resource file contains data for this file - $data = unserialize($data); - if (!$data) return array(); - return $data; - - } - - /** - * Saves the lockdata - * - * @param array $newData - * @return void - */ - protected function putData(array $newData) { - - // opening up the file, and creating an exclusive lock - $handle = fopen($this->locksFile,'a+'); - flock($handle,LOCK_EX); - - // We can only truncate and rewind once the lock is acquired. - ftruncate($handle,0); - rewind($handle); - - fwrite($handle,serialize($newData)); - fclose($handle); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/PDO.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/PDO.php deleted file mode 100644 index 36c01d3e6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/Backend/PDO.php +++ /dev/null @@ -1,167 +0,0 @@ -pdo = $pdo; - $this->tableName = $tableName; - - } - - /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects - * - * This method should return all the locks for a particular uri, including - * locks that might be set on a parent uri. - * - * If returnChildLocks is set to true, this method should also look for - * any locks in the subtree of the uri for locks. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks) { - - // NOTE: the following 10 lines or so could be easily replaced by - // pure sql. MySQL's non-standard string concatenation prevents us - // from doing this though. - $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; - $params = array(time(),$uri); - - // We need to check locks for every part in the uri. - $uriParts = explode('/',$uri); - - // We already covered the last part of the uri - array_pop($uriParts); - - $currentPath=''; - - foreach($uriParts as $part) { - - if ($currentPath) $currentPath.='/'; - $currentPath.=$part; - - $query.=' OR (depth!=0 AND uri = ?)'; - $params[] = $currentPath; - - } - - if ($returnChildLocks) { - - $query.=' OR (uri LIKE ?)'; - $params[] = $uri . '/%'; - - } - $query.=')'; - - $stmt = $this->pdo->prepare($query); - $stmt->execute($params); - $result = $stmt->fetchAll(); - - $lockList = array(); - foreach($result as $row) { - - $lockInfo = new LockInfo(); - $lockInfo->owner = $row['owner']; - $lockInfo->token = $row['token']; - $lockInfo->timeout = $row['timeout']; - $lockInfo->created = $row['created']; - $lockInfo->scope = $row['scope']; - $lockInfo->depth = $row['depth']; - $lockInfo->uri = $row['uri']; - $lockList[] = $lockInfo; - - } - - return $lockList; - - } - - /** - * Locks a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lock($uri, LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 30*60; - $lockInfo->created = time(); - $lockInfo->uri = $uri; - - $locks = $this->getLocks($uri,false); - $exists = false; - foreach($locks as $lock) { - if ($lock->token == $lockInfo->token) $exists = true; - } - - if ($exists) { - $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); - $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); - } else { - $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); - $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); - } - - return true; - - } - - - - /** - * Removes a lock from a uri - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlock($uri, LockInfo $lockInfo) { - - $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?'); - $stmt->execute(array($uri,$lockInfo->token)); - - return $stmt->rowCount()===1; - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/LockInfo.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/LockInfo.php deleted file mode 100644 index ed3c02957..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Locks/LockInfo.php +++ /dev/null @@ -1,81 +0,0 @@ -addPlugin($lockPlugin); - * - * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). - * @author Evert Pot (http://evertpot.com/) - * @license http://sabre.io/license/ Modified BSD License - */ -class Plugin extends DAV\ServerPlugin { - - /** - * locksBackend - * - * @var Backend\Backend\Interface - */ - protected $locksBackend; - - /** - * server - * - * @var Sabre\DAV\Server - */ - protected $server; - - /** - * __construct - * - * @param Backend\BackendInterface $locksBackend - */ - public function __construct(Backend\BackendInterface $locksBackend = null) { - - $this->locksBackend = $locksBackend; - - } - - /** - * Initializes the plugin - * - * This method is automatically called by the Server class after addPlugin. - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); - $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50); - $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using Sabre\DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'locks'; - - } - - /** - * This method is called by the Server if the user used an HTTP method - * the server didn't recognize. - * - * This plugin intercepts the LOCK and UNLOCK methods. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function unknownMethod($method, $uri) { - - switch($method) { - - case 'LOCK' : $this->httpLock($uri); return false; - case 'UNLOCK' : $this->httpUnlock($uri); return false; - - } - - } - - /** - * This method is called after most properties have been found - * it allows us to add in any Lock-related properties - * - * @param string $path - * @param array $newProperties - * @return bool - */ - public function afterGetProperties($path, &$newProperties) { - - foreach($newProperties[404] as $propName=>$discard) { - - switch($propName) { - - case '{DAV:}supportedlock' : - $val = false; - if ($this->locksBackend) $val = true; - $newProperties[200][$propName] = new DAV\Property\SupportedLock($val); - unset($newProperties[404][$propName]); - break; - - case '{DAV:}lockdiscovery' : - $newProperties[200][$propName] = new DAV\Property\LockDiscovery($this->getLocks($path)); - unset($newProperties[404][$propName]); - break; - - } - - - } - return true; - - } - - - /** - * This method is called before the logic for any HTTP method is - * handled. - * - * This plugin uses that feature to intercept access to locked resources. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - switch($method) { - - case 'DELETE' : - $lastLock = null; - if (!$this->validateLock($uri,$lastLock, true)) - throw new DAV\Exception\Locked($lastLock); - break; - case 'MKCOL' : - case 'PROPPATCH' : - case 'PUT' : - case 'PATCH' : - $lastLock = null; - if (!$this->validateLock($uri,$lastLock)) - throw new DAV\Exception\Locked($lastLock); - break; - case 'MOVE' : - $lastLock = null; - if (!$this->validateLock(array( - $uri, - $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), - ),$lastLock, true)) - throw new DAV\Exception\Locked($lastLock); - break; - case 'COPY' : - $lastLock = null; - if (!$this->validateLock( - $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), - $lastLock, true)) - throw new DAV\Exception\Locked($lastLock); - break; - } - - return true; - - } - - /** - * Use this method to tell the server this plugin defines additional - * HTTP methods. - * - * This method is passed a uri. It should only return HTTP methods that are - * available for the specified uri. - * - * @param string $uri - * @return array - */ - public function getHTTPMethods($uri) { - - if ($this->locksBackend) - return array('LOCK','UNLOCK'); - - return array(); - - } - - /** - * Returns a list of features for the HTTP OPTIONS Dav: header. - * - * In this case this is only the number 2. The 2 in the Dav: header - * indicates the server supports locks. - * - * @return array - */ - public function getFeatures() { - - return array(2); - - } - - /** - * Returns all lock information on a particular uri - * - * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array. - * - * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree - * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object - * for any possible locks and return those as well. - * - * @param string $uri - * @param bool $returnChildLocks - * @return array - */ - public function getLocks($uri, $returnChildLocks = false) { - - $lockList = array(); - - if ($this->locksBackend) - $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks)); - - return $lockList; - - } - - /** - * Locks an uri - * - * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock - * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type - * of lock (shared or exclusive) and the owner of the lock - * - * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock - * - * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 - * - * @param string $uri - * @return void - */ - protected function httpLock($uri) { - - $lastLock = null; - if (!$this->validateLock($uri,$lastLock)) { - - // If the existing lock was an exclusive lock, we need to fail - if (!$lastLock || $lastLock->scope == LockInfo::EXCLUSIVE) { - //var_dump($lastLock); - throw new DAV\Exception\ConflictingLock($lastLock); - } - - } - - if ($body = $this->server->httpRequest->getBody(true)) { - // This is a new lock request - $lockInfo = $this->parseLockRequest($body); - $lockInfo->depth = $this->server->getHTTPDepth(); - $lockInfo->uri = $uri; - if($lastLock && $lockInfo->scope != LockInfo::SHARED) throw new DAV\Exception\ConflictingLock($lastLock); - - } elseif ($lastLock) { - - // This must have been a lock refresh - $lockInfo = $lastLock; - - // The resource could have been locked through another uri. - if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; - - } else { - - // There was neither a lock refresh nor a new lock request - throw new DAV\Exception\BadRequest('An xml body is required for lock requests'); - - } - - if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; - - $newFile = false; - - // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first - try { - $this->server->tree->getNodeForPath($uri); - - // We need to call the beforeWriteContent event for RFC3744 - // Edit: looks like this is not used, and causing problems now. - // - // See Issue 222 - // $this->server->broadcastEvent('beforeWriteContent',array($uri)); - - } catch (DAV\Exception\NotFound $e) { - - // It didn't, lets create it - $this->server->createFile($uri,fopen('php://memory','r')); - $newFile = true; - - } - - $this->lockNode($uri,$lockInfo); - - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Lock-Token','token . '>'); - $this->server->httpResponse->sendStatus($newFile?201:200); - $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo)); - - } - - /** - * Unlocks a uri - * - * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header - * The server should return 204 (No content) on success - * - * @param string $uri - * @return void - */ - protected function httpUnlock($uri) { - - $lockToken = $this->server->httpRequest->getHeader('Lock-Token'); - - // If the locktoken header is not supplied, we need to throw a bad request exception - if (!$lockToken) throw new DAV\Exception\BadRequest('No lock token was supplied'); - - $locks = $this->getLocks($uri); - - // Windows sometimes forgets to include < and > in the Lock-Token - // header - if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>'; - - foreach($locks as $lock) { - - if ('token . '>' == $lockToken) { - - $this->unlockNode($uri,$lock); - $this->server->httpResponse->setHeader('Content-Length','0'); - $this->server->httpResponse->sendStatus(204); - return; - - } - - } - - // If we got here, it means the locktoken was invalid - throw new DAV\Exception\LockTokenMatchesRequestUri(); - - } - - /** - * Locks a uri - * - * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored - * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function lockNode($uri,LockInfo $lockInfo) { - - if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return; - - if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); - throw new DAV\Exception\MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); - - } - - /** - * Unlocks a uri - * - * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified - * - * @param string $uri - * @param LockInfo $lockInfo - * @return bool - */ - public function unlockNode($uri, LockInfo $lockInfo) { - - if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return; - if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); - - } - - - /** - * Returns the contents of the HTTP Timeout header. - * - * The method formats the header into an integer. - * - * @return int - */ - public function getTimeoutHeader() { - - $header = $this->server->httpRequest->getHeader('Timeout'); - - if ($header) { - - if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); - else if (strtolower($header)=='infinite') $header = LockInfo::TIMEOUT_INFINITE; - else throw new DAV\Exception\BadRequest('Invalid HTTP timeout header'); - - } else { - - $header = 0; - - } - - return $header; - - } - - /** - * Generates the response for successful LOCK requests - * - * @param LockInfo $lockInfo - * @return string - */ - protected function generateLockResponse(LockInfo $lockInfo) { - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - - $prop = $dom->createElementNS('DAV:','d:prop'); - $dom->appendChild($prop); - - $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery'); - $prop->appendChild($lockDiscovery); - - $lockObj = new DAV\Property\LockDiscovery(array($lockInfo),true); - $lockObj->serialize($this->server,$lockDiscovery); - - return $dom->saveXML(); - - } - - /** - * validateLock should be called when a write operation is about to happen - * It will check if the requested url is locked, and see if the correct lock tokens are passed - * - * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri - * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre\DAV\Locks\LockInfo) - * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees. - * @return bool - */ - protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) { - - if (is_null($urls)) { - $urls = array($this->server->getRequestUri()); - } elseif (is_string($urls)) { - $urls = array($urls); - } elseif (!is_array($urls)) { - throw new DAV\Exception('The urls parameter should either be null, a string or an array'); - } - - $conditions = $this->getIfConditions(); - - // We're going to loop through the urls and make sure all lock conditions are satisfied - foreach($urls as $url) { - - $locks = $this->getLocks($url, $checkChildLocks); - - // If there were no conditions, but there were locks, we fail - if (!$conditions && $locks) { - reset($locks); - $lastLock = current($locks); - return false; - } - - // If there were no locks or conditions, we go to the next url - if (!$locks && !$conditions) continue; - - foreach($conditions as $condition) { - - if (!$condition['uri']) { - $conditionUri = $this->server->getRequestUri(); - } else { - $conditionUri = $this->server->calculateUri($condition['uri']); - } - - // If the condition has a url, and it isn't part of the affected url at all, check the next condition - if ($conditionUri && strpos($url,$conditionUri)!==0) continue; - - // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken - // At least 1 condition has to be satisfied - foreach($condition['tokens'] as $conditionToken) { - - $etagValid = true; - $lockValid = true; - - // key 2 can contain an etag - if ($conditionToken[2]) { - - $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); - $node = $this->server->tree->getNodeForPath($uri); - $etagValid = $node instanceof DAV\IFile && $node->getETag()==$conditionToken[2]; - - } - - // key 1 can contain a lock token - if ($conditionToken[1]) { - - $lockValid = false; - // Match all the locks - foreach($locks as $lockIndex=>$lock) { - - $lockToken = 'opaquelocktoken:' . $lock->token; - - // Checking NOT - if (!$conditionToken[0] && $lockToken != $conditionToken[1]) { - - // Condition valid, onto the next - $lockValid = true; - break; - } - if ($conditionToken[0] && $lockToken == $conditionToken[1]) { - - $lastLock = $lock; - // Condition valid and lock matched - unset($locks[$lockIndex]); - $lockValid = true; - break; - - } - - } - - } - - // If, after checking both etags and locks they are stil valid, - // we can continue with the next condition. - if ($etagValid && $lockValid) continue 2; - } - // No conditions matched, so we fail - throw new DAV\Exception\PreconditionFailed('The tokens provided in the if header did not match','If'); - } - - // Conditions were met, we'll also need to check if all the locks are gone - if (count($locks)) { - - reset($locks); - - // There's still locks, we fail - $lastLock = current($locks); - return false; - - } - - - } - - // We got here, this means every condition was satisfied - return true; - - } - - /** - * This method is created to extract information from the WebDAV HTTP 'If:' header - * - * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information - * The function will return an array, containing structs with the following keys - * - * * uri - the uri the condition applies to. If this is returned as an - * empty string, this implies it's referring to the request url. - * * tokens - The lock token. another 2 dimensional array containing 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) - * * etag - an etag, if supplied - * - * @return array - */ - public function getIfConditions() { - - $header = $this->server->httpRequest->getHeader('If'); - if (!$header) return array(); - - $matches = array(); - - $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; - preg_match_all($regex,$header,$matches,PREG_SET_ORDER); - - $conditions = array(); - - foreach($matches as $match) { - - $condition = array( - 'uri' => $match['uri'], - 'tokens' => array( - array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'') - ), - ); - - if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array( - $match['not']?0:1, - $match['token'], - isset($match['etag'])?$match['etag']:'' - ); - else { - $conditions[] = $condition; - } - - } - - return $conditions; - - } - - /** - * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object - * - * @param string $body - * @return DAV\Locks\LockInfo - */ - protected function parseLockRequest($body) { - - // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or - // 5.4.13. - $previous = libxml_disable_entity_loader(true); - - - $xml = simplexml_load_string( - DAV\XMLUtil::convertDAVNamespace($body), - null, - LIBXML_NOWARNING); - libxml_disable_entity_loader($previous); - - $xml->registerXPathNamespace('d','urn:DAV'); - $lockInfo = new LockInfo(); - - $children = $xml->children("urn:DAV"); - $lockInfo->owner = (string)$children->owner; - - $lockInfo->token = DAV\UUIDUtil::getUUID(); - $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0 ? LockInfo::EXCLUSIVE : LockInfo::SHARED; - - return $lockInfo; - - } - - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php deleted file mode 100644 index 71cbe6e29..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Mount/Plugin.php +++ /dev/null @@ -1,83 +0,0 @@ -server = $server; - $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); - - } - - /** - * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?mount - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if ($method!='GET') return; - if ($this->server->httpRequest->getQueryString()!='mount') return; - - $currentUri = $this->server->httpRequest->getAbsoluteUri(); - - // Stripping off everything after the ? - list($currentUri) = explode('?',$currentUri); - - $this->davMount($currentUri); - - // Returning false to break the event chain - return false; - - } - - /** - * Generates the davmount response - * - * @param string $uri absolute uri - * @return void - */ - public function davMount($uri) { - - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml'); - ob_start(); - echo '', "\n"; - echo "\n"; - echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; - echo ""; - $this->server->httpResponse->sendBody(ob_get_clean()); - - } - - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Node.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Node.php deleted file mode 100644 index 2a89856ac..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Node.php +++ /dev/null @@ -1,55 +0,0 @@ -rootNode = $rootNode; - - } - - /** - * Returns the INode object for the requested path - * - * @param string $path - * @return INode - */ - public function getNodeForPath($path) { - - $path = trim($path,'/'); - if (isset($this->cache[$path])) return $this->cache[$path]; - - // Is it the root node? - if (!strlen($path)) { - return $this->rootNode; - } - - // Attempting to fetch its parent - list($parentName, $baseName) = URLUtil::splitPath($path); - - // If there was no parent, we must simply ask it from the root node. - if ($parentName==="") { - $node = $this->rootNode->getChild($baseName); - } else { - // Otherwise, we recursively grab the parent and ask him/her. - $parent = $this->getNodeForPath($parentName); - - if (!($parent instanceof ICollection)) - throw new Exception\NotFound('Could not find node at path: ' . $path); - - $node = $parent->getChild($baseName); - - } - - $this->cache[$path] = $node; - return $node; - - } - - /** - * This function allows you to check if a node exists. - * - * @param string $path - * @return bool - */ - public function nodeExists($path) { - - try { - - // The root always exists - if ($path==='') return true; - - list($parent, $base) = URLUtil::splitPath($path); - - $parentNode = $this->getNodeForPath($parent); - if (!$parentNode instanceof ICollection) return false; - return $parentNode->childExists($base); - - } catch (Exception\NotFound $e) { - - return false; - - } - - } - - /** - * Returns a list of childnodes for a given path. - * - * @param string $path - * @return array - */ - public function getChildren($path) { - - $node = $this->getNodeForPath($path); - $children = $node->getChildren(); - foreach($children as $child) { - - $this->cache[trim($path,'/') . '/' . $child->getName()] = $child; - - } - return $children; - - } - - /** - * This method is called with every tree update - * - * Examples of tree updates are: - * * node deletions - * * node creations - * * copy - * * move - * * renaming nodes - * - * If Tree classes implement a form of caching, this will allow - * them to make sure caches will be expired. - * - * If a path is passed, it is assumed that the entire subtree is dirty - * - * @param string $path - * @return void - */ - public function markDirty($path) { - - // We don't care enough about sub-paths - // flushing the entire cache - $path = trim($path,'/'); - foreach($this->cache as $nodePath=>$node) { - if ($nodePath == $path || strpos($nodePath,$path.'/')===0) - unset($this->cache[$nodePath]); - - } - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/IFile.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/IFile.php deleted file mode 100644 index 18db8495f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/PartialUpdate/IFile.php +++ /dev/null @@ -1,39 +0,0 @@ -addPlugin($patchPlugin); - * - * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). - * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) - * @license http://sabre.io/license/ Modified BSD License - */ -class Plugin extends DAV\ServerPlugin { - - const RANGE_APPEND = 1; - const RANGE_START = 2; - const RANGE_END = 3; - - /** - * Reference to server - * - * @var Sabre\DAV\Server - */ - protected $server; - - /** - * Initializes the plugin - * - * This method is automatically called by the Server class after addPlugin. - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'partialupdate'; - - } - - /** - * This method is called by the Server if the user used an HTTP method - * the server didn't recognize. - * - * This plugin intercepts the PATCH methods. - * - * @param string $method - * @param string $uri - * @return bool|null - */ - public function unknownMethod($method, $uri) { - - switch($method) { - - case 'PATCH': - return $this->httpPatch($uri); - - } - - } - - /** - * Use this method to tell the server this plugin defines additional - * HTTP methods. - * - * This method is passed a uri. It should only return HTTP methods that are - * available for the specified uri. - * - * We claim to support PATCH method (partial update) if and only if - * - the node exist - * - the node implements our partial update interface - * - * @param string $uri - * @return array - */ - public function getHTTPMethods($uri) { - - $tree = $this->server->tree; - if ($tree->nodeExists($uri)) { - $node = $tree->getNodeForPath($uri); - if ($node instanceof IFile || $node instanceof IPatchSupport) { - return array('PATCH'); - } - } - return array(); - - } - - /** - * Returns a list of features for the HTTP OPTIONS Dav: header. - * - * @return array - */ - public function getFeatures() { - - return array('sabredav-partialupdate'); - - } - - /** - * Patch an uri - * - * The WebDAV patch request can be used to modify only a part of an - * existing resource. If the resource does not exist yet and the first - * offset is not 0, the request fails - * - * @param string $uri - * @return void - */ - protected function httpPatch($uri) { - - // Get the node. Will throw a 404 if not found - $node = $this->server->tree->getNodeForPath($uri); - if (!$node instanceof IFile && !$node instanceof IPatchSupport) { - throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); - } - - $range = $this->getHTTPUpdateRange(); - - if (!$range) { - throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); - } - - $contentType = strtolower( - $this->server->httpRequest->getHeader('Content-Type') - ); - - if ($contentType != 'application/x-sabredav-partialupdate') { - throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); - } - - $len = $this->server->httpRequest->getHeader('Content-Length'); - if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required'); - - switch($range[0]) { - case self::RANGE_START : - // Calculate the end-range if it doesn't exist. - if (!$range[2]) { - $range[2] = $range[1] + $len - 1; - } else { - if ($range[2] < $range[1]) { - throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); - } - if($range[2] - $range[1] + 1 != $len) { - throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); - } - } - break; - } - // Checking If-None-Match and related headers. - if (!$this->server->checkPreconditions()) return; - - if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null))) - return; - - $body = $this->server->httpRequest->getBody(); - - - if ($node instanceof IPatchSupport) { - $etag = $node->patch($body, $range[0], isset($range[1])?$range[1]:null); - } else { - // The old interface - switch($range[0]) { - case self::RANGE_APPEND : - throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport'); - case self::RANGE_START : - $etag = $node->putRange($body, $range[1]); - break; - case self::RANGE_END : - throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport'); - break; - } - } - - $this->server->broadcastEvent('afterWriteContent',array($uri, $node)); - - $this->server->httpResponse->setHeader('Content-Length','0'); - if ($etag) $this->server->httpResponse->setHeader('ETag',$etag); - $this->server->httpResponse->sendStatus(204); - - return false; - - } - - /** - * Returns the HTTP custom range update header - * - * This method returns null if there is no well-formed HTTP range request - * header. It returns array(1) if it was an append request, array(2, - * $start, $end) if it's a start and end range, lastly it's array(3, - * $endoffset) if the offset was negative, and should be calculated from - * the end of the file. - * - * Examples: - * - * null - invalid - * array(1) - append - * array(2,10,15) - update bytes 10, 11, 12, 13, 14, 15 - * array(2,10,null) - update bytes 10 until the end of the patch body - * array(3,-5) - update from 5 bytes from the end of the file. - * - * @return array|null - */ - public function getHTTPUpdateRange() { - - $range = $this->server->httpRequest->getHeader('X-Update-Range'); - if (is_null($range)) return null; - - // Matching "Range: bytes=1234-5678: both numbers are optional - - if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null; - - if ($matches[1]==='append') { - return array(self::RANGE_APPEND); - } elseif (strlen($matches[2])>0) { - return array(self::RANGE_START, $matches[2], $matches[3]?:null); - } elseif ($matches[4]) { - return array(self::RANGE_END, $matches[4]); - } else { - return null; - } - - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php deleted file mode 100644 index b2d5eb247..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property.php +++ /dev/null @@ -1,31 +0,0 @@ -time = $time; - } elseif (is_int($time) || ctype_digit($time)) { - $this->time = new \DateTime('@' . $time); - } else { - $this->time = new \DateTime($time); - } - - // Setting timezone to UTC - $this->time->setTimezone(new \DateTimeZone('UTC')); - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - $doc = $prop->ownerDocument; - //$prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); - //$prop->setAttribute('b:dt','dateTime.rfc1123'); - $prop->nodeValue = HTTP\Util::toHTTPDate($this->time); - - } - - /** - * getTime - * - * @return \DateTime - */ - public function getTime() { - - return $this->time; - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Href.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Href.php deleted file mode 100644 index 8f8147ba8..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Href.php +++ /dev/null @@ -1,99 +0,0 @@ -href = $href; - $this->autoPrefix = $autoPrefix; - - } - - /** - * Returns the uri - * - * @return string - */ - public function getHref() { - - return $this->href; - - } - - /** - * Serializes this property. - * - * It will additionally prepend the href property with the server's base uri. - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $dom) { - - $prefix = $server->xmlNamespaces['DAV:']; - $elem = $dom->ownerDocument->createElement($prefix . ':href'); - - if ($this->autoPrefix) { - $value = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href); - } else { - $value = $this->href; - } - $elem->appendChild($dom->ownerDocument->createTextNode($value)); - - $dom->appendChild($elem); - - } - - /** - * Unserializes this property from a DOM Element - * - * This method returns an instance of this class. - * It will only decode {DAV:}href values. For non-compatible elements null will be returned. - * - * @param \DOMElement $dom - * @return DAV\Property\Href - */ - static function unserialize(\DOMElement $dom) { - - if ($dom->firstChild && DAV\XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { - return new self($dom->firstChild->textContent,false); - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/HrefList.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/HrefList.php deleted file mode 100644 index ba8d9e46f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/HrefList.php +++ /dev/null @@ -1,105 +0,0 @@ -hrefs = $hrefs; - $this->autoPrefix = $autoPrefix; - - } - - /** - * Returns the uris - * - * @return array - */ - public function getHrefs() { - - return $this->hrefs; - - } - - /** - * Serializes this property. - * - * It will additionally prepend the href property with the server's base uri. - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $dom) { - - $prefix = $server->xmlNamespaces['DAV:']; - - foreach($this->hrefs as $href) { - - $elem = $dom->ownerDocument->createElement($prefix . ':href'); - if ($this->autoPrefix) { - $value = $server->getBaseUri() . DAV\URLUtil::encodePath($href); - } else { - $value = $href; - } - $elem->appendChild($dom->ownerDocument->createTextNode($value)); - - $dom->appendChild($elem); - } - - } - - /** - * Unserializes this property from a DOM Element - * - * This method returns an instance of this class. - * It will only decode {DAV:}href values. - * - * @param \DOMElement $dom - * @return DAV\Property\HrefList - */ - static function unserialize(\DOMElement $dom) { - - $hrefs = array(); - foreach($dom->childNodes as $child) { - if (DAV\XMLUtil::toClarkNotation($child)==='{DAV:}href') { - $hrefs[] = $child->textContent; - } - } - return new self($hrefs, false); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/IHref.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/IHref.php deleted file mode 100644 index 00a7c98a2..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/IHref.php +++ /dev/null @@ -1,25 +0,0 @@ -locks = $locks; - $this->revealLockToken = $revealLockToken; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - $doc = $prop->ownerDocument; - - foreach($this->locks as $lock) { - - $activeLock = $doc->createElementNS('DAV:','d:activelock'); - $prop->appendChild($activeLock); - - $lockScope = $doc->createElementNS('DAV:','d:lockscope'); - $activeLock->appendChild($lockScope); - - $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==DAV\Locks\LockInfo::EXCLUSIVE?'exclusive':'shared'))); - - $lockType = $doc->createElementNS('DAV:','d:locktype'); - $activeLock->appendChild($lockType); - - $lockType->appendChild($doc->createElementNS('DAV:','d:write')); - - /* {DAV:}lockroot */ - if (!self::$hideLockRoot) { - $lockRoot = $doc->createElementNS('DAV:','d:lockroot'); - $activeLock->appendChild($lockRoot); - $href = $doc->createElementNS('DAV:','d:href'); - $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri)); - $lockRoot->appendChild($href); - } - - $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == DAV\Server::DEPTH_INFINITY?'infinity':$lock->depth))); - $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout)); - - if ($this->revealLockToken) { - $lockToken = $doc->createElementNS('DAV:','d:locktoken'); - $activeLock->appendChild($lockToken); - $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); - } - - $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); - - } - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResourceType.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResourceType.php deleted file mode 100644 index 5bcf6e5d4..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResourceType.php +++ /dev/null @@ -1,127 +0,0 @@ -resourceType = array(); - elseif ($resourceType === DAV\Server::NODE_DIRECTORY) - $this->resourceType = array('{DAV:}collection'); - elseif (is_array($resourceType)) - $this->resourceType = $resourceType; - else - $this->resourceType = array($resourceType); - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - $propName = null; - $rt = $this->resourceType; - - foreach($rt as $resourceType) { - if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { - - if (isset($server->xmlNamespaces[$propName[1]])) { - $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); - } else { - $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); - } - - } - } - - } - - /** - * Returns the values in clark-notation - * - * For example array('{DAV:}collection') - * - * @return array - */ - public function getValue() { - - return $this->resourceType; - - } - - /** - * Checks if the principal contains a certain value - * - * @param string $type - * @return bool - */ - public function is($type) { - - return in_array($type, $this->resourceType); - - } - - /** - * Adds a resourcetype value to this property - * - * @param string $type - * @return void - */ - public function add($type) { - - $this->resourceType[] = $type; - $this->resourceType = array_unique($this->resourceType); - - } - - /** - * Unserializes a DOM element into a ResourceType property. - * - * @param \DOMElement $dom - * @return DAV\Property\ResourceType - */ - static public function unserialize(\DOMElement $dom) { - - $value = array(); - foreach($dom->childNodes as $child) { - - $value[] = DAV\XMLUtil::toClarkNotation($child); - - } - - return new self($value); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Response.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Response.php deleted file mode 100644 index 973413a49..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/Response.php +++ /dev/null @@ -1,157 +0,0 @@ -href = $href; - $this->responseProperties = $responseProperties; - - } - - /** - * Returns the url - * - * @return string - */ - public function getHref() { - - return $this->href; - - } - - /** - * Returns the property list - * - * @return array - */ - public function getResponseProperties() { - - return $this->responseProperties; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $dom) { - - $document = $dom->ownerDocument; - $properties = $this->responseProperties; - - $xresponse = $document->createElement('d:response'); - $dom->appendChild($xresponse); - - $uri = DAV\URLUtil::encodePath($this->href); - - // Adding the baseurl to the beginning of the url - $uri = $server->getBaseUri() . $uri; - - $xresponse->appendChild($document->createElement('d:href',$uri)); - - // The properties variable is an array containing properties, grouped by - // HTTP status - foreach($properties as $httpStatus=>$propertyGroup) { - - // The 'href' is also in this array, and it's special cased. - // We will ignore it - if ($httpStatus=='href') continue; - - // If there are no properties in this group, we can also just carry on - if (!count($propertyGroup)) continue; - - $xpropstat = $document->createElement('d:propstat'); - $xresponse->appendChild($xpropstat); - - $xprop = $document->createElement('d:prop'); - $xpropstat->appendChild($xprop); - - $nsList = $server->xmlNamespaces; - - foreach($propertyGroup as $propertyName=>$propertyValue) { - - $propName = null; - preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); - - // special case for empty namespaces - if ($propName[1]=='') { - - $currentProperty = $document->createElement($propName[2]); - $xprop->appendChild($currentProperty); - $currentProperty->setAttribute('xmlns',''); - - } else { - - if (!isset($nsList[$propName[1]])) { - $nsList[$propName[1]] = 'x' . count($nsList); - } - - // If the namespace was defined in the top-level xml namespaces, it means - // there was already a namespace declaration, and we don't have to worry about it. - if (isset($server->xmlNamespaces[$propName[1]])) { - $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); - } else { - $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); - } - $xprop->appendChild($currentProperty); - - } - - if (is_scalar($propertyValue)) { - $text = $document->createTextNode($propertyValue); - $currentProperty->appendChild($text); - } elseif ($propertyValue instanceof DAV\PropertyInterface) { - $propertyValue->serialize($server,$currentProperty); - } elseif (!is_null($propertyValue)) { - throw new DAV\Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); - } - - } - - $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus))); - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResponseList.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResponseList.php deleted file mode 100644 index 620c16ce8..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/ResponseList.php +++ /dev/null @@ -1,59 +0,0 @@ -responses = $responses; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $dom) { - - foreach($this->responses as $response) { - $response->serialize($server, $dom); - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php deleted file mode 100644 index 3898ecee6..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedLock.php +++ /dev/null @@ -1,78 +0,0 @@ -supportsLocks = $supportsLocks; - - } - - /** - * serialize - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $prop) { - - $doc = $prop->ownerDocument; - - if (!$this->supportsLocks) return null; - - $lockEntry1 = $doc->createElement('d:lockentry'); - $lockEntry2 = $doc->createElement('d:lockentry'); - - $prop->appendChild($lockEntry1); - $prop->appendChild($lockEntry2); - - $lockScope1 = $doc->createElement('d:lockscope'); - $lockScope2 = $doc->createElement('d:lockscope'); - $lockType1 = $doc->createElement('d:locktype'); - $lockType2 = $doc->createElement('d:locktype'); - - $lockEntry1->appendChild($lockScope1); - $lockEntry1->appendChild($lockType1); - $lockEntry2->appendChild($lockScope2); - $lockEntry2->appendChild($lockType2); - - $lockScope1->appendChild($doc->createElement('d:exclusive')); - $lockScope2->appendChild($doc->createElement('d:shared')); - - $lockType1->appendChild($doc->createElement('d:write')); - $lockType2->appendChild($doc->createElement('d:write')); - - //$frag->appendXML(''); - //$frag->appendXML(''); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedReportSet.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedReportSet.php deleted file mode 100644 index 2df280260..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Property/SupportedReportSet.php +++ /dev/null @@ -1,111 +0,0 @@ -addReport($reports); - - } - - /** - * Adds a report to this property - * - * The report must be a string in clark-notation. - * Multiple reports can be specified as an array. - * - * @param mixed $report - * @return void - */ - public function addReport($report) { - - if (!is_array($report)) $report = array($report); - - foreach($report as $r) { - - if (!preg_match('/^{([^}]*)}(.*)$/',$r)) - throw new DAV\Exception('Reportname must be in clark-notation'); - - $this->reports[] = $r; - - } - - } - - /** - * Returns the list of supported reports - * - * @return array - */ - public function getValue() { - - return $this->reports; - - } - - /** - * Serializes the node - * - * @param DAV\Server $server - * @param \DOMElement $prop - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $prop) { - - foreach($this->reports as $reportName) { - - $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); - $prop->appendChild($supportedReport); - - $report = $prop->ownerDocument->createElement('d:report'); - $supportedReport->appendChild($report); - - preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); - - list(, $namespace, $element) = $matches; - - $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; - - if ($prefix) { - $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element)); - } else { - $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element)); - } - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/PropertyInterface.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/PropertyInterface.php deleted file mode 100644 index 31fa2f29c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/PropertyInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - 'd', - 'http://sabredav.org/ns' => 's', - ); - - /** - * The propertymap can be used to map properties from - * requests to property classes. - * - * @var array - */ - public $propertyMap = array( - '{DAV:}resourcetype' => 'Sabre\\DAV\\Property\\ResourceType', - ); - - public $protectedProperties = array( - // RFC4918 - '{DAV:}getcontentlength', - '{DAV:}getetag', - '{DAV:}getlastmodified', - '{DAV:}lockdiscovery', - '{DAV:}supportedlock', - - // RFC4331 - '{DAV:}quota-available-bytes', - '{DAV:}quota-used-bytes', - - // RFC3744 - '{DAV:}supported-privilege-set', - '{DAV:}current-user-privilege-set', - '{DAV:}acl', - '{DAV:}acl-restrictions', - '{DAV:}inherited-acl-set', - - ); - - /** - * This is a flag that allow or not showing file, line and code - * of the exception in the returned XML - * - * @var bool - */ - public $debugExceptions = false; - - /** - * This property allows you to automatically add the 'resourcetype' value - * based on a node's classname or interface. - * - * The preset ensures that {DAV:}collection is automaticlly added for nodes - * implementing Sabre\DAV\ICollection. - * - * @var array - */ - public $resourceTypeMapping = array( - 'Sabre\\DAV\\ICollection' => '{DAV:}collection', - ); - - /** - * If this setting is turned off, SabreDAV's version number will be hidden - * from various places. - * - * Some people feel this is a good security measure. - * - * @var bool - */ - static public $exposeVersion = true; - - /** - * Sets up the server - * - * If a Sabre\DAV\Tree object is passed as an argument, it will - * use it as the directory tree. If a Sabre\DAV\INode is passed, it - * will create a Sabre\DAV\ObjectTree and use the node as the root. - * - * If nothing is passed, a Sabre\DAV\SimpleCollection is created in - * a Sabre\DAV\ObjectTree. - * - * If an array is passed, we automatically create a root node, and use - * the nodes in the array as top-level children. - * - * @param Tree|INode|array|null $treeOrNode The tree object - */ - public function __construct($treeOrNode = null) { - - if ($treeOrNode instanceof Tree) { - $this->tree = $treeOrNode; - } elseif ($treeOrNode instanceof INode) { - $this->tree = new ObjectTree($treeOrNode); - } elseif (is_array($treeOrNode)) { - - // If it's an array, a list of nodes was passed, and we need to - // create the root node. - foreach($treeOrNode as $node) { - if (!($node instanceof INode)) { - throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre\\DAV\\INode'); - } - } - - $root = new SimpleCollection('root', $treeOrNode); - $this->tree = new ObjectTree($root); - - } elseif (is_null($treeOrNode)) { - $root = new SimpleCollection('root'); - $this->tree = new ObjectTree($root); - } else { - throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null'); - } - $this->httpResponse = new HTTP\Response(); - $this->httpRequest = new HTTP\Request(); - - } - - /** - * Starts the DAV Server - * - * @return void - */ - public function exec() { - - try { - - // If nginx (pre-1.2) is used as a proxy server, and SabreDAV as an - // origin, we must make sure we send back HTTP/1.0 if this was - // requested. - // This is mainly because nginx doesn't support Chunked Transfer - // Encoding, and this forces the webserver SabreDAV is running on, - // to buffer entire responses to calculate Content-Length. - $this->httpResponse->defaultHttpVersion = $this->httpRequest->getHTTPVersion(); - - $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri()); - - } catch (Exception $e) { - - try { - $this->broadcastEvent('exception', array($e)); - } catch (Exception $ignore) { - } - $DOM = new \DOMDocument('1.0','utf-8'); - $DOM->formatOutput = true; - - $error = $DOM->createElementNS('DAV:','d:error'); - $error->setAttribute('xmlns:s',self::NS_SABREDAV); - $DOM->appendChild($error); - - $h = function($v) { - - return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8'); - - }; - - $error->appendChild($DOM->createElement('s:exception',$h(get_class($e)))); - $error->appendChild($DOM->createElement('s:message',$h($e->getMessage()))); - if ($this->debugExceptions) { - $error->appendChild($DOM->createElement('s:file',$h($e->getFile()))); - $error->appendChild($DOM->createElement('s:line',$h($e->getLine()))); - $error->appendChild($DOM->createElement('s:code',$h($e->getCode()))); - $error->appendChild($DOM->createElement('s:stacktrace',$h($e->getTraceAsString()))); - - } - if (self::$exposeVersion) { - $error->appendChild($DOM->createElement('s:sabredav-version',$h(Version::VERSION))); - } - - if($e instanceof Exception) { - - $httpCode = $e->getHTTPCode(); - $e->serialize($this,$error); - $headers = $e->getHTTPHeaders($this); - - } else { - - $httpCode = 500; - $headers = array(); - - } - $headers['Content-Type'] = 'application/xml; charset=utf-8'; - - $this->httpResponse->sendStatus($httpCode); - $this->httpResponse->setHeaders($headers); - $this->httpResponse->sendBody($DOM->saveXML()); - - } - - } - - /** - * Sets the base server uri - * - * @param string $uri - * @return void - */ - public function setBaseUri($uri) { - - // If the baseUri does not end with a slash, we must add it - if ($uri[strlen($uri)-1]!=='/') - $uri.='/'; - - $this->baseUri = $uri; - - } - - /** - * Returns the base responding uri - * - * @return string - */ - public function getBaseUri() { - - if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); - return $this->baseUri; - - } - - /** - * This method attempts to detect the base uri. - * Only the PATH_INFO variable is considered. - * - * If this variable is not set, the root (/) is assumed. - * - * @return string - */ - public function guessBaseUri() { - - $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); - $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); - - // If PATH_INFO is found, we can assume it's accurate. - if (!empty($pathInfo)) { - - // We need to make sure we ignore the QUERY_STRING part - if ($pos = strpos($uri,'?')) - $uri = substr($uri,0,$pos); - - // PATH_INFO is only set for urls, such as: /example.php/path - // in that case PATH_INFO contains '/path'. - // Note that REQUEST_URI is percent encoded, while PATH_INFO is - // not, Therefore they are only comparable if we first decode - // REQUEST_INFO as well. - $decodedUri = URLUtil::decodePath($uri); - - // A simple sanity check: - if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) { - $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo)); - return rtrim($baseUri,'/') . '/'; - } - - throw new Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); - - } - - // The last fallback is that we're just going to assume the server root. - return '/'; - - } - - /** - * Adds a plugin to the server - * - * For more information, console the documentation of Sabre\DAV\ServerPlugin - * - * @param ServerPlugin $plugin - * @return void - */ - public function addPlugin(ServerPlugin $plugin) { - - $this->plugins[$plugin->getPluginName()] = $plugin; - $plugin->initialize($this); - - } - - /** - * Returns an initialized plugin by it's name. - * - * This function returns null if the plugin was not found. - * - * @param string $name - * @return ServerPlugin - */ - public function getPlugin($name) { - - if (isset($this->plugins[$name])) - return $this->plugins[$name]; - - // This is a fallback and deprecated. - foreach($this->plugins as $plugin) { - if (get_class($plugin)===$name) return $plugin; - } - - return null; - - } - - /** - * Returns all plugins - * - * @return array - */ - public function getPlugins() { - - return $this->plugins; - - } - - - /** - * Subscribe to an event. - * - * When the event is triggered, we'll call all the specified callbacks. - * It is possible to control the order of the callbacks through the - * priority argument. - * - * This is for example used to make sure that the authentication plugin - * is triggered before anything else. If it's not needed to change this - * number, it is recommended to ommit. - * - * @param string $event - * @param callback $callback - * @param int $priority - * @return void - */ - public function subscribeEvent($event, $callback, $priority = 100) { - - if (!isset($this->eventSubscriptions[$event])) { - $this->eventSubscriptions[$event] = array(); - } - while(isset($this->eventSubscriptions[$event][$priority])) $priority++; - $this->eventSubscriptions[$event][$priority] = $callback; - ksort($this->eventSubscriptions[$event]); - - } - - /** - * Broadcasts an event - * - * This method will call all subscribers. If one of the subscribers returns false, the process stops. - * - * The arguments parameter will be sent to all subscribers - * - * @param string $eventName - * @param array $arguments - * @return bool - */ - public function broadcastEvent($eventName,$arguments = array()) { - - if (isset($this->eventSubscriptions[$eventName])) { - - foreach($this->eventSubscriptions[$eventName] as $subscriber) { - - $result = call_user_func_array($subscriber,$arguments); - if ($result===false) return false; - - } - - } - - return true; - - } - - /** - * Handles a http request, and execute a method based on its name - * - * @param string $method - * @param string $uri - * @return void - */ - public function invokeMethod($method, $uri) { - - $method = strtoupper($method); - - if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return; - - // Make sure this is a HTTP method we support - $internalMethods = array( - 'OPTIONS', - 'GET', - 'HEAD', - 'DELETE', - 'PROPFIND', - 'MKCOL', - 'PUT', - 'PROPPATCH', - 'COPY', - 'MOVE', - 'REPORT' - ); - - if (in_array($method,$internalMethods)) { - - call_user_func(array($this,'http' . $method), $uri); - - } else { - - if ($this->broadcastEvent('unknownMethod',array($method, $uri))) { - // Unsupported method - throw new Exception\NotImplemented('There was no handler found for this "' . $method . '" method'); - } - - } - - } - - // {{{ HTTP Method implementations - - /** - * HTTP OPTIONS - * - * @param string $uri - * @return void - */ - protected function httpOptions($uri) { - - $methods = $this->getAllowedMethods($uri); - - $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods))); - $features = array('1','3', 'extended-mkcol'); - - foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); - - $this->httpResponse->setHeader('DAV',implode(', ',$features)); - $this->httpResponse->setHeader('MS-Author-Via','DAV'); - $this->httpResponse->setHeader('Accept-Ranges','bytes'); - if (self::$exposeVersion) { - $this->httpResponse->setHeader('X-Sabre-Version',Version::VERSION); - } - $this->httpResponse->setHeader('Content-Length',0); - $this->httpResponse->sendStatus(200); - - } - - /** - * HTTP GET - * - * This method simply fetches the contents of a uri, like normal - * - * @param string $uri - * @return bool - */ - protected function httpGet($uri) { - - $node = $this->tree->getNodeForPath($uri,0); - - if (!$this->checkPreconditions(true)) return false; - if (!$node instanceof IFile) throw new Exception\NotImplemented('GET is only implemented on File objects'); - - $body = $node->get(); - - // Converting string into stream, if needed. - if (is_string($body)) { - $stream = fopen('php://temp','r+'); - fwrite($stream,$body); - rewind($stream); - $body = $stream; - } - - /* - * TODO: getetag, getlastmodified, getsize should also be used using - * this method - */ - $httpHeaders = $this->getHTTPHeaders($uri); - - /* ContentType needs to get a default, because many webservers will otherwise - * default to text/html, and we don't want this for security reasons. - */ - if (!isset($httpHeaders['Content-Type'])) { - $httpHeaders['Content-Type'] = 'application/octet-stream'; - } - - - if (isset($httpHeaders['Content-Length'])) { - - $nodeSize = $httpHeaders['Content-Length']; - - // Need to unset Content-Length, because we'll handle that during figuring out the range - unset($httpHeaders['Content-Length']); - - } else { - $nodeSize = null; - } - - $this->httpResponse->setHeaders($httpHeaders); - - $range = $this->getHTTPRange(); - $ifRange = $this->httpRequest->getHeader('If-Range'); - $ignoreRangeHeader = false; - - // If ifRange is set, and range is specified, we first need to check - // the precondition. - if ($nodeSize && $range && $ifRange) { - - // if IfRange is parsable as a date we'll treat it as a DateTime - // otherwise, we must treat it as an etag. - try { - $ifRangeDate = new \DateTime($ifRange); - - // It's a date. We must check if the entity is modified since - // the specified date. - if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; - else { - $modified = new \DateTime($httpHeaders['Last-Modified']); - if($modified > $ifRangeDate) $ignoreRangeHeader = true; - } - - } catch (\Exception $e) { - - // It's an entity. We can do a simple comparison. - if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; - elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; - } - } - - // We're only going to support HTTP ranges if the backend provided a filesize - if (!$ignoreRangeHeader && $nodeSize && $range) { - - // Determining the exact byte offsets - if (!is_null($range[0])) { - - $start = $range[0]; - $end = $range[1]?$range[1]:$nodeSize-1; - if($start >= $nodeSize) - throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); - - if($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); - if($end >= $nodeSize) $end = $nodeSize-1; - - } else { - - $start = $nodeSize-$range[1]; - $end = $nodeSize-1; - - if ($start<0) $start = 0; - - } - - // New read/write stream - $newStream = fopen('php://temp','r+'); - - // stream_copy_to_stream() has a bug/feature: the `whence` argument - // is interpreted as SEEK_SET (count from absolute offset 0), while - // for a stream it should be SEEK_CUR (count from current offset). - // If a stream is nonseekable, the function fails. So we *emulate* - // the correct behaviour with fseek(): - if ($start > 0) { - if (($curOffs = ftell($body)) === false) $curOffs = 0; - fseek($body, $start - $curOffs, SEEK_CUR); - } - stream_copy_to_stream($body, $newStream, $end-$start+1); - rewind($newStream); - - $this->httpResponse->setHeader('Content-Length', $end-$start+1); - $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize); - $this->httpResponse->sendStatus(206); - $this->httpResponse->sendBody($newStream); - - - } else { - - if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize); - $this->httpResponse->sendStatus(200); - $this->httpResponse->sendBody($body); - - } - - } - - /** - * HTTP HEAD - * - * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body - * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again - * - * @param string $uri - * @return void - */ - protected function httpHead($uri) { - - $node = $this->tree->getNodeForPath($uri); - /* This information is only collection for File objects. - * Ideally we want to throw 405 Method Not Allowed for every - * non-file, but MS Office does not like this - */ - if ($node instanceof IFile) { - $headers = $this->getHTTPHeaders($this->getRequestUri()); - if (!isset($headers['Content-Type'])) { - $headers['Content-Type'] = 'application/octet-stream'; - } - $this->httpResponse->setHeaders($headers); - } - $this->httpResponse->sendStatus(200); - - } - - /** - * HTTP Delete - * - * The HTTP delete method, deletes a given uri - * - * @param string $uri - * @return void - */ - protected function httpDelete($uri) { - - // Checking If-None-Match and related headers. - if (!$this->checkPreconditions()) return; - - if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; - $this->tree->delete($uri); - $this->broadcastEvent('afterUnbind',array($uri)); - - $this->httpResponse->sendStatus(204); - $this->httpResponse->setHeader('Content-Length','0'); - - } - - - /** - * WebDAV PROPFIND - * - * This WebDAV method requests information about an uri resource, or a list of resources - * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value - * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) - * - * The request body contains an XML data structure that has a list of properties the client understands - * The response body is also an xml document, containing information about every uri resource and the requested properties - * - * It has to return a HTTP 207 Multi-status status code - * - * @param string $uri - * @return void - */ - protected function httpPropfind($uri) { - - $requestedProperties = $this->parsePropFindRequest($this->httpRequest->getBody(true)); - - $depth = $this->getHTTPDepth(1); - // The only two options for the depth of a propfind is 0 or 1 - if ($depth!=0) $depth = 1; - - $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); - - // This is a multi-status response - $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->httpResponse->setHeader('Vary','Brief,Prefer'); - - // Normally this header is only needed for OPTIONS responses, however.. - // iCal seems to also depend on these being set for PROPFIND. Since - // this is not harmful, we'll add it. - $features = array('1','3', 'extended-mkcol'); - foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); - $this->httpResponse->setHeader('DAV',implode(', ',$features)); - - $prefer = $this->getHTTPPrefer(); - $minimal = $prefer['return-minimal']; - - $data = $this->generateMultiStatus($newProperties, $minimal); - $this->httpResponse->sendBody($data); - - } - - /** - * WebDAV PROPPATCH - * - * This method is called to update properties on a Node. The request is an XML body with all the mutations. - * In this XML body it is specified which properties should be set/updated and/or deleted - * - * @param string $uri - * @return void - */ - protected function httpPropPatch($uri) { - - $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true)); - - $result = $this->updateProperties($uri, $newProperties); - - $prefer = $this->getHTTPPrefer(); - $this->httpResponse->setHeader('Vary','Brief,Prefer'); - - if ($prefer['return-minimal']) { - - // If return-minimal is specified, we only have to check if the - // request was succesful, and don't need to return the - // multi-status. - $ok = true; - foreach($result as $code=>$prop) { - if ((int)$code > 299) { - $ok = false; - } - } - - if ($ok) { - - $this->httpResponse->sendStatus(204); - return; - - } - - } - - $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - - $this->httpResponse->sendBody( - $this->generateMultiStatus(array($result)) - ); - - } - - /** - * HTTP PUT method - * - * This HTTP method updates a file, or creates a new one. - * - * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content - * - * @param string $uri - * @return bool - */ - protected function httpPut($uri) { - - $body = $this->httpRequest->getBody(); - - // Intercepting Content-Range - if ($this->httpRequest->getHeader('Content-Range')) { - /** - Content-Range is dangerous for PUT requests: PUT per definition - stores a full resource. draft-ietf-httpbis-p2-semantics-15 says - in section 7.6: - An origin server SHOULD reject any PUT request that contains a - Content-Range header field, since it might be misinterpreted as - partial content (or might be partial content that is being mistakenly - PUT as a full representation). Partial content updates are possible - by targeting a separately identified resource with state that - overlaps a portion of the larger resource, or by using a different - method that has been specifically defined for partial updates (for - example, the PATCH method defined in [RFC5789]). - This clarifies RFC2616 section 9.6: - The recipient of the entity MUST NOT ignore any Content-* - (e.g. Content-Range) headers that it does not understand or implement - and MUST return a 501 (Not Implemented) response in such cases. - OTOH is a PUT request with a Content-Range currently the only way to - continue an aborted upload request and is supported by curl, mod_dav, - Tomcat and others. Since some clients do use this feature which results - in unexpected behaviour (cf PEAR::HTTP_WebDAV_Client 1.0.1), we reject - all PUT requests with a Content-Range for now. - */ - - throw new Exception\NotImplemented('PUT with Content-Range is not allowed.'); - } - - // Intercepting the Finder problem - if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) { - - /** - Many webservers will not cooperate well with Finder PUT requests, - because it uses 'Chunked' transfer encoding for the request body. - - The symptom of this problem is that Finder sends files to the - server, but they arrive as 0-length files in PHP. - - If we don't do anything, the user might think they are uploading - files successfully, but they end up empty on the server. Instead, - we throw back an error if we detect this. - - The reason Finder uses Chunked, is because it thinks the files - might change as it's being uploaded, and therefore the - Content-Length can vary. - - Instead it sends the X-Expected-Entity-Length header with the size - of the file at the very start of the request. If this header is set, - but we don't get a request body we will fail the request to - protect the end-user. - */ - - // Only reading first byte - $firstByte = fread($body,1); - if (strlen($firstByte)!==1) { - throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); - } - - // The body needs to stay intact, so we copy everything to a - // temporary stream. - - $newBody = fopen('php://temp','r+'); - fwrite($newBody,$firstByte); - stream_copy_to_stream($body, $newBody); - rewind($newBody); - - $body = $newBody; - - } - - // Checking If-None-Match and related headers. - if (!$this->checkPreconditions()) return; - - if ($this->tree->nodeExists($uri)) { - - $node = $this->tree->getNodeForPath($uri); - - // If the node is a collection, we'll deny it - if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.'); - if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false; - - $etag = $node->put($body); - - $this->broadcastEvent('afterWriteContent',array($uri, $node)); - - $this->httpResponse->setHeader('Content-Length','0'); - if ($etag) $this->httpResponse->setHeader('ETag',$etag); - $this->httpResponse->sendStatus(204); - - } else { - - $etag = null; - // If we got here, the resource didn't exist yet. - if (!$this->createFile($this->getRequestUri(),$body,$etag)) { - // For one reason or another the file was not created. - return; - } - - $this->httpResponse->setHeader('Content-Length','0'); - if ($etag) $this->httpResponse->setHeader('ETag', $etag); - $this->httpResponse->sendStatus(201); - - } - - } - - - /** - * WebDAV MKCOL - * - * The MKCOL method is used to create a new collection (directory) on the server - * - * @param string $uri - * @return void - */ - protected function httpMkcol($uri) { - - $requestBody = $this->httpRequest->getBody(true); - - if ($requestBody) { - - $contentType = $this->httpRequest->getHeader('Content-Type'); - if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) { - - // We must throw 415 for unsupported mkcol bodies - throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); - - } - - $dom = XMLUtil::loadDOMDocument($requestBody); - if (XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { - - // We must throw 415 for unsupported mkcol bodies - throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); - - } - - $properties = array(); - foreach($dom->firstChild->childNodes as $childNode) { - - if (XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue; - $properties = array_merge($properties, XMLUtil::parseProperties($childNode, $this->propertyMap)); - - } - if (!isset($properties['{DAV:}resourcetype'])) - throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property'); - - $resourceType = $properties['{DAV:}resourcetype']->getValue(); - unset($properties['{DAV:}resourcetype']); - - } else { - - $properties = array(); - $resourceType = array('{DAV:}collection'); - - } - - $result = $this->createCollection($uri, $resourceType, $properties); - - if (is_array($result)) { - $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - - $this->httpResponse->sendBody( - $this->generateMultiStatus(array($result)) - ); - - } else { - $this->httpResponse->setHeader('Content-Length','0'); - $this->httpResponse->sendStatus(201); - } - - } - - /** - * WebDAV HTTP MOVE method - * - * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo - * - * @param string $uri - * @return bool - */ - protected function httpMove($uri) { - - $moveInfo = $this->getCopyAndMoveInfo(); - - // If the destination is part of the source tree, we must fail - if ($moveInfo['destination']==$uri) - throw new Exception\Forbidden('Source and destination uri are identical.'); - - if ($moveInfo['destinationExists']) { - - if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false; - $this->tree->delete($moveInfo['destination']); - $this->broadcastEvent('afterUnbind',array($moveInfo['destination'])); - - } - - if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false; - if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false; - $this->tree->move($uri,$moveInfo['destination']); - $this->broadcastEvent('afterUnbind',array($uri)); - $this->broadcastEvent('afterBind',array($moveInfo['destination'])); - - // If a resource was overwritten we should send a 204, otherwise a 201 - $this->httpResponse->setHeader('Content-Length','0'); - $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201); - - } - - /** - * WebDAV HTTP COPY method - * - * This method copies one uri to a different uri, and works much like the MOVE request - * A lot of the actual request processing is done in getCopyMoveInfo - * - * @param string $uri - * @return bool - */ - protected function httpCopy($uri) { - - $copyInfo = $this->getCopyAndMoveInfo(); - // If the destination is part of the source tree, we must fail - if ($copyInfo['destination']==$uri) - throw new Exception\Forbidden('Source and destination uri are identical.'); - - if ($copyInfo['destinationExists']) { - if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false; - $this->tree->delete($copyInfo['destination']); - - } - if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false; - $this->tree->copy($uri,$copyInfo['destination']); - $this->broadcastEvent('afterBind',array($copyInfo['destination'])); - - // If a resource was overwritten we should send a 204, otherwise a 201 - $this->httpResponse->setHeader('Content-Length','0'); - $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201); - - } - - - - /** - * HTTP REPORT method implementation - * - * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) - * It's used in a lot of extensions, so it made sense to implement it into the core. - * - * @param string $uri - * @return void - */ - protected function httpReport($uri) { - - $body = $this->httpRequest->getBody(true); - $dom = XMLUtil::loadDOMDocument($body); - - $reportName = XMLUtil::toClarkNotation($dom->firstChild); - - if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) { - - // If broadcastEvent returned true, it means the report was not supported - throw new Exception\ReportNotSupported(); - - } - - } - - // }}} - // {{{ HTTP/WebDAV protocol helpers - - /** - * Returns an array with all the supported HTTP methods for a specific uri. - * - * @param string $uri - * @return array - */ - public function getAllowedMethods($uri) { - - $methods = array( - 'OPTIONS', - 'GET', - 'HEAD', - 'DELETE', - 'PROPFIND', - 'PUT', - 'PROPPATCH', - 'COPY', - 'MOVE', - 'REPORT' - ); - - // The MKCOL is only allowed on an unmapped uri - try { - $this->tree->getNodeForPath($uri); - } catch (Exception\NotFound $e) { - $methods[] = 'MKCOL'; - } - - // We're also checking if any of the plugins register any new methods - foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri)); - array_unique($methods); - - return $methods; - - } - - /** - * Gets the uri for the request, keeping the base uri into consideration - * - * @return string - */ - public function getRequestUri() { - - return $this->calculateUri($this->httpRequest->getUri()); - - } - - /** - * Calculates the uri for a request, making sure that the base uri is stripped out - * - * @param string $uri - * @throws Exception\Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri - * @return string - */ - public function calculateUri($uri) { - - if ($uri[0]!='/' && strpos($uri,'://')) { - - $uri = parse_url($uri,PHP_URL_PATH); - - } - - $uri = str_replace('//','/',$uri); - - if (strpos($uri,$this->getBaseUri())===0) { - - return trim(URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); - - // A special case, if the baseUri was accessed without a trailing - // slash, we'll accept it as well. - } elseif ($uri.'/' === $this->getBaseUri()) { - - return ''; - - } else { - - throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); - - } - - } - - /** - * Returns the HTTP depth header - * - * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object - * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent - * - * @param mixed $default - * @return int - */ - public function getHTTPDepth($default = self::DEPTH_INFINITY) { - - // If its not set, we'll grab the default - $depth = $this->httpRequest->getHeader('Depth'); - - if (is_null($depth)) return $default; - - if ($depth == 'infinity') return self::DEPTH_INFINITY; - - - // If its an unknown value. we'll grab the default - if (!ctype_digit($depth)) return $default; - - return (int)$depth; - - } - - /** - * Returns the HTTP range header - * - * This method returns null if there is no well-formed HTTP range request - * header or array($start, $end). - * - * The first number is the offset of the first byte in the range. - * The second number is the offset of the last byte in the range. - * - * If the second offset is null, it should be treated as the offset of the last byte of the entity - * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity - * - * @return array|null - */ - public function getHTTPRange() { - - $range = $this->httpRequest->getHeader('range'); - if (is_null($range)) return null; - - // Matching "Range: bytes=1234-5678: both numbers are optional - - if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; - - if ($matches[1]==='' && $matches[2]==='') return null; - - return array( - $matches[1]!==''?$matches[1]:null, - $matches[2]!==''?$matches[2]:null, - ); - - } - - /** - * Returns the HTTP Prefer header information. - * - * The prefer header is defined in: - * http://tools.ietf.org/html/draft-snell-http-prefer-14 - * - * This method will return an array with options. - * - * Currently, the following options may be returned: - * array( - * 'return-asynch' => true, - * 'return-minimal' => true, - * 'return-representation' => true, - * 'wait' => 30, - * 'strict' => true, - * 'lenient' => true, - * ) - * - * This method also supports the Brief header, and will also return - * 'return-minimal' if the brief header was set to 't'. - * - * For the boolean options, false will be returned if the headers are not - * specified. For the integer options it will be 'null'. - * - * @return array - */ - public function getHTTPPrefer() { - - $result = array( - 'return-asynch' => false, - 'return-minimal' => false, - 'return-representation' => false, - 'wait' => null, - 'strict' => false, - 'lenient' => false, - ); - - if ($prefer = $this->httpRequest->getHeader('Prefer')) { - - $parameters = array_map('trim', - explode(',', $prefer) - ); - - foreach($parameters as $parameter) { - - // Right now our regex only supports the tokens actually - // specified in the draft. We may need to expand this if new - // tokens get registered. - if(!preg_match('/^(?P[a-z0-9-]+)(?:=(?P[0-9]+))?$/', $parameter, $matches)) { - continue; - } - - switch($matches['token']) { - - case 'return-asynch' : - case 'return-minimal' : - case 'return-representation' : - case 'strict' : - case 'lenient' : - $result[$matches['token']] = true; - break; - case 'wait' : - $result[$matches['token']] = $matches['value']; - break; - - } - - } - - } - - if ($this->httpRequest->getHeader('Brief')=='t') { - $result['return-minimal'] = true; - } - - return $result; - - } - - - /** - * Returns information about Copy and Move requests - * - * This function is created to help getting information about the source and the destination for the - * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions - * - * The returned value is an array with the following keys: - * * destination - Destination path - * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten) - * - * @return array - */ - public function getCopyAndMoveInfo() { - - // Collecting the relevant HTTP headers - if (!$this->httpRequest->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied'); - $destination = $this->calculateUri($this->httpRequest->getHeader('Destination')); - $overwrite = $this->httpRequest->getHeader('Overwrite'); - if (!$overwrite) $overwrite = 'T'; - if (strtoupper($overwrite)=='T') $overwrite = true; - elseif (strtoupper($overwrite)=='F') $overwrite = false; - // We need to throw a bad request exception, if the header was invalid - else throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F'); - - list($destinationDir) = URLUtil::splitPath($destination); - - try { - $destinationParent = $this->tree->getNodeForPath($destinationDir); - if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection'); - } catch (Exception\NotFound $e) { - - // If the destination parent node is not found, we throw a 409 - throw new Exception\Conflict('The destination node is not found'); - } - - try { - - $destinationNode = $this->tree->getNodeForPath($destination); - - // If this succeeded, it means the destination already exists - // we'll need to throw precondition failed in case overwrite is false - if (!$overwrite) throw new Exception\PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); - - } catch (Exception\NotFound $e) { - - // Destination didn't exist, we're all good - $destinationNode = false; - - - - } - - // These are the three relevant properties we need to return - return array( - 'destination' => $destination, - 'destinationExists' => $destinationNode==true, - 'destinationNode' => $destinationNode, - ); - - } - - /** - * Returns a list of properties for a path - * - * This is a simplified version getPropertiesForPath. - * if you aren't interested in status codes, but you just - * want to have a flat list of properties. Use this method. - * - * @param string $path - * @param array $propertyNames - */ - public function getProperties($path, $propertyNames) { - - $result = $this->getPropertiesForPath($path,$propertyNames,0); - return $result[0][200]; - - } - - /** - * A kid-friendly way to fetch properties for a node's children. - * - * The returned array will be indexed by the path of the of child node. - * Only properties that are actually found will be returned. - * - * The parent node will not be returned. - * - * @param string $path - * @param array $propertyNames - * @return array - */ - public function getPropertiesForChildren($path, $propertyNames) { - - $result = array(); - foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) { - - // Skipping the parent path - if ($k === 0) continue; - - $result[$row['href']] = $row[200]; - - } - return $result; - - } - - /** - * Returns a list of HTTP headers for a particular resource - * - * The generated http headers are based on properties provided by the - * resource. The method basically provides a simple mapping between - * DAV property and HTTP header. - * - * The headers are intended to be used for HEAD and GET requests. - * - * @param string $path - * @return array - */ - public function getHTTPHeaders($path) { - - $propertyMap = array( - '{DAV:}getcontenttype' => 'Content-Type', - '{DAV:}getcontentlength' => 'Content-Length', - '{DAV:}getlastmodified' => 'Last-Modified', - '{DAV:}getetag' => 'ETag', - ); - - $properties = $this->getProperties($path,array_keys($propertyMap)); - - $headers = array(); - foreach($propertyMap as $property=>$header) { - if (!isset($properties[$property])) continue; - - if (is_scalar($properties[$property])) { - $headers[$header] = $properties[$property]; - - // GetLastModified gets special cased - } elseif ($properties[$property] instanceof Property\GetLastModified) { - $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime()); - } - - } - - return $headers; - - } - - /** - * Returns a list of properties for a given path - * - * The path that should be supplied should have the baseUrl stripped out - * The list of properties should be supplied in Clark notation. If the list is empty - * 'allprops' is assumed. - * - * If a depth of 1 is requested child elements will also be returned. - * - * @param string $path - * @param array $propertyNames - * @param int $depth - * @return array - */ - public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) { - - if ($depth!=0) $depth = 1; - - $path = rtrim($path,'/'); - - // This event allows people to intercept these requests early on in the - // process. - // - // We're not doing anything with the result, but this can be helpful to - // pre-fetch certain expensive live properties. - $this->broadCastEvent('beforeGetPropertiesForPath', array($path, $propertyNames, $depth)); - - $returnPropertyList = array(); - - $parentNode = $this->tree->getNodeForPath($path); - $nodes = array( - $path => $parentNode - ); - if ($depth==1 && $parentNode instanceof ICollection) { - foreach($this->tree->getChildren($path) as $childNode) - $nodes[$path . '/' . $childNode->getName()] = $childNode; - } - - // If the propertyNames array is empty, it means all properties are requested. - // We shouldn't actually return everything we know though, and only return a - // sensible list. - $allProperties = count($propertyNames)==0; - - foreach($nodes as $myPath=>$node) { - - $currentPropertyNames = $propertyNames; - - $newProperties = array( - '200' => array(), - '404' => array(), - ); - - if ($allProperties) { - // Default list of propertyNames, when all properties were requested. - $currentPropertyNames = array( - '{DAV:}getlastmodified', - '{DAV:}getcontentlength', - '{DAV:}resourcetype', - '{DAV:}quota-used-bytes', - '{DAV:}quota-available-bytes', - '{DAV:}getetag', - '{DAV:}getcontenttype', - ); - } - - // If the resourceType was not part of the list, we manually add it - // and mark it for removal. We need to know the resourcetype in order - // to make certain decisions about the entry. - // WebDAV dictates we should add a / and the end of href's for collections - $removeRT = false; - if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) { - $currentPropertyNames[] = '{DAV:}resourcetype'; - $removeRT = true; - } - - $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties)); - // If this method explicitly returned false, we must ignore this - // node as it is inaccessible. - if ($result===false) continue; - - if (count($currentPropertyNames) > 0) { - - if ($node instanceof IProperties) { - $nodeProperties = $node->getProperties($currentPropertyNames); - - // The getProperties method may give us too much, - // properties, in case the implementor was lazy. - // - // So as we loop through this list, we will only take the - // properties that were actually requested and discard the - // rest. - foreach($currentPropertyNames as $k=>$currentPropertyName) { - if (isset($nodeProperties[$currentPropertyName])) { - unset($currentPropertyNames[$k]); - $newProperties[200][$currentPropertyName] = $nodeProperties[$currentPropertyName]; - } - } - - } - - } - - foreach($currentPropertyNames as $prop) { - - if (isset($newProperties[200][$prop])) continue; - - switch($prop) { - case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Property\GetLastModified($node->getLastModified()); break; - case '{DAV:}getcontentlength' : - if ($node instanceof IFile) { - $size = $node->getSize(); - if (!is_null($size)) { - $newProperties[200][$prop] = (int)$node->getSize(); - } - } - break; - case '{DAV:}quota-used-bytes' : - if ($node instanceof IQuota) { - $quotaInfo = $node->getQuotaInfo(); - $newProperties[200][$prop] = $quotaInfo[0]; - } - break; - case '{DAV:}quota-available-bytes' : - if ($node instanceof IQuota) { - $quotaInfo = $node->getQuotaInfo(); - $newProperties[200][$prop] = $quotaInfo[1]; - } - break; - case '{DAV:}getetag' : if ($node instanceof IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; - case '{DAV:}getcontenttype' : if ($node instanceof IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; - case '{DAV:}supported-report-set' : - $reports = array(); - foreach($this->plugins as $plugin) { - $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath)); - } - $newProperties[200][$prop] = new Property\SupportedReportSet($reports); - break; - case '{DAV:}resourcetype' : - $newProperties[200]['{DAV:}resourcetype'] = new Property\ResourceType(); - foreach($this->resourceTypeMapping as $className => $resourceType) { - if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType); - } - break; - - } - - // If we were unable to find the property, we will list it as 404. - if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; - - } - - $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node)); - - $newProperties['href'] = trim($myPath,'/'); - - // Its is a WebDAV recommendation to add a trailing slash to collectionnames. - // Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard. - if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) { - $rt = $newProperties[200]['{DAV:}resourcetype']; - if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) { - $newProperties['href'] .='/'; - } - } - - // If the resourcetype property was manually added to the requested property list, - // we will remove it again. - if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']); - - $returnPropertyList[] = $newProperties; - - } - - return $returnPropertyList; - - } - - /** - * This method is invoked by sub-systems creating a new file. - * - * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). - * It was important to get this done through a centralized function, - * allowing plugins to intercept this using the beforeCreateFile event. - * - * This method will return true if the file was actually created - * - * @param string $uri - * @param resource $data - * @param string $etag - * @return bool - */ - public function createFile($uri,$data, &$etag = null) { - - list($dir,$name) = URLUtil::splitPath($uri); - - if (!$this->broadcastEvent('beforeBind',array($uri))) return false; - - $parent = $this->tree->getNodeForPath($dir); - if (!$parent instanceof ICollection) { - throw new Exception\Conflict('Files can only be created as children of collections'); - } - - if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false; - - $etag = $parent->createFile($name,$data); - $this->tree->markDirty($dir . '/' . $name); - - $this->broadcastEvent('afterBind',array($uri)); - $this->broadcastEvent('afterCreateFile',array($uri, $parent)); - - return true; - } - - /** - * This method is invoked by sub-systems creating a new directory. - * - * @param string $uri - * @return void - */ - public function createDirectory($uri) { - - $this->createCollection($uri,array('{DAV:}collection'),array()); - - } - - /** - * Use this method to create a new collection - * - * The {DAV:}resourcetype is specified using the resourceType array. - * At the very least it must contain {DAV:}collection. - * - * The properties array can contain a list of additional properties. - * - * @param string $uri The new uri - * @param array $resourceType The resourceType(s) - * @param array $properties A list of properties - * @return array|null - */ - public function createCollection($uri, array $resourceType, array $properties) { - - list($parentUri,$newName) = URLUtil::splitPath($uri); - - // Making sure {DAV:}collection was specified as resourceType - if (!in_array('{DAV:}collection', $resourceType)) { - throw new Exception\InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection'); - } - - - // Making sure the parent exists - try { - - $parent = $this->tree->getNodeForPath($parentUri); - - } catch (Exception\NotFound $e) { - - throw new Exception\Conflict('Parent node does not exist'); - - } - - // Making sure the parent is a collection - if (!$parent instanceof ICollection) { - throw new Exception\Conflict('Parent node is not a collection'); - } - - - - // Making sure the child does not already exist - try { - $parent->getChild($newName); - - // If we got here.. it means there's already a node on that url, and we need to throw a 405 - throw new Exception\MethodNotAllowed('The resource you tried to create already exists'); - - } catch (Exception\NotFound $e) { - // This is correct - } - - - if (!$this->broadcastEvent('beforeBind',array($uri))) return; - - // There are 2 modes of operation. The standard collection - // creates the directory, and then updates properties - // the extended collection can create it directly. - if ($parent instanceof IExtendedCollection) { - - $parent->createExtendedCollection($newName, $resourceType, $properties); - - } else { - - // No special resourcetypes are supported - if (count($resourceType)>1) { - throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); - } - - $parent->createDirectory($newName); - $rollBack = false; - $exception = null; - $errorResult = null; - - if (count($properties)>0) { - - try { - - $errorResult = $this->updateProperties($uri, $properties); - if (!isset($errorResult[200])) { - $rollBack = true; - } - - } catch (Exception $e) { - - $rollBack = true; - $exception = $e; - - } - - } - - if ($rollBack) { - if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; - $this->tree->delete($uri); - - // Re-throwing exception - if ($exception) throw $exception; - - return $errorResult; - } - - } - $this->tree->markDirty($parentUri); - $this->broadcastEvent('afterBind',array($uri)); - - } - - /** - * This method updates a resource's properties - * - * The properties array must be a list of properties. Array-keys are - * property names in clarknotation, array-values are it's values. - * If a property must be deleted, the value should be null. - * - * Note that this request should either completely succeed, or - * completely fail. - * - * The response is an array with statuscodes for keys, which in turn - * contain arrays with propertynames. This response can be used - * to generate a multistatus body. - * - * @param string $uri - * @param array $properties - * @return array - */ - public function updateProperties($uri, array $properties) { - - // we'll start by grabbing the node, this will throw the appropriate - // exceptions if it doesn't. - $node = $this->tree->getNodeForPath($uri); - - $result = array( - 200 => array(), - 403 => array(), - 424 => array(), - ); - $remainingProperties = $properties; - $hasError = false; - - // Running through all properties to make sure none of them are protected - if (!$hasError) foreach($properties as $propertyName => $value) { - if(in_array($propertyName, $this->protectedProperties)) { - $result[403][$propertyName] = null; - unset($remainingProperties[$propertyName]); - $hasError = true; - } - } - - if (!$hasError) { - // Allowing plugins to take care of property updating - $hasError = !$this->broadcastEvent('updateProperties',array( - &$remainingProperties, - &$result, - $node - )); - } - - // If the node is not an instance of Sabre\DAV\IProperties, every - // property is 403 Forbidden - if (!$hasError && count($remainingProperties) && !($node instanceof IProperties)) { - $hasError = true; - foreach($properties as $propertyName=> $value) { - $result[403][$propertyName] = null; - } - $remainingProperties = array(); - } - - // Only if there were no errors we may attempt to update the resource - if (!$hasError) { - - if (count($remainingProperties)>0) { - - $updateResult = $node->updateProperties($remainingProperties); - - if ($updateResult===true) { - // success - foreach($remainingProperties as $propertyName=>$value) { - $result[200][$propertyName] = null; - } - - } elseif ($updateResult===false) { - // The node failed to update the properties for an - // unknown reason - foreach($remainingProperties as $propertyName=>$value) { - $result[403][$propertyName] = null; - } - - } elseif (is_array($updateResult)) { - - // The node has detailed update information - // We need to merge the results with the earlier results. - foreach($updateResult as $status => $props) { - if (is_array($props)) { - if (!isset($result[$status])) - $result[$status] = array(); - - $result[$status] = array_merge($result[$status], $updateResult[$status]); - } - } - - } else { - throw new Exception('Invalid result from updateProperties'); - } - $remainingProperties = array(); - } - - } - - foreach($remainingProperties as $propertyName=>$value) { - // if there are remaining properties, it must mean - // there's a dependency failure - $result[424][$propertyName] = null; - } - - // Removing empty array values - foreach($result as $status=>$props) { - - if (count($props)===0) unset($result[$status]); - - } - $result['href'] = $uri; - return $result; - - } - - /** - * This method checks the main HTTP preconditions. - * - * Currently these are: - * * If-Match - * * If-None-Match - * * If-Modified-Since - * * If-Unmodified-Since - * - * The method will return true if all preconditions are met - * The method will return false, or throw an exception if preconditions - * failed. If false is returned the operation should be aborted, and - * the appropriate HTTP response headers are already set. - * - * Normally this method will throw 412 Precondition Failed for failures - * related to If-None-Match, If-Match and If-Unmodified Since. It will - * set the status to 304 Not Modified for If-Modified_since. - * - * If the $handleAsGET argument is set to true, it will also return 304 - * Not Modified for failure of the If-None-Match precondition. This is the - * desired behaviour for HTTP GET and HTTP HEAD requests. - * - * @param bool $handleAsGET - * @return bool - */ - public function checkPreconditions($handleAsGET = false) { - - $uri = $this->getRequestUri(); - $node = null; - $lastMod = null; - $etag = null; - - if ($ifMatch = $this->httpRequest->getHeader('If-Match')) { - - // If-Match contains an entity tag. Only if the entity-tag - // matches we are allowed to make the request succeed. - // If the entity-tag is '*' we are only allowed to make the - // request succeed if a resource exists at that url. - try { - $node = $this->tree->getNodeForPath($uri); - } catch (Exception\NotFound $e) { - throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); - } - - // Only need to check entity tags if they are not * - if ($ifMatch!=='*') { - - // There can be multiple etags - $ifMatch = explode(',',$ifMatch); - $haveMatch = false; - foreach($ifMatch as $ifMatchItem) { - - // Stripping any extra spaces - $ifMatchItem = trim($ifMatchItem,' '); - - $etag = $node->getETag(); - if ($etag===$ifMatchItem) { - $haveMatch = true; - } else { - // Evolution has a bug where it sometimes prepends the " - // with a \. This is our workaround. - if (str_replace('\\"','"', $ifMatchItem) === $etag) { - $haveMatch = true; - } - } - - } - if (!$haveMatch) { - throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); - } - } - } - - if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) { - - // The If-None-Match header contains an etag. - // Only if the ETag does not match the current ETag, the request will succeed - // The header can also contain *, in which case the request - // will only succeed if the entity does not exist at all. - $nodeExists = true; - if (!$node) { - try { - $node = $this->tree->getNodeForPath($uri); - } catch (Exception\NotFound $e) { - $nodeExists = false; - } - } - if ($nodeExists) { - $haveMatch = false; - if ($ifNoneMatch==='*') $haveMatch = true; - else { - - // There might be multiple etags - $ifNoneMatch = explode(',', $ifNoneMatch); - $etag = $node->getETag(); - - foreach($ifNoneMatch as $ifNoneMatchItem) { - - // Stripping any extra spaces - $ifNoneMatchItem = trim($ifNoneMatchItem,' '); - - if ($etag===$ifNoneMatchItem) $haveMatch = true; - - } - - } - - if ($haveMatch) { - if ($handleAsGET) { - $this->httpResponse->sendStatus(304); - return false; - } else { - throw new Exception\PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match'); - } - } - } - - } - - if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) { - - // The If-Modified-Since header contains a date. We - // will only return the entity if it has been changed since - // that date. If it hasn't been changed, we return a 304 - // header - // Note that this header only has to be checked if there was no If-None-Match header - // as per the HTTP spec. - $date = HTTP\Util::parseHTTPDate($ifModifiedSince); - - if ($date) { - if (is_null($node)) { - $node = $this->tree->getNodeForPath($uri); - } - $lastMod = $node->getLastModified(); - if ($lastMod) { - $lastMod = new \DateTime('@' . $lastMod); - if ($lastMod <= $date) { - $this->httpResponse->sendStatus(304); - $this->httpResponse->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod)); - return false; - } - } - } - } - - if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) { - - // The If-Unmodified-Since will allow allow the request if the - // entity has not changed since the specified date. - $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince); - - // We must only check the date if it's valid - if ($date) { - if (is_null($node)) { - $node = $this->tree->getNodeForPath($uri); - } - $lastMod = $node->getLastModified(); - if ($lastMod) { - $lastMod = new \DateTime('@' . $lastMod); - if ($lastMod > $date) { - throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since'); - } - } - } - - } - return true; - - } - - // }}} - // {{{ XML Readers & Writers - - - /** - * Generates a WebDAV propfind response body based on a list of nodes. - * - * If 'strip404s' is set to true, all 404 responses will be removed. - * - * @param array $fileProperties The list with nodes - * @param bool strip404s - * @return string - */ - public function generateMultiStatus(array $fileProperties, $strip404s = false) { - - $dom = new \DOMDocument('1.0','utf-8'); - //$dom->formatOutput = true; - $multiStatus = $dom->createElement('d:multistatus'); - $dom->appendChild($multiStatus); - - // Adding in default namespaces - foreach($this->xmlNamespaces as $namespace=>$prefix) { - - $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); - - } - - foreach($fileProperties as $entry) { - - $href = $entry['href']; - unset($entry['href']); - - if ($strip404s && isset($entry[404])) { - unset($entry[404]); - } - - $response = new Property\Response($href,$entry); - $response->serialize($this,$multiStatus); - - } - - return $dom->saveXML(); - - } - - /** - * This method parses a PropPatch request - * - * PropPatch changes the properties for a resource. This method - * returns a list of properties. - * - * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, - * and the value contains the property value. If a property is to be removed the value - * will be null. - * - * @param string $body xml body - * @return array list of properties in need of updating or deletion - */ - public function parsePropPatchRequest($body) { - - //We'll need to change the DAV namespace declaration to something else in order to make it parsable - $dom = XMLUtil::loadDOMDocument($body); - - $newProperties = array(); - - foreach($dom->firstChild->childNodes as $child) { - - if ($child->nodeType !== XML_ELEMENT_NODE) continue; - - $operation = XMLUtil::toClarkNotation($child); - - if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; - - $innerProperties = XMLUtil::parseProperties($child, $this->propertyMap); - - foreach($innerProperties as $propertyName=>$propertyValue) { - - if ($operation==='{DAV:}remove') { - $propertyValue = null; - } - - $newProperties[$propertyName] = $propertyValue; - - } - - } - - return $newProperties; - - } - - /** - * This method parses the PROPFIND request and returns its information - * - * This will either be a list of properties, or an empty array; in which case - * an {DAV:}allprop was requested. - * - * @param string $body - * @return array - */ - public function parsePropFindRequest($body) { - - // If the propfind body was empty, it means IE is requesting 'all' properties - if (!$body) return array(); - - $dom = XMLUtil::loadDOMDocument($body); - $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); - return array_keys(XMLUtil::parseProperties($elem)); - - } - - // }}} - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php deleted file mode 100644 index d232cfe4b..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/ServerPlugin.php +++ /dev/null @@ -1,90 +0,0 @@ -name = $name; - foreach($children as $child) { - - if (!($child instanceof INode)) throw new Exception('Only instances of Sabre\DAV\INode are allowed to be passed in the children argument'); - $this->addChild($child); - - } - - } - - /** - * Adds a new childnode to this collection - * - * @param INode $child - * @return void - */ - public function addChild(INode $child) { - - $this->children[$child->getName()] = $child; - - } - - /** - * Returns the name of the collection - * - * @return string - */ - public function getName() { - - return $this->name; - - } - - /** - * Returns a child object, by its name. - * - * This method makes use of the getChildren method to grab all the child nodes, and compares the name. - * Generally its wise to override this, as this can usually be optimized - * - * This method must throw Sabre\DAV\Exception\NotFound if the node does not - * exist. - * - * @param string $name - * @throws Exception\NotFound - * @return INode - */ - public function getChild($name) { - - if (isset($this->children[$name])) return $this->children[$name]; - throw new Exception\NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); - - } - - /** - * Returns a list of children for this collection - * - * @return array - */ - public function getChildren() { - - return array_values($this->children); - - } - - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php deleted file mode 100644 index ede3ff48e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/SimpleFile.php +++ /dev/null @@ -1,121 +0,0 @@ -name = $name; - $this->contents = $contents; - $this->mimeType = $mimeType; - - } - - /** - * Returns the node name for this file. - * - * This name is used to construct the url. - * - * @return string - */ - public function getName() { - - return $this->name; - - } - - /** - * Returns the data - * - * This method may either return a string or a readable stream resource - * - * @return mixed - */ - public function get() { - - return $this->contents; - - } - - /** - * Returns the size of the file, in bytes. - * - * @return int - */ - public function getSize() { - - return strlen($this->contents); - - } - - /** - * Returns the ETag for a file - * - * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined - * @return string - */ - public function getETag() { - - return '"' . md5($this->contents) . '"'; - - } - - /** - * Returns the mime-type for a file - * - * If null is returned, we'll assume application/octet-stream - * @return string - */ - public function getContentType() { - - return $this->mimeType; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/StringUtil.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/StringUtil.php deleted file mode 100644 index 3426dd44d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/StringUtil.php +++ /dev/null @@ -1,91 +0,0 @@ -dataDir = $dataDir; - - } - - /** - * Initialize the plugin - * - * This is called automatically be the Server class after this plugin is - * added with Sabre\DAV\Server::addPlugin() - * - * @param Server $server - * @return void - */ - public function initialize(Server $server) { - - $this->server = $server; - $server->subscribeEvent('beforeMethod',array($this,'beforeMethod')); - $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile')); - - } - - /** - * This method is called before any HTTP method handler - * - * This method intercepts any GET, DELETE, PUT and PROPFIND calls to - * filenames that are known to match the 'temporary file' regex. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function beforeMethod($method, $uri) { - - if (!$tempLocation = $this->isTempFile($uri)) - return true; - - switch($method) { - case 'GET' : - return $this->httpGet($tempLocation); - case 'PUT' : - return $this->httpPut($tempLocation); - case 'PROPFIND' : - return $this->httpPropfind($tempLocation, $uri); - case 'DELETE' : - return $this->httpDelete($tempLocation); - } - return true; - - } - - /** - * This method is invoked if some subsystem creates a new file. - * - * This is used to deal with HTTP LOCK requests which create a new - * file. - * - * @param string $uri - * @param resource $data - * @return bool - */ - public function beforeCreateFile($uri,$data) { - - if ($tempPath = $this->isTempFile($uri)) { - - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - file_put_contents($tempPath,$data); - return false; - } - return true; - - } - - /** - * This method will check if the url matches the temporary file pattern - * if it does, it will return an path based on $this->dataDir for the - * temporary file storage. - * - * @param string $path - * @return boolean|string - */ - protected function isTempFile($path) { - - // We're only interested in the basename. - list(, $tempPath) = URLUtil::splitPath($path); - - foreach($this->temporaryFilePatterns as $tempFile) { - - if (preg_match($tempFile,$tempPath)) { - return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; - } - - } - - return false; - - } - - - /** - * This method handles the GET method for temporary files. - * If the file doesn't exist, it will return false which will kick in - * the regular system for the GET method. - * - * @param string $tempLocation - * @return bool - */ - public function httpGet($tempLocation) { - - if (!file_exists($tempLocation)) return true; - - $hR = $this->server->httpResponse; - $hR->setHeader('Content-Type','application/octet-stream'); - $hR->setHeader('Content-Length',filesize($tempLocation)); - $hR->setHeader('X-Sabre-Temp','true'); - $hR->sendStatus(200); - $hR->sendBody(fopen($tempLocation,'r')); - return false; - - } - - /** - * This method handles the PUT method. - * - * @param string $tempLocation - * @return bool - */ - public function httpPut($tempLocation) { - - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - - $newFile = !file_exists($tempLocation); - - if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { - throw new Exception\PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); - } - - file_put_contents($tempLocation,$this->server->httpRequest->getBody()); - $hR->sendStatus($newFile?201:200); - return false; - - } - - /** - * This method handles the DELETE method. - * - * If the file didn't exist, it will return false, which will make the - * standard HTTP DELETE handler kick in. - * - * @param string $tempLocation - * @return bool - */ - public function httpDelete($tempLocation) { - - if (!file_exists($tempLocation)) return true; - - unlink($tempLocation); - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - $hR->sendStatus(204); - return false; - - } - - /** - * This method handles the PROPFIND method. - * - * It's a very lazy method, it won't bother checking the request body - * for which properties were requested, and just sends back a default - * set of properties. - * - * @param string $tempLocation - * @param string $uri - * @return bool - */ - public function httpPropfind($tempLocation, $uri) { - - if (!file_exists($tempLocation)) return true; - - $hR = $this->server->httpResponse; - $hR->setHeader('X-Sabre-Temp','true'); - $hR->sendStatus(207); - $hR->setHeader('Content-Type','application/xml; charset=utf-8'); - - $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); - - $properties = array( - 'href' => $uri, - 200 => array( - '{DAV:}getlastmodified' => new Property\GetLastModified(filemtime($tempLocation)), - '{DAV:}getcontentlength' => filesize($tempLocation), - '{DAV:}resourcetype' => new Property\ResourceType(null), - '{'.Server::NS_SABREDAV.'}tempFile' => true, - - ), - ); - - $data = $this->server->generateMultiStatus(array($properties)); - $hR->sendBody($data); - return false; - - } - - - /** - * This method returns the directory where the temporary files should be stored. - * - * @return string - */ - protected function getDataDir() - { - return $this->dataDir; - } -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree.php deleted file mode 100644 index e097d3f86..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree.php +++ /dev/null @@ -1,193 +0,0 @@ -getNodeForPath($path); - return true; - - } catch (Exception\NotFound $e) { - - return false; - - } - - } - - /** - * Copies a file from path to another - * - * @param string $sourcePath The source location - * @param string $destinationPath The full destination path - * @return void - */ - public function copy($sourcePath, $destinationPath) { - - $sourceNode = $this->getNodeForPath($sourcePath); - - // grab the dirname and basename components - list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); - - $destinationParent = $this->getNodeForPath($destinationDir); - $this->copyNode($sourceNode,$destinationParent,$destinationName); - - $this->markDirty($destinationDir); - - } - - /** - * Moves a file from one location to another - * - * @param string $sourcePath The path to the file which should be moved - * @param string $destinationPath The full destination path, so not just the destination parent node - * @return int - */ - public function move($sourcePath, $destinationPath) { - - list($sourceDir, $sourceName) = URLUtil::splitPath($sourcePath); - list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); - - if ($sourceDir===$destinationDir) { - $renameable = $this->getNodeForPath($sourcePath); - $renameable->setName($destinationName); - } else { - $this->copy($sourcePath,$destinationPath); - $this->getNodeForPath($sourcePath)->delete(); - } - $this->markDirty($sourceDir); - $this->markDirty($destinationDir); - - } - - /** - * Deletes a node from the tree - * - * @param string $path - * @return void - */ - public function delete($path) { - - $node = $this->getNodeForPath($path); - $node->delete(); - - list($parent) = URLUtil::splitPath($path); - $this->markDirty($parent); - - } - - /** - * Returns a list of childnodes for a given path. - * - * @param string $path - * @return array - */ - public function getChildren($path) { - - $node = $this->getNodeForPath($path); - return $node->getChildren(); - - } - - /** - * This method is called with every tree update - * - * Examples of tree updates are: - * * node deletions - * * node creations - * * copy - * * move - * * renaming nodes - * - * If Tree classes implement a form of caching, this will allow - * them to make sure caches will be expired. - * - * If a path is passed, it is assumed that the entire subtree is dirty - * - * @param string $path - * @return void - */ - public function markDirty($path) { - - - } - - /** - * copyNode - * - * @param INode $source - * @param ICollection $destinationParent - * @param string $destinationName - * @return void - */ - protected function copyNode(INode $source,ICollection $destinationParent,$destinationName = null) { - - if (!$destinationName) $destinationName = $source->getName(); - - if ($source instanceof IFile) { - - $data = $source->get(); - - // If the body was a string, we need to convert it to a stream - if (is_string($data)) { - $stream = fopen('php://temp','r+'); - fwrite($stream,$data); - rewind($stream); - $data = $stream; - } - $destinationParent->createFile($destinationName,$data); - $destination = $destinationParent->getChild($destinationName); - - } elseif ($source instanceof ICollection) { - - $destinationParent->createDirectory($destinationName); - - $destination = $destinationParent->getChild($destinationName); - foreach($source->getChildren() as $child) { - - $this->copyNode($child,$destination); - - } - - } - if ($source instanceof IProperties && $destination instanceof IProperties) { - - $props = $source->getProperties(array()); - $destination->updateProperties($props); - - } - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree/Filesystem.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree/Filesystem.php deleted file mode 100644 index 2ba11b040..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Tree/Filesystem.php +++ /dev/null @@ -1,133 +0,0 @@ -basePath = $basePath; - - } - - /** - * Returns a new node for the given path - * - * @param string $path - * @return DAV\FS\Node - */ - public function getNodeForPath($path) { - - $realPath = $this->getRealPath($path); - if (!file_exists($realPath)) { - throw new DAV\Exception\NotFound('File at location ' . $realPath . ' not found'); - } - if (is_dir($realPath)) { - return new DAV\FS\Directory($realPath); - } else { - return new DAV\FS\File($realPath); - } - - } - - /** - * Returns the real filesystem path for a webdav url. - * - * @param string $publicPath - * @return string - */ - protected function getRealPath($publicPath) { - - return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/'); - - } - - /** - * Copies a file or directory. - * - * This method must work recursively and delete the destination - * if it exists - * - * @param string $source - * @param string $destination - * @return void - */ - public function copy($source,$destination) { - - $source = $this->getRealPath($source); - $destination = $this->getRealPath($destination); - $this->realCopy($source,$destination); - - } - - /** - * Used by self::copy - * - * @param string $source - * @param string $destination - * @return void - */ - protected function realCopy($source,$destination) { - - if (is_file($source)) { - copy($source,$destination); - } else { - mkdir($destination); - foreach(scandir($source) as $subnode) { - - if ($subnode=='.' || $subnode=='..') continue; - $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode); - - } - } - - } - - /** - * Moves a file or directory recursively. - * - * If the destination exists, delete it first. - * - * @param string $source - * @param string $destination - * @return void - */ - public function move($source,$destination) { - - $source = $this->getRealPath($source); - $destination = $this->getRealPath($destination); - rename($source,$destination); - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/URLUtil.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/URLUtil.php deleted file mode 100644 index 739cf2c7a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/URLUtil.php +++ /dev/null @@ -1,124 +0,0 @@ -principalPrefix = $principalPrefix; - $this->principalBackend = $principalBackend; - - } - - /** - * This method returns a node for a principal. - * - * The passed array contains principal information, and is guaranteed to - * at least contain a uri item. Other properties may or may not be - * supplied by the authentication backend. - * - * @param array $principalInfo - * @return IPrincipal - */ - abstract function getChildForPrincipal(array $principalInfo); - - /** - * Returns the name of this collection. - * - * @return string - */ - public function getName() { - - list(,$name) = DAV\URLUtil::splitPath($this->principalPrefix); - return $name; - - } - - /** - * Return the list of users - * - * @return array - */ - public function getChildren() { - - if ($this->disableListing) - throw new DAV\Exception\MethodNotAllowed('Listing members of this collection is disabled'); - - $children = array(); - foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) { - - $children[] = $this->getChildForPrincipal($principalInfo); - - - } - return $children; - - } - - /** - * Returns a child object, by its name. - * - * @param string $name - * @throws DAV\Exception\NotFound - * @return IPrincipal - */ - public function getChild($name) { - - $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); - if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found'); - return $this->getChildForPrincipal($principalInfo); - - } - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return a list of 'child names', which may be - * used to call $this->getChild in the future. - * - * @param array $searchProperties - * @return array - */ - public function searchPrincipals(array $searchProperties) { - - $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties); - $r = array(); - - foreach($result as $row) { - list(, $r[]) = DAV\URLUtil::splitPath($row); - } - - return $r; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/AceConflict.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/AceConflict.php deleted file mode 100644 index ee18adeac..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/AceConflict.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:no-ace-conflict'); - $errorNode->appendChild($np); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NeedPrivileges.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NeedPrivileges.php deleted file mode 100644 index 7d9c80561..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NeedPrivileges.php +++ /dev/null @@ -1,83 +0,0 @@ -uri = $uri; - $this->privileges = $privileges; - - parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); - - } - - /** - * Adds in extra information in the xml response. - * - * This method adds the {DAV:}need-privileges element as defined in rfc3744 - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - $doc = $errorNode->ownerDocument; - - $np = $doc->createElementNS('DAV:','d:need-privileges'); - $errorNode->appendChild($np); - - foreach($this->privileges as $privilege) { - - $resource = $doc->createElementNS('DAV:','d:resource'); - $np->appendChild($resource); - - $resource->appendChild($doc->createElementNS('DAV:','d:href',$server->getBaseUri() . $this->uri)); - - $priv = $doc->createElementNS('DAV:','d:privilege'); - $resource->appendChild($priv); - - preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts); - $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2])); - - - } - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NoAbstract.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NoAbstract.php deleted file mode 100644 index 4e1899aac..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NoAbstract.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:no-abstract'); - $errorNode->appendChild($np); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php deleted file mode 100644 index d23fdc06a..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:recognized-principal'); - $errorNode->appendChild($np); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotSupportedPrivilege.php deleted file mode 100644 index dd4cb00a3..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Exception/NotSupportedPrivilege.php +++ /dev/null @@ -1,35 +0,0 @@ -ownerDocument; - - $np = $doc->createElementNS('DAV:','d:not-supported-privilege'); - $errorNode->appendChild($np); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/IACL.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/IACL.php deleted file mode 100644 index 92970c727..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/IACL.php +++ /dev/null @@ -1,74 +0,0 @@ -getChild in the future. - * - * @param array $searchProperties - * @return array - */ - function searchPrincipals(array $searchProperties); - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Plugin.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Plugin.php deleted file mode 100644 index 572a9d415..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Plugin.php +++ /dev/null @@ -1,1402 +0,0 @@ - 'Display name', - '{http://sabredav.org/ns}email-address' => 'Email address', - ); - - /** - * Any principal uri's added here, will automatically be added to the list - * of ACL's. They will effectively receive {DAV:}all privileges, as a - * protected privilege. - * - * @var array - */ - public $adminPrincipals = array(); - - /** - * Returns a list of features added by this plugin. - * - * This list is used in the response of a HTTP OPTIONS request. - * - * @return array - */ - public function getFeatures() { - - return array('access-control', 'calendarserver-principal-property-search'); - - } - - /** - * Returns a list of available methods for a given url - * - * @param string $uri - * @return array - */ - public function getMethods($uri) { - - return array('ACL'); - - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using Sabre\DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() { - - return 'acl'; - - } - - /** - * Returns a list of reports this plugin supports. - * - * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * - * @param string $uri - * @return array - */ - public function getSupportedReportSet($uri) { - - return array( - '{DAV:}expand-property', - '{DAV:}principal-property-search', - '{DAV:}principal-search-property-set', - ); - - } - - - /** - * Checks if the current user has the specified privilege(s). - * - * You can specify a single privilege, or a list of privileges. - * This method will throw an exception if the privilege is not available - * and return true otherwise. - * - * @param string $uri - * @param array|string $privileges - * @param int $recursion - * @param bool $throwExceptions if set to false, this method won't throw exceptions. - * @throws Sabre\DAVACL\Exception\NeedPrivileges - * @return bool - */ - public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { - - if (!is_array($privileges)) $privileges = array($privileges); - - $acl = $this->getCurrentUserPrivilegeSet($uri); - - if (is_null($acl)) { - if ($this->allowAccessToNodesWithoutACL) { - return true; - } else { - if ($throwExceptions) - throw new Exception\NeedPrivileges($uri,$privileges); - else - return false; - - } - } - - $failed = array(); - foreach($privileges as $priv) { - - if (!in_array($priv, $acl)) { - $failed[] = $priv; - } - - } - - if ($failed) { - if ($throwExceptions) - throw new Exception\NeedPrivileges($uri,$failed); - else - return false; - } - return true; - - } - - /** - * Returns the standard users' principal. - * - * This is one authorative principal url for the current user. - * This method will return null if the user wasn't logged in. - * - * @return string|null - */ - public function getCurrentUserPrincipal() { - - $authPlugin = $this->server->getPlugin('auth'); - if (is_null($authPlugin)) return null; - /** @var $authPlugin Sabre\DAV\Auth\Plugin */ - - $userName = $authPlugin->getCurrentUser(); - if (!$userName) return null; - - return $this->defaultUsernamePath . '/' . $userName; - - } - - - /** - * Returns a list of principals that's associated to the current - * user, either directly or through group membership. - * - * @return array - */ - public function getCurrentUserPrincipals() { - - $currentUser = $this->getCurrentUserPrincipal(); - - if (is_null($currentUser)) return array(); - - return array_merge( - array($currentUser), - $this->getPrincipalMembership($currentUser) - ); - - } - - /** - * This array holds a cache for all the principals that are associated with - * a single principal. - * - * @var array - */ - protected $principalMembershipCache = array(); - - - /** - * Returns all the principal groups the specified principal is a member of. - * - * @param string $principal - * @return array - */ - public function getPrincipalMembership($mainPrincipal) { - - // First check our cache - if (isset($this->principalMembershipCache[$mainPrincipal])) { - return $this->principalMembershipCache[$mainPrincipal]; - } - - $check = array($mainPrincipal); - $principals = array(); - - while(count($check)) { - - $principal = array_shift($check); - - $node = $this->server->tree->getNodeForPath($principal); - if ($node instanceof IPrincipal) { - foreach($node->getGroupMembership() as $groupMember) { - - if (!in_array($groupMember, $principals)) { - - $check[] = $groupMember; - $principals[] = $groupMember; - - } - - } - - } - - } - - // Store the result in the cache - $this->principalMembershipCache[$mainPrincipal] = $principals; - - return $principals; - - } - - /** - * Returns the supported privilege structure for this ACL plugin. - * - * See RFC3744 for more details. Currently we default on a simple, - * standard structure. - * - * You can either get the list of privileges by a uri (path) or by - * specifying a Node. - * - * @param string|DAV\INode $node - * @return array - */ - public function getSupportedPrivilegeSet($node) { - - if (is_string($node)) { - $node = $this->server->tree->getNodeForPath($node); - } - - if ($node instanceof IACL) { - $result = $node->getSupportedPrivilegeSet(); - - if ($result) - return $result; - } - - return self::getDefaultSupportedPrivilegeSet(); - - } - - /** - * Returns a fairly standard set of privileges, which may be useful for - * other systems to use as a basis. - * - * @return array - */ - static function getDefaultSupportedPrivilegeSet() { - - return array( - 'privilege' => '{DAV:}all', - 'abstract' => true, - 'aggregates' => array( - array( - 'privilege' => '{DAV:}read', - 'aggregates' => array( - array( - 'privilege' => '{DAV:}read-acl', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}read-current-user-privilege-set', - 'abstract' => true, - ), - ), - ), // {DAV:}read - array( - 'privilege' => '{DAV:}write', - 'aggregates' => array( - array( - 'privilege' => '{DAV:}write-acl', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}write-properties', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}write-content', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}bind', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}unbind', - 'abstract' => true, - ), - array( - 'privilege' => '{DAV:}unlock', - 'abstract' => true, - ), - ), - ), // {DAV:}write - ), - ); // {DAV:}all - - } - - /** - * Returns the supported privilege set as a flat list - * - * This is much easier to parse. - * - * The returned list will be index by privilege name. - * The value is a struct containing the following properties: - * - aggregates - * - abstract - * - concrete - * - * @param string|DAV\INode $node - * @return array - */ - final public function getFlatPrivilegeSet($node) { - - $privs = $this->getSupportedPrivilegeSet($node); - - $flat = array(); - $this->getFPSTraverse($privs, null, $flat); - - return $flat; - - } - - /** - * Traverses the privilege set tree for reordering - * - * This function is solely used by getFlatPrivilegeSet, and would have been - * a closure if it wasn't for the fact I need to support PHP 5.2. - * - * @param array $priv - * @param $concrete - * @param array $flat - * @return void - */ - final private function getFPSTraverse($priv, $concrete, &$flat) { - - $myPriv = array( - 'privilege' => $priv['privilege'], - 'abstract' => isset($priv['abstract']) && $priv['abstract'], - 'aggregates' => array(), - 'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'], - ); - - if (isset($priv['aggregates'])) - foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege']; - - $flat[$priv['privilege']] = $myPriv; - - if (isset($priv['aggregates'])) { - - foreach($priv['aggregates'] as $subPriv) { - - $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat); - - } - - } - - } - - /** - * Returns the full ACL list. - * - * Either a uri or a DAV\INode may be passed. - * - * null will be returned if the node doesn't support ACLs. - * - * @param string|DAV\INode $node - * @return array - */ - public function getACL($node) { - - if (is_string($node)) { - $node = $this->server->tree->getNodeForPath($node); - } - if (!$node instanceof IACL) { - return null; - } - $acl = $node->getACL(); - foreach($this->adminPrincipals as $adminPrincipal) { - $acl[] = array( - 'principal' => $adminPrincipal, - 'privilege' => '{DAV:}all', - 'protected' => true, - ); - } - return $acl; - - } - - /** - * Returns a list of privileges the current user has - * on a particular node. - * - * Either a uri or a DAV\INode may be passed. - * - * null will be returned if the node doesn't support ACLs. - * - * @param string|DAV\INode $node - * @return array - */ - public function getCurrentUserPrivilegeSet($node) { - - if (is_string($node)) { - $node = $this->server->tree->getNodeForPath($node); - } - - $acl = $this->getACL($node); - - if (is_null($acl)) return null; - - $principals = $this->getCurrentUserPrincipals(); - - $collected = array(); - - foreach($acl as $ace) { - - $principal = $ace['principal']; - - switch($principal) { - - case '{DAV:}owner' : - $owner = $node->getOwner(); - if ($owner && in_array($owner, $principals)) { - $collected[] = $ace; - } - break; - - - // 'all' matches for every user - case '{DAV:}all' : - - // 'authenticated' matched for every user that's logged in. - // Since it's not possible to use ACL while not being logged - // in, this is also always true. - case '{DAV:}authenticated' : - $collected[] = $ace; - break; - - // 'unauthenticated' can never occur either, so we simply - // ignore these. - case '{DAV:}unauthenticated' : - break; - - default : - if (in_array($ace['principal'], $principals)) { - $collected[] = $ace; - } - break; - - } - - - } - - // Now we deduct all aggregated privileges. - $flat = $this->getFlatPrivilegeSet($node); - - $collected2 = array(); - while(count($collected)) { - - $current = array_pop($collected); - $collected2[] = $current['privilege']; - - foreach($flat[$current['privilege']]['aggregates'] as $subPriv) { - $collected2[] = $subPriv; - $collected[] = $flat[$subPriv]; - } - - } - - return array_values(array_unique($collected2)); - - } - - /** - * Principal property search - * - * This method can search for principals matching certain values in - * properties. - * - * This method will return a list of properties for the matched properties. - * - * @param array $searchProperties The properties to search on. This is a - * key-value list. The keys are property - * names, and the values the strings to - * match them on. - * @param array $requestedProperties This is the list of properties to - * return for every match. - * @param string $collectionUri The principal collection to search on. - * If this is ommitted, the standard - * principal collection-set will be used. - * @return array This method returns an array structure similar to - * Sabre\DAV\Server::getPropertiesForPath. Returned - * properties are index by a HTTP status code. - * - */ - public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null) { - - if (!is_null($collectionUri)) { - $uris = array($collectionUri); - } else { - $uris = $this->principalCollectionSet; - } - - $lookupResults = array(); - foreach($uris as $uri) { - - $principalCollection = $this->server->tree->getNodeForPath($uri); - if (!$principalCollection instanceof IPrincipalCollection) { - // Not a principal collection, we're simply going to ignore - // this. - continue; - } - - $results = $principalCollection->searchPrincipals($searchProperties); - foreach($results as $result) { - $lookupResults[] = rtrim($uri,'/') . '/' . $result; - } - - } - - $matches = array(); - - foreach($lookupResults as $lookupResult) { - - list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); - - } - - return $matches; - - } - - /** - * Sets up the plugin - * - * This method is automatically called by the server class. - * - * @param DAV\Server $server - * @return void - */ - public function initialize(DAV\Server $server) { - - $this->server = $server; - $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); - - $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),20); - $server->subscribeEvent('beforeBind', array($this,'beforeBind'),20); - $server->subscribeEvent('beforeUnbind', array($this,'beforeUnbind'),20); - $server->subscribeEvent('updateProperties',array($this,'updateProperties')); - $server->subscribeEvent('beforeUnlock', array($this,'beforeUnlock'),20); - $server->subscribeEvent('report',array($this,'report')); - $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod')); - - array_push($server->protectedProperties, - '{DAV:}alternate-URI-set', - '{DAV:}principal-URL', - '{DAV:}group-membership', - '{DAV:}principal-collection-set', - '{DAV:}current-user-principal', - '{DAV:}supported-privilege-set', - '{DAV:}current-user-privilege-set', - '{DAV:}acl', - '{DAV:}acl-restrictions', - '{DAV:}inherited-acl-set', - '{DAV:}owner', - '{DAV:}group' - ); - - // Automatically mapping nodes implementing IPrincipal to the - // {DAV:}principal resourcetype. - $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; - - // Mapping the group-member-set property to the HrefList property - // class. - $server->propertyMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Property\\HrefList'; - - } - - - /* {{{ Event handlers */ - - /** - * Triggered before any method is handled - * - * @param string $method - * @param string $uri - * @return void - */ - public function beforeMethod($method, $uri) { - - $exists = $this->server->tree->nodeExists($uri); - - // If the node doesn't exists, none of these checks apply - if (!$exists) return; - - switch($method) { - - case 'GET' : - case 'HEAD' : - case 'OPTIONS' : - // For these 3 we only need to know if the node is readable. - $this->checkPrivileges($uri,'{DAV:}read'); - break; - - case 'PUT' : - case 'LOCK' : - case 'UNLOCK' : - // This method requires the write-content priv if the node - // already exists, and bind on the parent if the node is being - // created. - // The bind privilege is handled in the beforeBind event. - $this->checkPrivileges($uri,'{DAV:}write-content'); - break; - - - case 'PROPPATCH' : - $this->checkPrivileges($uri,'{DAV:}write-properties'); - break; - - case 'ACL' : - $this->checkPrivileges($uri,'{DAV:}write-acl'); - break; - - case 'COPY' : - case 'MOVE' : - // Copy requires read privileges on the entire source tree. - // If the target exists write-content normally needs to be - // checked, however, we're deleting the node beforehand and - // creating a new one after, so this is handled by the - // beforeUnbind event. - // - // The creation of the new node is handled by the beforeBind - // event. - // - // If MOVE is used beforeUnbind will also be used to check if - // the sourcenode can be deleted. - $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE); - - break; - - } - - } - - /** - * Triggered before a new node is created. - * - * This allows us to check permissions for any operation that creates a - * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. - * - * @param string $uri - * @return void - */ - public function beforeBind($uri) { - - list($parentUri,$nodeName) = DAV\URLUtil::splitPath($uri); - $this->checkPrivileges($parentUri,'{DAV:}bind'); - - } - - /** - * Triggered before a node is deleted - * - * This allows us to check permissions for any operation that will delete - * an existing node. - * - * @param string $uri - * @return void - */ - public function beforeUnbind($uri) { - - list($parentUri,$nodeName) = DAV\URLUtil::splitPath($uri); - $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS); - - } - - /** - * Triggered before a node is unlocked. - * - * @param string $uri - * @param DAV\Locks\LockInfo $lock - * @TODO: not yet implemented - * @return void - */ - public function beforeUnlock($uri, DAV\Locks\LockInfo $lock) { - - - } - - /** - * Triggered before properties are looked up in specific nodes. - * - * @param string $uri - * @param DAV\INode $node - * @param array $requestedProperties - * @param array $returnedProperties - * @TODO really should be broken into multiple methods, or even a class. - * @return bool - */ - public function beforeGetProperties($uri, DAV\INode $node, &$requestedProperties, &$returnedProperties) { - - // Checking the read permission - if (!$this->checkPrivileges($uri,'{DAV:}read',self::R_PARENT,false)) { - - // User is not allowed to read properties - if ($this->hideNodesFromListings) { - return false; - } - - // Marking all requested properties as '403'. - foreach($requestedProperties as $key=>$requestedProperty) { - unset($requestedProperties[$key]); - $returnedProperties[403][$requestedProperty] = null; - } - return; - - } - - /* Adding principal properties */ - if ($node instanceof IPrincipal) { - - if (false !== ($index = array_search('{DAV:}alternate-URI-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}alternate-URI-set'] = new DAV\Property\HrefList($node->getAlternateUriSet()); - - } - if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}principal-URL'] = new DAV\Property\Href($node->getPrincipalUrl() . '/'); - - } - if (false !== ($index = array_search('{DAV:}group-member-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}group-member-set'] = new DAV\Property\HrefList($node->getGroupMemberSet()); - - } - if (false !== ($index = array_search('{DAV:}group-membership', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}group-membership'] = new DAV\Property\HrefList($node->getGroupMembership()); - - } - - if (false !== ($index = array_search('{DAV:}displayname', $requestedProperties))) { - - $returnedProperties[200]['{DAV:}displayname'] = $node->getDisplayName(); - - } - - } - if (false !== ($index = array_search('{DAV:}principal-collection-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $val = $this->principalCollectionSet; - // Ensuring all collections end with a slash - foreach($val as $k=>$v) $val[$k] = $v . '/'; - $returnedProperties[200]['{DAV:}principal-collection-set'] = new DAV\Property\HrefList($val); - - } - if (false !== ($index = array_search('{DAV:}current-user-principal', $requestedProperties))) { - - unset($requestedProperties[$index]); - if ($url = $this->getCurrentUserPrincipal()) { - $returnedProperties[200]['{DAV:}current-user-principal'] = new Property\Principal(Property\Principal::HREF, $url . '/'); - } else { - $returnedProperties[200]['{DAV:}current-user-principal'] = new Property\Principal(Property\Principal::UNAUTHENTICATED); - } - - } - if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); - - } - if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) { - - if (!$this->checkPrivileges($uri, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) { - $returnedProperties[403]['{DAV:}current-user-privilege-set'] = null; - unset($requestedProperties[$index]); - } else { - $val = $this->getCurrentUserPrivilegeSet($node); - if (!is_null($val)) { - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}current-user-privilege-set'] = new Property\CurrentUserPrivilegeSet($val); - } - } - - } - - /* The ACL property contains all the permissions */ - if (false !== ($index = array_search('{DAV:}acl', $requestedProperties))) { - - if (!$this->checkPrivileges($uri, '{DAV:}read-acl', self::R_PARENT, false)) { - - unset($requestedProperties[$index]); - $returnedProperties[403]['{DAV:}acl'] = null; - - } else { - - $acl = $this->getACL($node); - if (!is_null($acl)) { - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}acl'] = new Property\Acl($this->getACL($node)); - } - - } - - } - - /* The acl-restrictions property contains information on how privileges - * must behave. - */ - if (false !== ($index = array_search('{DAV:}acl-restrictions', $requestedProperties))) { - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}acl-restrictions'] = new Property\AclRestrictions(); - } - - /* Adding ACL properties */ - if ($node instanceof IACL) { - - if (false !== ($index = array_search('{DAV:}owner', $requestedProperties))) { - - unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}owner'] = new DAV\Property\Href($node->getOwner() . '/'); - - } - - } - - } - - /** - * This method intercepts PROPPATCH methods and make sure the - * group-member-set is updated correctly. - * - * @param array $propertyDelta - * @param array $result - * @param DAV\INode $node - * @return bool - */ - public function updateProperties(&$propertyDelta, &$result, DAV\INode $node) { - - if (!array_key_exists('{DAV:}group-member-set', $propertyDelta)) - return; - - if (is_null($propertyDelta['{DAV:}group-member-set'])) { - $memberSet = array(); - } elseif ($propertyDelta['{DAV:}group-member-set'] instanceof DAV\Property\HrefList) { - $memberSet = array_map( - array($this->server,'calculateUri'), - $propertyDelta['{DAV:}group-member-set']->getHrefs() - ); - } else { - throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null'); - } - - if (!($node instanceof IPrincipal)) { - $result[403]['{DAV:}group-member-set'] = null; - unset($propertyDelta['{DAV:}group-member-set']); - - // Returning false will stop the updateProperties process - return false; - } - - $node->setGroupMemberSet($memberSet); - // We must also clear our cache, just in case - - $this->principalMembershipCache = array(); - - $result[200]['{DAV:}group-member-set'] = null; - unset($propertyDelta['{DAV:}group-member-set']); - - } - - /** - * This method handles HTTP REPORT requests - * - * @param string $reportName - * @param \DOMNode $dom - * @return bool - */ - public function report($reportName, $dom) { - - switch($reportName) { - - case '{DAV:}principal-property-search' : - $this->principalPropertySearchReport($dom); - return false; - case '{DAV:}principal-search-property-set' : - $this->principalSearchPropertySetReport($dom); - return false; - case '{DAV:}expand-property' : - $this->expandPropertyReport($dom); - return false; - - } - - } - - /** - * This event is triggered for any HTTP method that is not known by the - * webserver. - * - * @param string $method - * @param string $uri - * @return bool - */ - public function unknownMethod($method, $uri) { - - if ($method!=='ACL') return; - - $this->httpACL($uri); - return false; - - } - - /** - * This method is responsible for handling the 'ACL' event. - * - * @param string $uri - * @return void - */ - public function httpACL($uri) { - - $body = $this->server->httpRequest->getBody(true); - $dom = DAV\XMLUtil::loadDOMDocument($body); - - $newAcl = - Property\Acl::unserialize($dom->firstChild) - ->getPrivileges(); - - // Normalizing urls - foreach($newAcl as $k=>$newAce) { - $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']); - } - - $node = $this->server->tree->getNodeForPath($uri); - - if (!($node instanceof IACL)) { - throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method'); - } - - $oldAcl = $this->getACL($node); - - $supportedPrivileges = $this->getFlatPrivilegeSet($node); - - /* Checking if protected principals from the existing principal set are - not overwritten. */ - foreach($oldAcl as $oldAce) { - - if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; - - $found = false; - foreach($newAcl as $newAce) { - if ( - $newAce['privilege'] === $oldAce['privilege'] && - $newAce['principal'] === $oldAce['principal'] && - $newAce['protected'] - ) - $found = true; - } - - if (!$found) - throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); - - } - - foreach($newAcl as $newAce) { - - // Do we recognize the privilege - if (!isset($supportedPrivileges[$newAce['privilege']])) { - throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server'); - } - - if ($supportedPrivileges[$newAce['privilege']]['abstract']) { - throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege'); - } - - // Looking up the principal - try { - $principal = $this->server->tree->getNodeForPath($newAce['principal']); - } catch (DAV\Exception\NotFound $e) { - throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); - } - if (!($principal instanceof IPrincipal)) { - throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); - } - - } - $node->setACL($newAcl); - - } - - /* }}} */ - - /* Reports {{{ */ - - /** - * The expand-property report is defined in RFC3253 section 3-8. - * - * This report is very similar to a standard PROPFIND. The difference is - * that it has the additional ability to look at properties containing a - * {DAV:}href element, follow that property and grab additional elements - * there. - * - * Other rfc's, such as ACL rely on this report, so it made sense to put - * it in this plugin. - * - * @param \DOMElement $dom - * @return void - */ - protected function expandPropertyReport($dom) { - - $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild); - $depth = $this->server->getHTTPDepth(0); - $requestUri = $this->server->getRequestUri(); - - $result = $this->expandProperties($requestUri,$requestedProperties,$depth); - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $multiStatus = $dom->createElement('d:multistatus'); - $dom->appendChild($multiStatus); - - // Adding in default namespaces - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); - - } - - foreach($result as $response) { - $response->serialize($this->server, $multiStatus); - } - - $xml = $dom->saveXML(); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->sendBody($xml); - - } - - /** - * This method is used by expandPropertyReport to parse - * out the entire HTTP request. - * - * @param \DOMElement $node - * @return array - */ - protected function parseExpandPropertyReportRequest($node) { - - $requestedProperties = array(); - do { - - if (DAV\XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; - - if ($node->firstChild) { - - $children = $this->parseExpandPropertyReportRequest($node->firstChild); - - } else { - - $children = array(); - - } - - $namespace = $node->getAttribute('namespace'); - if (!$namespace) $namespace = 'DAV:'; - - $propName = '{'.$namespace.'}' . $node->getAttribute('name'); - $requestedProperties[$propName] = $children; - - } while ($node = $node->nextSibling); - - return $requestedProperties; - - } - - /** - * This method expands all the properties and returns - * a list with property values - * - * @param array $path - * @param array $requestedProperties the list of required properties - * @param int $depth - * @return array - */ - protected function expandProperties($path, array $requestedProperties, $depth) { - - $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); - - $result = array(); - - foreach($foundProperties as $node) { - - foreach($requestedProperties as $propertyName=>$childRequestedProperties) { - - // We're only traversing if sub-properties were requested - if(count($childRequestedProperties)===0) continue; - - // We only have to do the expansion if the property was found - // and it contains an href element. - if (!array_key_exists($propertyName,$node[200])) continue; - - if ($node[200][$propertyName] instanceof DAV\Property\IHref) { - $hrefs = array($node[200][$propertyName]->getHref()); - } elseif ($node[200][$propertyName] instanceof DAV\Property\HrefList) { - $hrefs = $node[200][$propertyName]->getHrefs(); - } - - $childProps = array(); - foreach($hrefs as $href) { - $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0)); - } - $node[200][$propertyName] = new DAV\Property\ResponseList($childProps); - - } - $result[] = new DAV\Property\Response($node['href'], $node); - - } - - return $result; - - } - - /** - * principalSearchPropertySetReport - * - * This method responsible for handing the - * {DAV:}principal-search-property-set report. This report returns a list - * of properties the client may search on, using the - * {DAV:}principal-property-search report. - * - * @param \DOMDocument $dom - * @return void - */ - protected function principalSearchPropertySetReport(\DOMDocument $dom) { - - $httpDepth = $this->server->getHTTPDepth(0); - if ($httpDepth!==0) { - throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); - } - - if ($dom->firstChild->hasChildNodes()) - throw new DAV\Exception\BadRequest('The principal-search-property-set report element is not allowed to have child elements'); - - $dom = new \DOMDocument('1.0','utf-8'); - $dom->formatOutput = true; - $root = $dom->createElement('d:principal-search-property-set'); - $dom->appendChild($root); - // Adding in default namespaces - foreach($this->server->xmlNamespaces as $namespace=>$prefix) { - - $root->setAttribute('xmlns:' . $prefix,$namespace); - - } - - $nsList = $this->server->xmlNamespaces; - - foreach($this->principalSearchPropertySet as $propertyName=>$description) { - - $psp = $dom->createElement('d:principal-search-property'); - $root->appendChild($psp); - - $prop = $dom->createElement('d:prop'); - $psp->appendChild($prop); - - $propName = null; - preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); - - $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]); - $prop->appendChild($currentProperty); - - $descriptionElem = $dom->createElement('d:description'); - $descriptionElem->setAttribute('xml:lang','en'); - $descriptionElem->appendChild($dom->createTextNode($description)); - $psp->appendChild($descriptionElem); - - - } - - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody($dom->saveXML()); - - } - - /** - * principalPropertySearchReport - * - * This method is responsible for handing the - * {DAV:}principal-property-search report. This report can be used for - * clients to search for groups of principals, based on the value of one - * or more properties. - * - * @param \DOMDocument $dom - * @return void - */ - protected function principalPropertySearchReport(\DOMDocument $dom) { - - list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom); - - $uri = null; - if (!$applyToPrincipalCollectionSet) { - $uri = $this->server->getRequestUri(); - } - $result = $this->principalSearch($searchProperties, $requestedProperties, $uri); - - $prefer = $this->server->getHTTPPRefer(); - - $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); - - } - - /** - * parsePrincipalPropertySearchReportRequest - * - * This method parses the request body from a - * {DAV:}principal-property-search report. - * - * This method returns an array with two elements: - * 1. an array with properties to search on, and their values - * 2. a list of propertyvalues that should be returned for the request. - * - * @param \DOMDocument $dom - * @return array - */ - protected function parsePrincipalPropertySearchReportRequest($dom) { - - $httpDepth = $this->server->getHTTPDepth(0); - if ($httpDepth!==0) { - throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); - } - - $searchProperties = array(); - - $applyToPrincipalCollectionSet = false; - - // Parsing the search request - foreach($dom->firstChild->childNodes as $searchNode) { - - if (DAV\XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') { - $applyToPrincipalCollectionSet = true; - } - - if (DAV\XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') - continue; - - $propertyName = null; - $propertyValue = null; - - foreach($searchNode->childNodes as $childNode) { - - switch(DAV\XMLUtil::toClarkNotation($childNode)) { - - case '{DAV:}prop' : - $property = DAV\XMLUtil::parseProperties($searchNode); - reset($property); - $propertyName = key($property); - break; - - case '{DAV:}match' : - $propertyValue = $childNode->textContent; - break; - - } - - - } - - if (is_null($propertyName) || is_null($propertyValue)) - throw new DAV\Exception\BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); - - $searchProperties[$propertyName] = $propertyValue; - - } - - return array($searchProperties, array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet); - - } - - - /* }}} */ - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php deleted file mode 100644 index 7270aec39..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Principal.php +++ /dev/null @@ -1,281 +0,0 @@ -principalBackend = $principalBackend; - $this->principalProperties = $principalProperties; - - } - - /** - * Returns the full principal url - * - * @return string - */ - public function getPrincipalUrl() { - - return $this->principalProperties['uri']; - - } - - /** - * Returns a list of alternative urls for a principal - * - * This can for example be an email address, or ldap url. - * - * @return array - */ - public function getAlternateUriSet() { - - $uris = array(); - if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) { - - $uris = $this->principalProperties['{DAV:}alternate-URI-set']; - - } - - if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { - $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; - } - - return array_unique($uris); - - } - - /** - * Returns the list of group members - * - * If this principal is a group, this function should return - * all member principal uri's for the group. - * - * @return array - */ - public function getGroupMemberSet() { - - return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']); - - } - - /** - * Returns the list of groups this principal is member of - * - * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array - */ - public function getGroupMembership() { - - return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']); - - } - - - /** - * Sets a list of group members - * - * If this principal is a group, this method sets all the group members. - * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $groupMembers - * @return void - */ - public function setGroupMemberSet(array $groupMembers) { - - $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers); - - } - - - /** - * Returns this principals name. - * - * @return string - */ - public function getName() { - - $uri = $this->principalProperties['uri']; - list(, $name) = DAV\URLUtil::splitPath($uri); - return $name; - - } - - /** - * Returns the name of the user - * - * @return string - */ - public function getDisplayName() { - - if (isset($this->principalProperties['{DAV:}displayname'])) { - return $this->principalProperties['{DAV:}displayname']; - } else { - return $this->getName(); - } - - } - - /** - * Returns a list of properties - * - * @param array $requestedProperties - * @return array - */ - public function getProperties($requestedProperties) { - - $newProperties = array(); - foreach($requestedProperties as $propName) { - - if (isset($this->principalProperties[$propName])) { - $newProperties[$propName] = $this->principalProperties[$propName]; - } - - } - - return $newProperties; - - } - - /** - * Updates this principals properties. - * - * @param array $mutations - * @see Sabre\DAV\IProperties::updateProperties - * @return bool|array - */ - public function updateProperties($mutations) { - - return $this->principalBackend->updatePrincipal($this->principalProperties['uri'], $mutations); - - } - - /** - * Returns the owner principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getOwner() { - - return $this->principalProperties['uri']; - - - } - - /** - * Returns a group principal - * - * This must be a url to a principal, or null if there's no owner - * - * @return string|null - */ - public function getGroup() { - - return null; - - } - - /** - * Returns a list of ACE's for this node. - * - * Each ACE has the following properties: - * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are - * currently the only supported privileges - * * 'principal', a url to the principal who owns the node - * * 'protected' (optional), indicating that this ACE is not allowed to - * be updated. - * - * @return array - */ - public function getACL() { - - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->getPrincipalUrl(), - 'protected' => true, - ), - ); - - } - - /** - * Updates the ACL - * - * This method will receive a list of new ACE's. - * - * @param array $acl - * @return void - */ - public function setACL(array $acl) { - - throw new DAV\Exception\MethodNotAllowed('Updating ACLs is not allowed here'); - - } - - /** - * Returns the list of supported privileges for this node. - * - * The returned data structure is a list of nested privileges. - * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple - * standard structure. - * - * If null is returned from this method, the default privilege set is used, - * which is fine for most common usecases. - * - * @return array|null - */ - public function getSupportedPrivilegeSet() { - - return null; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php deleted file mode 100644 index b898c790f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/AbstractBackend.php +++ /dev/null @@ -1,18 +0,0 @@ - array( - * '{DAV:}prop1' => null, - * ), - * 201 => array( - * '{DAV:}prop2' => null, - * ), - * 403 => array( - * '{DAV:}prop3' => null, - * ), - * 424 => array( - * '{DAV:}prop4' => null, - * ), - * ); - * - * In this previous example prop1 was successfully updated or deleted, and - * prop2 was succesfully created. - * - * prop3 failed to update due to '403 Forbidden' and because of this prop4 - * also could not be updated with '424 Failed dependency'. - * - * This last example was actually incorrect. While 200 and 201 could appear - * in 1 response, if there's any error (403) the other properties should - * always fail with 423 (failed dependency). - * - * But anyway, if you don't want to scratch your head over this, just - * return true or false. - * - * @param string $path - * @param array $mutations - * @return array|bool - */ - function updatePrincipal($path, $mutations); - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return an array with full principal uri's. - * - * If somebody attempted to search on a property the backend does not - * support, you should simply return 0 results. - * - * You can also just return 0 results if you choose to not support - * searching at all, but keep in mind that this may stop certain features - * from working. - * - * @param string $prefixPath - * @param array $searchProperties - * @return array - */ - function searchPrincipals($prefixPath, array $searchProperties); - - /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array - */ - function getGroupMemberSet($principal); - - /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array - */ - function getGroupMembership($principal); - - /** - * Updates the list of group members for a group principal. - * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members - * @return void - */ - function setGroupMemberSet($principal, array $members); - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/PDO.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/PDO.php deleted file mode 100644 index 9390e8160..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalBackend/PDO.php +++ /dev/null @@ -1,428 +0,0 @@ - array( - 'dbField' => 'displayname', - ), - - /** - * This property is actually used by the CardDAV plugin, where it gets - * mapped to {http://calendarserver.orgi/ns/}me-card. - * - * The reason we don't straight-up use that property, is because - * me-card is defined as a property on the users' addressbook - * collection. - */ - '{http://sabredav.org/ns}vcard-url' => array( - 'dbField' => 'vcardurl', - ), - /** - * This is the users' primary email-address. - */ - '{http://sabredav.org/ns}email-address' => array( - 'dbField' => 'email', - ), - ); - - /** - * Sets up the backend. - * - * @param PDO $pdo - * @param string $tableName - * @param string $groupMembersTableName - */ - public function __construct(\PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') { - - $this->pdo = $pdo; - $this->tableName = $tableName; - $this->groupMembersTableName = $groupMembersTableName; - - } - - - /** - * Returns a list of principals based on a prefix. - * - * This prefix will often contain something like 'principals'. You are only - * expected to return principals that are in this base path. - * - * You are expected to return at least a 'uri' for every user, you can - * return any additional properties if you wish so. Common properties are: - * {DAV:}displayname - * {http://sabredav.org/ns}email-address - This is a custom SabreDAV - * field that's actualy injected in a number of other properties. If - * you have an email address, use this property. - * - * @param string $prefixPath - * @return array - */ - public function getPrincipalsByPrefix($prefixPath) { - - $fields = array( - 'uri', - ); - - foreach($this->fieldMap as $key=>$value) { - $fields[] = $value['dbField']; - } - $result = $this->pdo->query('SELECT '.implode(',', $fields).' FROM '. $this->tableName); - - $principals = array(); - - while($row = $result->fetch(\PDO::FETCH_ASSOC)) { - - // Checking if the principal is in the prefix - list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']); - if ($rowPrefix !== $prefixPath) continue; - - $principal = array( - 'uri' => $row['uri'], - ); - foreach($this->fieldMap as $key=>$value) { - if ($row[$value['dbField']]) { - $principal[$key] = $row[$value['dbField']]; - } - } - $principals[] = $principal; - - } - - return $principals; - - } - - /** - * Returns a specific principal, specified by it's path. - * The returned structure should be the exact same as from - * getPrincipalsByPrefix. - * - * @param string $path - * @return array - */ - public function getPrincipalByPath($path) { - - $fields = array( - 'id', - 'uri', - ); - - foreach($this->fieldMap as $key=>$value) { - $fields[] = $value['dbField']; - } - $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?'); - $stmt->execute(array($path)); - - $row = $stmt->fetch(\PDO::FETCH_ASSOC); - if (!$row) return; - - $principal = array( - 'id' => $row['id'], - 'uri' => $row['uri'], - ); - foreach($this->fieldMap as $key=>$value) { - if ($row[$value['dbField']]) { - $principal[$key] = $row[$value['dbField']]; - } - } - return $principal; - - } - - /** - * Updates one ore more webdav properties on a principal. - * - * The list of mutations is supplied as an array. Each key in the array is - * a propertyname, such as {DAV:}displayname. - * - * Each value is the actual value to be updated. If a value is null, it - * must be deleted. - * - * This method should be atomic. It must either completely succeed, or - * completely fail. Success and failure can simply be returned as 'true' or - * 'false'. - * - * It is also possible to return detailed failure information. In that case - * an array such as this should be returned: - * - * array( - * 200 => array( - * '{DAV:}prop1' => null, - * ), - * 201 => array( - * '{DAV:}prop2' => null, - * ), - * 403 => array( - * '{DAV:}prop3' => null, - * ), - * 424 => array( - * '{DAV:}prop4' => null, - * ), - * ); - * - * In this previous example prop1 was successfully updated or deleted, and - * prop2 was succesfully created. - * - * prop3 failed to update due to '403 Forbidden' and because of this prop4 - * also could not be updated with '424 Failed dependency'. - * - * This last example was actually incorrect. While 200 and 201 could appear - * in 1 response, if there's any error (403) the other properties should - * always fail with 423 (failed dependency). - * - * But anyway, if you don't want to scratch your head over this, just - * return true or false. - * - * @param string $path - * @param array $mutations - * @return array|bool - */ - public function updatePrincipal($path, $mutations) { - - $updateAble = array(); - foreach($mutations as $key=>$value) { - - // We are not aware of this field, we must fail. - if (!isset($this->fieldMap[$key])) { - - $response = array( - 403 => array( - $key => null, - ), - 424 => array(), - ); - - // Adding the rest to the response as a 424 - foreach($mutations as $subKey=>$subValue) { - if ($subKey !== $key) { - $response[424][$subKey] = null; - } - } - return $response; - } - - $updateAble[$this->fieldMap[$key]['dbField']] = $value; - - } - - // No fields to update - $query = "UPDATE " . $this->tableName . " SET "; - - $first = true; - foreach($updateAble as $key => $value) { - if (!$first) { - $query.= ', '; - } - $first = false; - $query.= "$key = :$key "; - } - $query.='WHERE uri = :uri'; - $stmt = $this->pdo->prepare($query); - $updateAble['uri'] = $path; - $stmt->execute($updateAble); - - return true; - - } - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return an array with full principal uri's. - * - * If somebody attempted to search on a property the backend does not - * support, you should simply return 0 results. - * - * You can also just return 0 results if you choose to not support - * searching at all, but keep in mind that this may stop certain features - * from working. - * - * @param string $prefixPath - * @param array $searchProperties - * @return array - */ - public function searchPrincipals($prefixPath, array $searchProperties) { - - $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 '; - $values = array(); - foreach($searchProperties as $property => $value) { - - switch($property) { - - case '{DAV:}displayname' : - $query.=' AND displayname LIKE ?'; - $values[] = '%' . $value . '%'; - break; - case '{http://sabredav.org/ns}email-address' : - $query.=' AND email LIKE ?'; - $values[] = '%' . $value . '%'; - break; - default : - // Unsupported property - return array(); - - } - - } - $stmt = $this->pdo->prepare($query); - $stmt->execute($values); - - $principals = array(); - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - - // Checking if the principal is in the prefix - list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']); - if ($rowPrefix !== $prefixPath) continue; - - $principals[] = $row['uri']; - - } - - return $principals; - - } - - /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array - */ - public function getGroupMemberSet($principal) { - - $principal = $this->getPrincipalByPath($principal); - if (!$principal) throw new DAV\Exception('Principal not found'); - - $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); - $stmt->execute(array($principal['id'])); - - $result = array(); - while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $result[] = $row['uri']; - } - return $result; - - } - - /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array - */ - public function getGroupMembership($principal) { - - $principal = $this->getPrincipalByPath($principal); - if (!$principal) throw new DAV\Exception('Principal not found'); - - $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); - $stmt->execute(array($principal['id'])); - - $result = array(); - while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $result[] = $row['uri']; - } - return $result; - - } - - /** - * Updates the list of group members for a group principal. - * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members - * @return void - */ - public function setGroupMemberSet($principal, array $members) { - - // Grabbing the list of principal id's. - $stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); - $stmt->execute(array_merge(array($principal), $members)); - - $memberIds = array(); - $principalId = null; - - while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - if ($row['uri'] == $principal) { - $principalId = $row['id']; - } else { - $memberIds[] = $row['id']; - } - } - if (!$principalId) throw new DAV\Exception('Principal not found'); - - // Wiping out old members - $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;'); - $stmt->execute(array($principalId)); - - foreach($memberIds as $memberId) { - - $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);'); - $stmt->execute(array($principalId, $memberId)); - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalCollection.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalCollection.php deleted file mode 100644 index 059284b20..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/PrincipalCollection.php +++ /dev/null @@ -1,33 +0,0 @@ -principalBackend, $principal); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Acl.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Acl.php deleted file mode 100644 index 1afcc3898..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Acl.php +++ /dev/null @@ -1,211 +0,0 @@ -privileges = $privileges; - $this->prefixBaseUrl = $prefixBaseUrl; - - } - - /** - * Returns the list of privileges for this property - * - * @return array - */ - public function getPrivileges() { - - return $this->privileges; - - } - - /** - * Serializes the property into a DOMElement - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - foreach($this->privileges as $ace) { - - $this->serializeAce($doc, $node, $ace, $server); - - } - - } - - /** - * Unserializes the {DAV:}acl xml element. - * - * @param \DOMElement $dom - * @return Acl - */ - static public function unserialize(\DOMElement $dom) { - - $privileges = array(); - $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace'); - for($ii=0; $ii < $xaces->length; $ii++) { - - $xace = $xaces->item($ii); - $principal = $xace->getElementsByTagNameNS('urn:DAV','principal'); - if ($principal->length !== 1) { - throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); - } - $principal = Principal::unserialize($principal->item(0)); - - switch($principal->getType()) { - case Principal::HREF : - $principal = $principal->getHref(); - break; - case Principal::AUTHENTICATED : - $principal = '{DAV:}authenticated'; - break; - case Principal::UNAUTHENTICATED : - $principal = '{DAV:}unauthenticated'; - break; - case Principal::ALL : - $principal = '{DAV:}all'; - break; - - } - - $protected = false; - - if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { - $protected = true; - } - - $grants = $xace->getElementsByTagNameNS('urn:DAV','grant'); - if ($grants->length < 1) { - throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); - } - $grant = $grants->item(0); - - $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege'); - for($jj=0; $jj<$xprivs->length; $jj++) { - - $xpriv = $xprivs->item($jj); - - $privilegeName = null; - - for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { - - $childNode = $xpriv->childNodes->item($kk); - if ($t = DAV\XMLUtil::toClarkNotation($childNode)) { - $privilegeName = $t; - break; - } - } - if (is_null($privilegeName)) { - throw new DAV\Exception\BadRequest('{DAV:}privilege elements must have a privilege element contained within them.'); - } - - $privileges[] = array( - 'principal' => $principal, - 'protected' => $protected, - 'privilege' => $privilegeName, - ); - - } - - } - - return new self($privileges); - - } - - /** - * Serializes a single access control entry. - * - * @param \DOMDocument $doc - * @param \DOMElement $node - * @param array $ace - * @param DAV\Server $server - * @return void - */ - private function serializeAce($doc,$node,$ace, DAV\Server $server) { - - $xace = $doc->createElementNS('DAV:','d:ace'); - $node->appendChild($xace); - - $principal = $doc->createElementNS('DAV:','d:principal'); - $xace->appendChild($principal); - switch($ace['principal']) { - case '{DAV:}authenticated' : - $principal->appendChild($doc->createElementNS('DAV:','d:authenticated')); - break; - case '{DAV:}unauthenticated' : - $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated')); - break; - case '{DAV:}all' : - $principal->appendChild($doc->createElementNS('DAV:','d:all')); - break; - default: - $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/')); - } - - $grant = $doc->createElementNS('DAV:','d:grant'); - $xace->appendChild($grant); - - $privParts = null; - - preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts); - - $xprivilege = $doc->createElementNS('DAV:','d:privilege'); - $grant->appendChild($xprivilege); - - $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); - - if (isset($ace['protected']) && $ace['protected']) - $xace->appendChild($doc->createElement('d:protected')); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/AclRestrictions.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/AclRestrictions.php deleted file mode 100644 index 6fcc4c1ca..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/AclRestrictions.php +++ /dev/null @@ -1,34 +0,0 @@ -ownerDocument; - - $elem->appendChild($doc->createElementNS('DAV:','d:grant-only')); - $elem->appendChild($doc->createElementNS('DAV:','d:no-invert')); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php deleted file mode 100644 index 69e04516d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php +++ /dev/null @@ -1,124 +0,0 @@ -privileges = $privileges; - - } - - /** - * Serializes the property in the DOM - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - foreach($this->privileges as $privName) { - - $this->serializePriv($doc,$node,$privName); - - } - - } - - /** - * Returns true or false, whether the specified principal appears in the - * list. - * - * @return bool - */ - public function has($privilegeName) { - - return in_array($privilegeName, $this->privileges); - - } - - /** - * Serializes one privilege - * - * @param \DOMDocument $doc - * @param \DOMElement $node - * @param string $privName - * @return void - */ - protected function serializePriv($doc,$node,$privName) { - - $xp = $doc->createElementNS('DAV:','d:privilege'); - $node->appendChild($xp); - - $privParts = null; - preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts); - - $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); - - } - - /** - * Unserializes the {DAV:}current-user-privilege-set element. - * - * @param DOMElement $node - * @return CurrentUserPrivilegeSet - */ - static public function unserialize(\DOMElement $node) { - - $result = array(); - - $xprivs = $node->getElementsByTagNameNS('urn:DAV','privilege'); - - for($jj=0; $jj<$xprivs->length; $jj++) { - - $xpriv = $xprivs->item($jj); - - $privilegeName = null; - - for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { - - $childNode = $xpriv->childNodes->item($kk); - if ($t = DAV\XMLUtil::toClarkNotation($childNode)) { - $privilegeName = $t; - break; - } - } - - $result[] = $privilegeName; - - } - - return new self($result); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Principal.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Principal.php deleted file mode 100644 index 56de8456f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/Principal.php +++ /dev/null @@ -1,161 +0,0 @@ -type = $type; - - if ($type===self::HREF && is_null($href)) { - throw new DAV\Exception('The href argument must be specified for the HREF principal type.'); - } - $this->href = $href; - - } - - /** - * Returns the principal type - * - * @return int - */ - public function getType() { - - return $this->type; - - } - - /** - * Returns the principal uri. - * - * @return string - */ - public function getHref() { - - return $this->href; - - } - - /** - * Serializes the property into a DOMElement. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server, \DOMElement $node) { - - $prefix = $server->xmlNamespaces['DAV:']; - switch($this->type) { - - case self::UNAUTHENTICATED : - $node->appendChild( - $node->ownerDocument->createElement($prefix . ':unauthenticated') - ); - break; - case self::AUTHENTICATED : - $node->appendChild( - $node->ownerDocument->createElement($prefix . ':authenticated') - ); - break; - case self::HREF : - $href = $node->ownerDocument->createElement($prefix . ':href'); - $href->nodeValue = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href); - $node->appendChild($href); - break; - - } - - } - - /** - * Deserializes a DOM element into a property object. - * - * @param \DOMElement $dom - * @return Principal - */ - static public function unserialize(\DOMElement $dom) { - - $parent = $dom->firstChild; - while(!DAV\XMLUtil::toClarkNotation($parent)) { - $parent = $parent->nextSibling; - } - - switch(DAV\XMLUtil::toClarkNotation($parent)) { - - case '{DAV:}unauthenticated' : - return new self(self::UNAUTHENTICATED); - case '{DAV:}authenticated' : - return new self(self::AUTHENTICATED); - case '{DAV:}href': - return new self(self::HREF, $parent->textContent); - case '{DAV:}all': - return new self(self::ALL); - default : - throw new DAV\Exception\BadRequest('Unexpected element (' . DAV\XMLUtil::toClarkNotation($parent) . '). Could not deserialize'); - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/SupportedPrivilegeSet.php deleted file mode 100644 index ad717487c..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Property/SupportedPrivilegeSet.php +++ /dev/null @@ -1,94 +0,0 @@ -privileges = $privileges; - - } - - /** - * Serializes the property into a domdocument. - * - * @param DAV\Server $server - * @param \DOMElement $node - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $node) { - - $doc = $node->ownerDocument; - $this->serializePriv($doc, $node, $this->privileges); - - } - - /** - * Serializes a property - * - * This is a recursive function. - * - * @param \DOMDocument $doc - * @param \DOMElement $node - * @param array $privilege - * @return void - */ - private function serializePriv($doc,$node,$privilege) { - - $xsp = $doc->createElementNS('DAV:','d:supported-privilege'); - $node->appendChild($xsp); - - $xp = $doc->createElementNS('DAV:','d:privilege'); - $xsp->appendChild($xp); - - $privParts = null; - preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts); - - $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); - - if (isset($privilege['abstract']) && $privilege['abstract']) { - $xsp->appendChild($doc->createElementNS('DAV:','d:abstract')); - } - - if (isset($privilege['description'])) { - $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description'])); - } - - if (isset($privilege['aggregates'])) { - foreach($privilege['aggregates'] as $subPrivilege) { - $this->serializePriv($doc,$xsp,$subPrivilege); - } - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Version.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Version.php deleted file mode 100644 index b2bacbdb8..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAVACL/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -httpRequest->getHeader('Authorization'); - $authHeader = explode(' ',$authHeader); - - if ($authHeader[0]!='AWS' || !isset($authHeader[1])) { - $this->errorCode = self::ERR_NOAWSHEADER; - return false; - } - - list($this->accessKey,$this->signature) = explode(':',$authHeader[1]); - - return true; - - } - - /** - * Returns the username for the request - * - * @return string - */ - public function getAccessKey() { - - return $this->accessKey; - - } - - /** - * Validates the signature based on the secretKey - * - * @param string $secretKey - * @return bool - */ - public function validate($secretKey) { - - $contentMD5 = $this->httpRequest->getHeader('Content-MD5'); - - if ($contentMD5) { - // We need to validate the integrity of the request - $body = $this->httpRequest->getBody(true); - $this->httpRequest->setBody($body,true); - - if ($contentMD5!=base64_encode(md5($body,true))) { - // content-md5 header did not match md5 signature of body - $this->errorCode = self::ERR_MD5CHECKSUMWRONG; - return false; - } - - } - - if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) - $requestDate = $this->httpRequest->getHeader('Date'); - - if (!$this->validateRFC2616Date($requestDate)) - return false; - - $amzHeaders = $this->getAmzHeaders(); - - $signature = base64_encode( - $this->hmacsha1($secretKey, - $this->httpRequest->getMethod() . "\n" . - $contentMD5 . "\n" . - $this->httpRequest->getHeader('Content-type') . "\n" . - $requestDate . "\n" . - $amzHeaders . - $this->httpRequest->getURI() - ) - ); - - if ($this->signature != $signature) { - - $this->errorCode = self::ERR_INVALIDSIGNATURE; - return false; - - } - - return true; - - } - - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - public function requireLogin() { - - $this->httpResponse->setHeader('WWW-Authenticate','AWS'); - $this->httpResponse->sendStatus(401); - - } - - /** - * Makes sure the supplied value is a valid RFC2616 date. - * - * If we would just use strtotime to get a valid timestamp, we have no way of checking if a - * user just supplied the word 'now' for the date header. - * - * This function also makes sure the Date header is within 15 minutes of the operating - * system date, to prevent replay attacks. - * - * @param string $dateHeader - * @return bool - */ - protected function validateRFC2616Date($dateHeader) { - - $date = Util::parseHTTPDate($dateHeader); - - // Unknown format - if (!$date) { - $this->errorCode = self::ERR_INVALIDDATEFORMAT; - return false; - } - - $min = new \DateTime('-15 minutes'); - $max = new \DateTime('+15 minutes'); - - // We allow 15 minutes around the current date/time - if ($date > $max || $date < $min) { - $this->errorCode = self::ERR_REQUESTTIMESKEWED; - return false; - } - - return $date; - - } - - /** - * Returns a list of AMZ headers - * - * @return string - */ - protected function getAmzHeaders() { - - $amzHeaders = array(); - $headers = $this->httpRequest->getHeaders(); - foreach($headers as $headerName => $headerValue) { - if (strpos(strtolower($headerName),'x-amz-')===0) { - $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n"; - } - } - ksort($amzHeaders); - - $headerStr = ''; - foreach($amzHeaders as $h=>$v) { - $headerStr.=$h.':'.$v; - } - - return $headerStr; - - } - - /** - * Generates an HMAC-SHA1 signature - * - * @param string $key - * @param string $message - * @return string - */ - private function hmacsha1($key, $message) { - - $blocksize=64; - if (strlen($key)>$blocksize) - $key=pack('H*', sha1($key)); - $key=str_pad($key,$blocksize,chr(0x00)); - $ipad=str_repeat(chr(0x36),$blocksize); - $opad=str_repeat(chr(0x5c),$blocksize); - $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message)))); - return $hmac; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/AbstractAuth.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/AbstractAuth.php deleted file mode 100644 index b5583b87f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/AbstractAuth.php +++ /dev/null @@ -1,111 +0,0 @@ -httpResponse = new Response(); - $this->httpRequest = new Request(); - - } - - /** - * Sets an alternative HTTP response object - * - * @param Response $response - * @return void - */ - public function setHTTPResponse(Response $response) { - - $this->httpResponse = $response; - - } - - /** - * Sets an alternative HTTP request object - * - * @param Request $request - * @return void - */ - public function setHTTPRequest(Request $request) { - - $this->httpRequest = $request; - - } - - - /** - * Sets the realm - * - * The realm is often displayed in authentication dialog boxes - * Commonly an application name displayed here - * - * @param string $realm - * @return void - */ - public function setRealm($realm) { - - $this->realm = $realm; - - } - - /** - * Returns the realm - * - * @return string - */ - public function getRealm() { - - return $this->realm; - - } - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - abstract public function requireLogin(); - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/BasicAuth.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/BasicAuth.php deleted file mode 100644 index 18fe1638b..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/BasicAuth.php +++ /dev/null @@ -1,67 +0,0 @@ -httpRequest->getRawServerValue('PHP_AUTH_USER'))!==null && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))!==null) { - - return array($user,$pass); - - } - - // Most other webservers - $auth = $this->httpRequest->getHeader('Authorization'); - - // Apache could prefix environment variables with REDIRECT_ when urls - // are passed through mod_rewrite - if (!$auth) { - $auth = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); - } - - if (!$auth) return false; - - if (strpos(strtolower($auth),'basic')!==0) return false; - - return explode(':', base64_decode(substr($auth, 6)),2); - - } - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - public function requireLogin() { - - $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); - $this->httpResponse->sendStatus(401); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php deleted file mode 100644 index c15273939..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/DigestAuth.php +++ /dev/null @@ -1,240 +0,0 @@ -nonce = uniqid(); - $this->opaque = md5($this->realm); - parent::__construct(); - - } - - /** - * Gathers all information from the headers - * - * This method needs to be called prior to anything else. - * - * @return void - */ - public function init() { - - $digest = $this->getDigest(); - $this->digestParts = $this->parseDigest($digest); - - } - - /** - * Sets the quality of protection value. - * - * Possible values are: - * Sabre\HTTP\DigestAuth::QOP_AUTH - * Sabre\HTTP\DigestAuth::QOP_AUTHINT - * - * Multiple values can be specified using logical OR. - * - * QOP_AUTHINT ensures integrity of the request body, but this is not - * supported by most HTTP clients. QOP_AUTHINT also requires the entire - * request body to be md5'ed, which can put strains on CPU and memory. - * - * @param int $qop - * @return void - */ - public function setQOP($qop) { - - $this->qop = $qop; - - } - - /** - * Validates the user. - * - * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); - * - * @param string $A1 - * @return bool - */ - public function validateA1($A1) { - - $this->A1 = $A1; - return $this->validate(); - - } - - /** - * Validates authentication through a password. The actual password must be provided here. - * It is strongly recommended not store the password in plain-text and use validateA1 instead. - * - * @param string $password - * @return bool - */ - public function validatePassword($password) { - - $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); - return $this->validate(); - - } - - /** - * Returns the username for the request - * - * @return string - */ - public function getUsername() { - - return $this->digestParts['username']; - - } - - /** - * Validates the digest challenge - * - * @return bool - */ - protected function validate() { - - $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri']; - - if ($this->digestParts['qop']=='auth-int') { - // Making sure we support this qop value - if (!($this->qop & self::QOP_AUTHINT)) return false; - // We need to add an md5 of the entire request body to the A2 part of the hash - $body = $this->httpRequest->getBody(true); - $this->httpRequest->setBody($body,true); - $A2 .= ':' . md5($body); - } else { - - // We need to make sure we support this qop value - if (!($this->qop & self::QOP_AUTH)) return false; - } - - $A2 = md5($A2); - - $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); - - return $this->digestParts['response']==$validResponse; - - - } - - /** - * Returns an HTTP 401 header, forcing login - * - * This should be called when username and password are incorrect, or not supplied at all - * - * @return void - */ - public function requireLogin() { - - $qop = ''; - switch($this->qop) { - case self::QOP_AUTH : $qop = 'auth'; break; - case self::QOP_AUTHINT : $qop = 'auth-int'; break; - case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break; - } - - $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); - $this->httpResponse->sendStatus(401); - - } - - - /** - * This method returns the full digest string. - * - * It should be compatibile with mod_php format and other webservers. - * - * If the header could not be found, null will be returned - * - * @return mixed - */ - public function getDigest() { - - // mod_php - $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST'); - if ($digest) return $digest; - - // most other servers - $digest = $this->httpRequest->getHeader('Authorization'); - - // Apache could prefix environment variables with REDIRECT_ when urls - // are passed through mod_rewrite - if (!$digest) { - $digest = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); - } - - if ($digest && strpos(strtolower($digest),'digest')===0) { - return substr($digest,7); - } else { - return null; - } - - } - - - /** - * Parses the different pieces of the digest string into an array. - * - * This method returns false if an incomplete digest was supplied - * - * @param string $digest - * @return mixed - */ - protected function parseDigest($digest) { - - // protect against missing data - $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); - $data = array(); - - preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); - - foreach ($matches as $m) { - $data[$m[1]] = $m[2] ? $m[2] : $m[3]; - unset($needed_parts[$m[1]]); - } - - return $needed_parts ? false : $data; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Request.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Request.php deleted file mode 100644 index e8baca04f..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Request.php +++ /dev/null @@ -1,284 +0,0 @@ -_SERVER = $serverData; - else $this->_SERVER =& $_SERVER; - - if ($postData) $this->_POST = $postData; - else $this->_POST =& $_POST; - - } - - /** - * Returns the value for a specific http header. - * - * This method returns null if the header did not exist. - * - * @param string $name - * @return string - */ - public function getHeader($name) { - - $name = strtoupper(str_replace(array('-'),array('_'),$name)); - if (isset($this->_SERVER['HTTP_' . $name])) { - return $this->_SERVER['HTTP_' . $name]; - } - - // There's a few headers that seem to end up in the top-level - // server array. - switch($name) { - case 'CONTENT_TYPE' : - case 'CONTENT_LENGTH' : - if (isset($this->_SERVER[$name])) { - return $this->_SERVER[$name]; - } - break; - - } - return; - - } - - /** - * Returns all (known) HTTP headers. - * - * All headers are converted to lower-case, and additionally all underscores - * are automatically converted to dashes - * - * @return array - */ - public function getHeaders() { - - $hdrs = array(); - foreach($this->_SERVER as $key=>$value) { - - switch($key) { - case 'CONTENT_LENGTH' : - case 'CONTENT_TYPE' : - $hdrs[strtolower(str_replace('_','-',$key))] = $value; - break; - default : - if (strpos($key,'HTTP_')===0) { - $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value; - } - break; - } - - } - - return $hdrs; - - } - - /** - * Returns the HTTP request method - * - * This is for example POST or GET - * - * @return string - */ - public function getMethod() { - - return $this->_SERVER['REQUEST_METHOD']; - - } - - /** - * Returns the requested uri - * - * @return string - */ - public function getUri() { - - return $this->_SERVER['REQUEST_URI']; - - } - - /** - * Will return protocol + the hostname + the uri - * - * @return string - */ - public function getAbsoluteUri() { - - // Checking if the request was made through HTTPS. The last in line is for IIS - $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off'); - return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri(); - - } - - /** - * Returns everything after the ? from the current url - * - * @return string - */ - public function getQueryString() { - - return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:''; - - } - - /** - * Returns the HTTP request body body - * - * This method returns a readable stream resource. - * If the asString parameter is set to true, a string is sent instead. - * - * @param bool $asString - * @return resource - */ - public function getBody($asString = false) { - - if (is_null($this->body)) { - if (!is_null(self::$defaultInputStream)) { - $this->body = self::$defaultInputStream; - } else { - $this->body = fopen('php://input','r'); - self::$defaultInputStream = $this->body; - } - } - if ($asString) { - $body = stream_get_contents($this->body); - return $body; - } else { - return $this->body; - } - - } - - /** - * Sets the contents of the HTTP request body - * - * This method can either accept a string, or a readable stream resource. - * - * If the setAsDefaultInputStream is set to true, it means for this run of the - * script the supplied body will be used instead of php://input. - * - * @param mixed $body - * @param bool $setAsDefaultInputStream - * @return void - */ - public function setBody($body,$setAsDefaultInputStream = false) { - - if(is_resource($body)) { - $this->body = $body; - } else { - - $stream = fopen('php://temp','r+'); - fputs($stream,$body); - rewind($stream); - // String is assumed - $this->body = $stream; - } - if ($setAsDefaultInputStream) { - self::$defaultInputStream = $this->body; - } - - } - - /** - * Returns PHP's _POST variable. - * - * The reason this is in a method is so it can be subclassed and - * overridden. - * - * @return array - */ - public function getPostVars() { - - return $this->_POST; - - } - - /** - * Returns a specific item from the _SERVER array. - * - * Do not rely on this feature, it is for internal use only. - * - * @param string $field - * @return string - */ - public function getRawServerValue($field) { - - return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null; - - } - - /** - * Returns the HTTP version specified within the request. - * - * @return string - */ - public function getHTTPVersion() { - - $protocol = $this->getRawServerValue('SERVER_PROTOCOL'); - if ($protocol==='HTTP/1.0') { - return '1.0'; - } else { - return '1.1'; - } - - } - -} - diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Response.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Response.php deleted file mode 100644 index d53ddb418..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Response.php +++ /dev/null @@ -1,175 +0,0 @@ - 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authorative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', // RFC 4918 - 208 => 'Already Reported', // RFC 5842 - 226 => 'IM Used', // RFC 3229 - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 306 => 'Reserved', - 307 => 'Temporary Redirect', - 400 => 'Bad request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 418 => 'I\'m a teapot', // RFC 2324 - 422 => 'Unprocessable Entity', // RFC 4918 - 423 => 'Locked', // RFC 4918 - 424 => 'Failed Dependency', // RFC 4918 - 426 => 'Upgrade required', - 428 => 'Precondition required', // draft-nottingham-http-new-status - 429 => 'Too Many Requests', // draft-nottingham-http-new-status - 431 => 'Request Header Fields Too Large', // draft-nottingham-http-new-status - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version not supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', // RFC 4918 - 508 => 'Loop Detected', // RFC 5842 - 509 => 'Bandwidth Limit Exceeded', // non-standard - 510 => 'Not extended', - 511 => 'Network Authentication Required', // draft-nottingham-http-new-status - ); - - return 'HTTP/' . $httpVersion . ' ' . $code . ' ' . $msg[$code]; - - } - - // @codeCoverageIgnoreStart - // We cannot reasonably test header() related methods. - - /** - * Sends an HTTP status header to the client. - * - * @param int $code HTTP status code - * @return bool - */ - public function sendStatus($code) { - - if (!headers_sent()) - return header($this->getStatusMessage($code, $this->defaultHttpVersion)); - else return false; - - } - - /** - * Sets an HTTP header for the response - * - * @param string $name - * @param string $value - * @param bool $replace - * @return bool - */ - public function setHeader($name, $value, $replace = true) { - - $value = str_replace(array("\r","\n"),array('\r','\n'),$value); - if (!headers_sent()) - return header($name . ': ' . $value, $replace); - else return false; - - - } - // @codeCoverageIgnoreEnd - - /** - * Sets a bunch of HTTP Headers - * - * headersnames are specified as keys, value in the array value - * - * @param array $headers - * @return void - */ - public function setHeaders(array $headers) { - - foreach($headers as $key=>$value) - $this->setHeader($key, $value); - - } - - /** - * Sends the entire response body - * - * This method can accept either an open filestream, or a string. - * - * @param mixed $body - * @return void - */ - public function sendBody($body) { - - if (is_resource($body)) { - - file_put_contents('php://output', $body); - - } else { - - // We assume a string - echo $body; - - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Util.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Util.php deleted file mode 100644 index f03643de5..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Util.php +++ /dev/null @@ -1,82 +0,0 @@ -= 0) - return new \DateTime('@' . $realDate, new \DateTimeZone('UTC')); - - } - - /** - * Transforms a DateTime object to HTTP's most common date format. - * - * We're serializing it as the RFC 1123 date, which, for HTTP must be - * specified as GMT. - * - * @param \DateTime $dateTime - * @return string - */ - static function toHTTPDate(\DateTime $dateTime) { - - // We need to clone it, as we don't want to affect the existing - // DateTime. - $dateTime = clone $dateTime; - $dateTime->setTimeZone(new \DateTimeZone('GMT')); - return $dateTime->format('D, d M Y H:i:s \G\M\T'); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Version.php b/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Version.php deleted file mode 100644 index 5936b5778..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/HTTP/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -timeGenerator->generate($node, $clockSeq)); + break; + + case self::TYPE_DCE: + break; + + case self::TYPE_NAME: + if (!($ns instanceof UuidInterface)) { + $ns = $this->codec->decode($ns); + } + $hash = md5($ns->getBytes() . $name); + break; + + case self::TYPE_RANDOM: + $hash = bin2hex(random_bytes(16)); + break; + + case self::TYPE_NAME_SHA1: + if (!($ns instanceof UuidInterface)) { + $ns = $this->codec->decode($ns); + } + $hash = sha1($ns->getBytes() . $name); + break; + } + return $this->uuidFromHashedName($hash, $mode); +*/ + + /** + * Else generate v4 UUID (pseudo-random) + */ + + $uuid = \bin2hex(\random_bytes(16)); + + # set variant and version fields for 'true' random uuid + $uuid[12] = '4'; + $hex = '0123456789abcdef'; + $uuid[16] = $hex[(8 + (\ord($uuid[16]) & 3))]; + + return \substr($uuid, 0, 8).'-' + . \substr($uuid, 8, 4).'-' + . \substr($uuid, 12, 4).'-' + . \substr($uuid, 16, 4).'-' + . \substr($uuid, 20); + } + + public static function time() { return self::generate(self::TYPE_TIME); } + + public static function random() { return self::generate(self::TYPE_RANDOM); } + + public static function isValid($uuid) + { + return \function_exists('uuid_is_valid') + ? \uuid_is_valid($uuid) + : !!\preg_match('/^[0-9a-f]{8}\\-[0-9a-f]{4}\\-[0-9a-f]{4}\\-[0-9a-f]{4}\\-[0-9a-f]{12}$/i', $uuid); + } + +} From ebfc2a705b36635e50f49305cd1026b581d874a0 Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 6 Apr 2021 10:21:59 +0200 Subject: [PATCH 37/90] Replaced Sabre\DAV --- .../Providers/AddressBook/PdoAddressBook.php | 31 +- .../0.0.0/app/libraries/Sabre/DAV/Client.php | 656 ------------------ .../app/libraries/Sabre/DAV/Exception.php | 64 -- .../Sabre/DAV/Exception/BadRequest.php | 28 - .../Sabre/DAV/Exception/Conflict.php | 28 - .../Sabre/DAV/Exception/Forbidden.php | 27 - .../DAV/Exception/InsufficientStorage.php | 27 - .../Sabre/DAV/Exception/MethodNotAllowed.php | 45 -- .../Sabre/DAV/Exception/NotAuthenticated.php | 30 - .../Sabre/DAV/Exception/NotFound.php | 28 - .../Sabre/DAV/Exception/NotImplemented.php | 27 - .../Sabre/DAV/Exception/PaymentRequired.php | 30 - .../DAV/Exception/PreconditionFailed.php | 71 -- .../RequestedRangeNotSatisfiable.php | 31 - .../0.0.0/app/libraries/Sabre/DAV/XMLUtil.php | 191 ----- .../app/libraries/snappymail/dav/client.php | 237 +++++++ .../libraries/snappymail/http/exception.php | 84 +++ .../app/libraries/snappymail/http/request.php | 159 +++++ .../snappymail/http/request/curl.php | 115 +++ .../snappymail/http/request/socket.php | 177 +++++ .../libraries/snappymail/http/response.php | 88 +++ 21 files changed, 869 insertions(+), 1305 deletions(-) delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/BadRequest.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/Conflict.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/Forbidden.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/InsufficientStorage.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/MethodNotAllowed.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotFound.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotImplemented.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/PaymentRequired.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/PreconditionFailed.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php delete mode 100644 snappymail/v/0.0.0/app/libraries/Sabre/DAV/XMLUtil.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/http/exception.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/http/request.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/http/request/curl.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/http/request/socket.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/http/response.php diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php index f8f1aed03..e1d0f391a 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php @@ -3,6 +3,7 @@ namespace RainLoop\Providers\AddressBook; use \RainLoop\Providers\AddressBook\Enumerations\PropertyType; +use \SnappyMail\DAV\Client as DAVClient; class PdoAddressBook extends \RainLoop\Common\PdoAbstract @@ -104,7 +105,7 @@ class PdoAddressBook return $aResult; } - private function prepearRemoteSyncData($oClient, string $sPath) + private function prepearRemoteSyncData(DAVClient $oClient, string $sPath) { $mResult = false; $aResponse = null; @@ -175,7 +176,7 @@ class PdoAddressBook return $mResult; } - private function davClientRequest($oClient, string $sCmd, string $sUrl, $mData = null) : ?array + private function davClientRequest(DAVClient $oClient, string $sCmd, string $sUrl, $mData = null) : ?array { \MailSo\Base\Utils::ResetTimeLimit(); @@ -214,7 +215,7 @@ class PdoAddressBook return $aResponse; } - private function detectionPropFind(\Sabre\DAV\Client $oClient, string $sPath) : ?array + private function detectionPropFind(DAVClient $oClient, string $sPath) : ?array { $aResponse = null; @@ -239,7 +240,7 @@ class PdoAddressBook return $aResponse; } - private function getContactsPaths(\Sabre\DAV\Client $oClient, string $sUser, string $sPassword, string $sProxy = '') : array + private function getContactsPaths(DAVClient $oClient, string $sUser, string $sPassword, string $sProxy = '') : array { $aContactsPaths = array(); @@ -249,11 +250,6 @@ class PdoAddressBook // [{DAV:}current-user-principal] => /cloud/remote.php/carddav/principals/admin/ // [{urn:ietf:params:xml:ns:carddav}addressbook-home-set] => /cloud/remote.php/carddav/addressbooks/admin/ - if (!$oClient) - { - return $aContactsPaths; - } - $aResponse = $this->detectionPropFind($oClient, '/.well-known/carddav'); $sNextPath = ''; @@ -425,7 +421,7 @@ class PdoAddressBook return $aContactsPaths; } - private function checkContactsPath(\Sabre\DAV\Client $oClient, string $sPath) : bool + private function checkContactsPath(DAVClient $oClient, string $sPath) : bool { if (!$oClient) { @@ -475,7 +471,7 @@ class PdoAddressBook return $bGood; } - public function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : \Sabre\DAV\Client + public function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : DAVClient { if (!\preg_match('/^http[s]?:\/\//i', $sUrl)) { @@ -509,7 +505,7 @@ class PdoAddressBook $aSettings['proxy'] = $sProxy; } - $oClient = new \Sabre\DAV\Client($aSettings); + $oClient = new DAVClient($aSettings); $oClient->setVerifyPeer(false); $oClient->__UrlPath__ = $aUrl['path']; @@ -519,13 +515,8 @@ class PdoAddressBook return $oClient; } - public function getDavClient(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : ?\Sabre\DAV\Client + public function getDavClient(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : ?DAVClient { - if (!\class_exists('Sabre\DAV\Client')) - { - return null; - } - $aMatch = array(); $sUserAddressBookNameName = ''; @@ -538,10 +529,6 @@ class PdoAddressBook } $oClient = $this->getDavClientFromUrl($sUrl, $sUser, $sPassword, $sProxy); - if (!$oClient) - { - return null; - } $sPath = $oClient->__UrlPath__; diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php deleted file mode 100644 index 2d7ea4040..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Client.php +++ /dev/null @@ -1,656 +0,0 @@ -$validSetting = $settings[$validSetting]; - } - } - - if (isset($settings['authType'])) { - $this->authType = $settings['authType']; - } else { - $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST; - } - - $this->propertyMap['{DAV:}resourcetype'] = 'Sabre\\DAV\\Property\\ResourceType'; - - } - - /** - * Add trusted root certificates to the webdav client. - * - * The parameter certificates should be a absolute path to a file - * which contains all trusted certificates - * - * @param string $certificates - */ - public function addTrustedCertificates($certificates) { - $this->trustedCertificates = $certificates; - } - - /** - * Enables/disables SSL peer verification - * - * @param boolean $value - */ - public function setVerifyPeer($value) { - $this->verifyPeer = $value; - } - - /** - * Does a PROPFIND request - * - * The list of requested properties must be specified as an array, in clark - * notation. - * - * The returned array will contain a list of filenames as keys, and - * properties as values. - * - * The properties array will contain the list of properties. Only properties - * that are actually returned from the server (without error) will be - * returned, anything else is discarded. - * - * Depth should be either 0 or 1. A depth of 1 will cause a request to be - * made to the server to also return all child resources. - * - * @param string $url - * @param array $properties - * @param int $depth - * @return array - */ - public function propFind($url, array $properties, $depth = 0) { - - $body = '' . "\n"; - $body.= '' . "\n"; - $body.= ' ' . "\n"; - - foreach($properties as $property) { - - list( - $namespace, - $elementName - ) = XMLUtil::parseClarkNotation($property); - - if ($namespace === 'DAV:') { - $body.=' ' . "\n"; - } else { - $body.=" \n"; - } - - } - - $body.= ' ' . "\n"; - $body.= ''; - - $response = $this->request('PROPFIND', $url, $body, array( - 'Depth' => $depth, - 'Content-Type' => 'application/xml' - )); - - $result = $this->parseMultiStatus($response['body']); - - // If depth was 0, we only return the top item - if ($depth===0) { - reset($result); - $result = current($result); - return isset($result[200])?$result[200]:array(); - } - - $newResult = array(); - foreach($result as $href => $statusList) { - - $newResult[$href] = isset($statusList[200])?$statusList[200]:array(); - - } - - return $newResult; - - } - - /** - * Updates a list of properties on the server - * - * The list of properties must have clark-notation properties for the keys, - * and the actual (string) value for the value. If the value is null, an - * attempt is made to delete the property. - * - * @todo Must be building the request using the DOM, and does not yet - * support complex properties. - * @param string $url - * @param array $properties - * @return void - */ - public function propPatch($url, array $properties) { - - $body = '' . "\n"; - $body.= '' . "\n"; - - foreach($properties as $propName => $propValue) { - - list( - $namespace, - $elementName - ) = XMLUtil::parseClarkNotation($propName); - - if ($propValue === null) { - - $body.="\n"; - - if ($namespace === 'DAV:') { - $body.=' ' . "\n"; - } else { - $body.=" \n"; - } - - $body.="\n"; - - } else { - - $body.="\n"; - if ($namespace === 'DAV:') { - $body.=' '; - } else { - $body.=" "; - } - // Shitty.. i know - $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); - if ($namespace === 'DAV:') { - $body.='' . "\n"; - } else { - $body.="\n"; - } - $body.="\n"; - - } - - } - - $body.= ''; - - $this->request('PROPPATCH', $url, $body, array( - 'Content-Type' => 'application/xml' - )); - - } - - /** - * Performs an HTTP options request - * - * This method returns all the features from the 'DAV:' header as an array. - * If there was no DAV header, or no contents this method will return an - * empty array. - * - * @return array - */ - public function options() { - - $result = $this->request('OPTIONS'); - if (!isset($result['headers']['dav'])) { - return array(); - } - - $features = explode(',', $result['headers']['dav']); - foreach($features as &$v) { - $v = trim($v); - } - return $features; - - } - - /** - * Performs an actual HTTP request, and returns the result. - * - * If the specified url is relative, it will be expanded based on the base - * url. - * - * The returned array contains 3 keys: - * * body - the response body - * * httpCode - a HTTP code (200, 404, etc) - * * headers - a list of response http headers. The header names have - * been lowercased. - * - * @param string $method - * @param string $url - * @param string $body - * @param array $headers - * @return array - */ - public function request($method, $url = '', $body = null, $headers = array()) { - - $url = $this->getAbsoluteUrl($url); - - $curlSettings = array( - CURLOPT_RETURNTRANSFER => true, - // Return headers as part of the response - CURLOPT_HEADER => true, - - // For security we cast this to a string. If somehow an array could - // be passed here, it would be possible for an attacker to use @ to - // post local files. - CURLOPT_POSTFIELDS => (string)$body, - CURLOPT_USERAGENT => 'SnappyMail DAV Client', // TODO rainloop - // Automatically follow redirects - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_MAXREDIRS => 5, - CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, - CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, - ); - - if($this->verifyPeer !== null) { - $curlSettings[CURLOPT_SSL_VERIFYPEER] = $this->verifyPeer; - // TODO rainloop - if (!$this->verifyPeer) { - $curlSettings[CURLOPT_SSL_VERIFYHOST] = 0; - } - // END rainloop - } - - if($this->trustedCertificates) { - $curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates; - } - - switch ($method) { - case 'HEAD' : - - // do not read body with HEAD requests (this is necessary because cURL does not ignore the body with HEAD - // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP - // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with - // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the - // response body - $curlSettings[CURLOPT_NOBODY] = true; - $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; - break; - - default: - $curlSettings[CURLOPT_CUSTOMREQUEST] = $method; - break; - - } - - // Adding HTTP headers - $nHeaders = array(); - foreach($headers as $key=>$value) { - - $nHeaders[] = $key . ': ' . $value; - - } - $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders; - - if ($this->proxy) { - $curlSettings[CURLOPT_PROXY] = $this->proxy; - } - - if ($this->userName && $this->authType) { - $curlType = 0; - if ($this->authType & self::AUTH_BASIC) { - $curlType |= CURLAUTH_BASIC; - } - if ($this->authType & self::AUTH_DIGEST) { - $curlType |= CURLAUTH_DIGEST; - } - $curlSettings[CURLOPT_HTTPAUTH] = $curlType; - $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password; - } - - list( - $response, - $curlInfo, - $curlErrNo, - $curlError - ) = $this->curlRequest($url, $curlSettings); - - $headerBlob = substr($response, 0, $curlInfo['header_size']); - $response = substr($response, $curlInfo['header_size']); - - // In the case of 100 Continue, or redirects we'll have multiple lists - // of headers for each separate HTTP response. We can easily split this - // because they are separated by \r\n\r\n - $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n")); - - // We only care about the last set of headers - $headerBlob = $headerBlob[count($headerBlob)-1]; - - // Splitting headers - $headerBlob = explode("\r\n", $headerBlob); - - $headers = array(); - foreach($headerBlob as $header) { - $parts = explode(':', $header, 2); - if (count($parts)==2) { - $headers[strtolower(trim($parts[0]))] = trim($parts[1]); - } - } - - $response = array( - 'body' => $response, - 'statusCode' => $curlInfo['http_code'], - 'headers' => $headers - ); - - if ($curlErrNo) { - throw new Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')'); - } - - if ($response['statusCode']>=400) { - switch ($response['statusCode']) { - case 400 : - throw new Exception\BadRequest('Bad request'); - case 401 : - throw new Exception\NotAuthenticated('Not authenticated'); - case 402 : - throw new Exception\PaymentRequired('Payment required'); - case 403 : - throw new Exception\Forbidden('Forbidden'); - case 404: - throw new Exception\NotFound('Resource not found.'); - case 405 : - throw new Exception\MethodNotAllowed('Method not allowed'); - case 409 : - throw new Exception\Conflict('Conflict'); - case 412 : - throw new Exception\PreconditionFailed('Precondition failed'); - case 416 : - throw new Exception\RequestedRangeNotSatisfiable('Requested Range Not Satisfiable'); - case 500 : - throw new Exception('Internal server error'); - case 501 : - throw new Exception\NotImplemented('Not Implemented'); - case 507 : - throw new Exception\InsufficientStorage('Insufficient storage'); - default: - throw new Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')'); - } - } - - return $response; - - } - - /** - * Wrapper for all curl functions. - * - * The only reason this was split out in a separate method, is so it - * becomes easier to unittest. - * - * @param string $url - * @param array $settings - * @return array - */ - // @codeCoverageIgnoreStart - protected function curlRequest($url, $settings) { - - // TODO rainloop - $curl = curl_init($url); - - if (ini_get('open_basedir') === '') - { - curl_setopt_array($curl, $settings); - $data = curl_exec($curl); - } - else - { - $settings[CURLOPT_FOLLOWLOCATION] = false; - curl_setopt_array($curl, $settings); - - $max_redirects = isset($settings[CURLOPT_MAXREDIRS]) ? $settings[CURLOPT_MAXREDIRS] : 5; - $mr = $max_redirects; - if ($mr > 0) - { - $newurl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); - - $rcurl = curl_copy_handle($curl); - curl_setopt($rcurl, CURLOPT_HEADER, true); - curl_setopt($rcurl, CURLOPT_NOBODY, true); - curl_setopt($rcurl, CURLOPT_FORBID_REUSE, false); - curl_setopt($rcurl, CURLOPT_RETURNTRANSFER, true); - do - { - curl_setopt($rcurl, CURLOPT_URL, $newurl); - $header = curl_exec($rcurl); - if (curl_errno($rcurl)) - { - $code = 0; - } - else - { - $code = curl_getinfo($rcurl, CURLINFO_HTTP_CODE); - if ($code == 301 || $code == 302) - { - $matches = array(); - preg_match('/Location:(.*?)\n/', $header, $matches); - $newurl = trim(array_pop($matches)); - } - else - { - $code = 0; - } - } - } while ($code && --$mr); - - curl_close($rcurl); - if ($mr > 0) - { - curl_setopt($curl, CURLOPT_URL, $newurl); - } - } - - if ($mr == 0 && $max_redirects > 0) - { - $data = false; - } - else - { - $data = curl_exec($curl); - } - } - - return array( - $data, - curl_getinfo($curl), - curl_errno($curl), - curl_error($curl) - ); - // END rainloop - - $curl = curl_init($url); - curl_setopt_array($curl, $settings); - - return array( - curl_exec($curl), - curl_getinfo($curl), - curl_errno($curl), - curl_error($curl) - ); - - } - // @codeCoverageIgnoreEnd - - /** - * Returns the full url based on the given url (which may be relative). All - * urls are expanded based on the base url as given by the server. - * - * @param string $url - * @return string - */ - protected function getAbsoluteUrl($url) { - - // If the url starts with http:// or https://, the url is already absolute. - if (preg_match('/^http(s?):\/\//', $url)) { - return $url; - } - - // If the url starts with a slash, we must calculate the url based off - // the root of the base url. - if (strpos($url,'/') === 0) { - $parts = parse_url($this->baseUri); - return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url; - } - - // Otherwise... - return $this->baseUri . $url; - - } - - /** - * Parses a WebDAV multistatus response body - * - * This method returns an array with the following structure - * - * array( - * 'url/to/resource' => array( - * '200' => array( - * '{DAV:}property1' => 'value1', - * '{DAV:}property2' => 'value2', - * ), - * '404' => array( - * '{DAV:}property1' => null, - * '{DAV:}property2' => null, - * ), - * ) - * 'url/to/resource2' => array( - * .. etc .. - * ) - * ) - * - * - * @param string $body xml body - * @return array - */ - public function parseMultiStatus($body) { - - $body = XMLUtil::convertDAVNamespace($body); - - // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or - // 5.4.13. - $previous = libxml_disable_entity_loader(true); - $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA); - libxml_disable_entity_loader($previous); - - if ($responseXML===false) { - throw new \InvalidArgumentException('The passed data is not valid XML'); - } - - $responseXML->registerXPathNamespace('d', 'urn:DAV'); - - $propResult = array(); - - foreach($responseXML->xpath('d:response') as $response) { - $response->registerXPathNamespace('d', 'urn:DAV'); - $href = $response->xpath('d:href'); - $href = (string)$href[0]; - - $properties = array(); - - foreach($response->xpath('d:propstat') as $propStat) { - - $propStat->registerXPathNamespace('d', 'urn:DAV'); - $status = $propStat->xpath('d:status'); - list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3); - - // Only using the propertymap for results with status 200. - $propertyMap = $statusCode==='200' ? $this->propertyMap : array(); - - $properties[$statusCode] = XMLUtil::parseProperties(dom_import_simplexml($propStat), $propertyMap); - - } - - $propResult[$href] = $properties; - - } - - return $propResult; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception.php deleted file mode 100644 index af47c36a2..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception.php +++ /dev/null @@ -1,64 +0,0 @@ -getAllowedMethods($server->getRequestUri()); - - return array( - 'Allow' => strtoupper(implode(', ',$methods)), - ); - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php deleted file mode 100644 index 26eb61d3d..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/NotAuthenticated.php +++ /dev/null @@ -1,30 +0,0 @@ -header = $header; - - } - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 412; - - } - - /** - * This method allows the exception to include additional information into the WebDAV error response - * - * @param DAV\Server $server - * @param \DOMElement $errorNode - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $errorNode) { - - if ($this->header) { - $prop = $errorNode->ownerDocument->createElement('s:header'); - $prop->nodeValue = $this->header; - $errorNode->appendChild($prop); - } - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php deleted file mode 100644 index 2651b1d3e..000000000 --- a/snappymail/v/0.0.0/app/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php +++ /dev/null @@ -1,31 +0,0 @@ - - * will be returned as: - * {http://www.example.org}myelem - * - * This format is used throughout the SabreDAV sourcecode. - * Elements encoded with the urn:DAV namespace will - * be returned as if they were in the DAV: namespace. This is to avoid - * compatibility problems. - * - * This function will return null if a nodetype other than an Element is passed. - * - * @param \DOMNode $dom - * @return string - */ - static function toClarkNotation(\DOMNode $dom) { - - if ($dom->nodeType !== XML_ELEMENT_NODE) return null; - - // Mapping back to the real namespace, in case it was dav - if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; - - // Mapping to clark notation - return '{' . $ns . '}' . $dom->localName; - - } - - /** - * Parses a clark-notation string, and returns the namespace and element - * name components. - * - * If the string was invalid, it will throw an InvalidArgumentException. - * - * @param string $str - * @throws InvalidArgumentException - * @return array - */ - static function parseClarkNotation($str) { - - if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) { - throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); - } - - return array( - $matches[1], - $matches[2] - ); - - } - - /** - * This method takes an XML document (as string) and converts all instances of the - * DAV: namespace to urn:DAV - * - * This is unfortunately needed, because the DAV: namespace violates the xml namespaces - * spec, and causes the DOM to throw errors - * - * @param string $xmlDocument - * @return array|string|null - */ - static function convertDAVNamespace($xmlDocument) { - - // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: - // namespace is actually a violation of the XML namespaces specification, and will cause errors - return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); - - } - - /** - * This method provides a generic way to load a DOMDocument for WebDAV use. - * - * This method throws a Sabre\DAV\Exception\BadRequest exception for any xml errors. - * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. - * - * @param string $xml - * @throws Sabre\DAV\Exception\BadRequest - * @return DOMDocument - */ - static function loadDOMDocument($xml) { - - if (empty($xml)) - throw new Exception\BadRequest('Empty XML document sent'); - - // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) - // does not support this, so we must intercept this and convert to UTF-8. - if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { - - // Note: the preceeding byte sequence is "]*)encoding="UTF-16"([^>]*)>|u','',$xml); - - } - - // Retaining old error setting - $oldErrorSetting = libxml_use_internal_errors(true); - // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or - // 5.4.13. - $oldEntityLoaderSetting = libxml_disable_entity_loader(true); - - // Clearing any previous errors - libxml_clear_errors(); - - $dom = new \DOMDocument(); - - // We don't generally care about any whitespace - $dom->preserveWhiteSpace = false; - - $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); - - if ($error = libxml_get_last_error()) { - libxml_clear_errors(); - throw new Exception\BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); - } - - // Restoring old mechanism for error handling - if ($oldErrorSetting===false) libxml_use_internal_errors(false); - if ($oldEntityLoaderSetting===false) libxml_disable_entity_loader(false); - - return $dom; - - } - - /** - * Parses all WebDAV properties out of a DOM Element - * - * Generally WebDAV properties are enclosed in {DAV:}prop elements. This - * method helps by going through all these and pulling out the actual - * propertynames, making them array keys and making the property values, - * well.. the array values. - * - * If no value was given (self-closing element) null will be used as the - * value. This is used in for example PROPFIND requests. - * - * Complex values are supported through the propertyMap argument. The - * propertyMap should have the clark-notation properties as it's keys, and - * classnames as values. - * - * When any of these properties are found, the unserialize() method will be - * (statically) called. The result of this method is used as the value. - * - * @param \DOMElement $parentNode - * @param array $propertyMap - * @return array - */ - static function parseProperties(\DOMElement $parentNode, array $propertyMap = array()) { - - $propList = array(); - foreach($parentNode->childNodes as $propNode) { - - if (self::toClarkNotation($propNode)!=='{DAV:}prop') continue; - - foreach($propNode->childNodes as $propNodeData) { - - /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ - if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue; - - $propertyName = self::toClarkNotation($propNodeData); - if (isset($propertyMap[$propertyName])) { - $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); - } else { - $propList[$propertyName] = $propNodeData->textContent; - } - } - - - } - return $propList; - - } - -} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php b/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php new file mode 100644 index 000000000..0c1e6c2ce --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php @@ -0,0 +1,237 @@ + 'SnappyMail\\DAV\\Property\\ResourceType' + ); + + protected $baseUri; + + /** + * Constructor + * + * Settings are provided through the 'settings' argument. The following + * settings are supported: + * + * * baseUri + * * userName (optional) + * * password (optional) + * * proxy (optional) + */ + function __construct(array $settings) + { + if (!isset($settings['baseUri'])) { + throw new \InvalidArgumentException('A baseUri must be provided'); + } + $this->baseUri = $settings['baseUri']; + + $this->HTTP = \SnappyMail\HTTP\Request::factory('socket'); + $this->HTTP->proxy = $settings['proxy'] ?? null; + $this->HTTP->setAuth(3, $settings['userName'] ?? '', $settings['password'] ?? ''); + $this->HTTP->max_response_kb = 0; + $this->HTTP->timeout = 15; // timeout in seconds. + $this->HTTP->follow_location = false; + } + + /** + * Enable/disable SSL peer verification + */ + public function setVerifyPeer(bool $value) : void + { + $this->HTTP->verify_peer = $value; + } + + /** + * Does a PROPFIND request + * + * The list of requested properties must be specified as an array, in clark + * notation. + * + * The returned array will contain a list of filenames as keys, and + * properties as values. + * + * The properties array will contain the list of properties. Only properties + * that are actually returned from the server (without error) will be + * returned, anything else is discarded. + * + * Depth should be either 0 or 1. A depth of 1 will cause a request to be + * made to the server to also return all child resources. + */ + public function propFind(string $url, array $properties, int $depth = 0) : array + { + $body = '' . "\n"; + $body.= '' . "\n"; + $body.= ' ' . "\n"; + + foreach ($properties as $property) { + if (!\preg_match('/^{([^}]*)}(.*)$/', $property, $match)) { + throw new \InvalidArgumentException('\'' . $property . '\' is not a valid clark-notation formatted string'); + } + if ('DAV:' === $match[1]) { + $body .= " \n"; + } else { + $body .= " \n"; + } + } + + $body .= ' ' . "\n"; + $body .= ''; + + if (!\preg_match('/^http(s?):\/\//', $url)) { + // If the url starts with a slash, we must calculate the url based off + // the root of the base url. + if (0 === \strpos($url, '/')) { + $parts = \parse_url($this->baseUri); + $url = $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url; + } else { + $url = $this->baseUri . $url; + } + } + $response = $this->HTTP->doRequest('PROPFIND', $url, $body, array( + "Depth: {$depth}", + 'Content-Type: application/xml' + )); + if (300 <= $response->status) { + throw new \SnappyMail\HTTP\Exception('', $response->status); + } + + /** + * Parse the WebDAV multistatus response body + */ + $responseXML = \simplexml_load_string( + /** + * Convert all instances of the DAV: namespace to urn:DAV + * + * This is unfortunately needed, because the DAV: namespace violates the xml namespaces + * spec, and causes the DOM to throw errors + * + * This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: + * namespace is actually a violation of the XML namespaces specification, and will cause errors + */ + \preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/", "xmlns\\1=\\2urn:DAV\\2", $response->body), + null, LIBXML_NOBLANKS | LIBXML_NOCDATA); + + if (false === $responseXML) { + throw new \InvalidArgumentException('The passed data is not valid XML'); + } + + $responseXML->registerXPathNamespace('d', 'urn:DAV'); + + $result = array(); + + foreach ($responseXML->xpath('d:response') as $response) { + $response->registerXPathNamespace('d', 'urn:DAV'); + $href = $response->xpath('d:href'); + $href = (string) $href[0]; + + $properties = array(); + + foreach ($response->xpath('d:propstat') as $propStat) { + $propStat->registerXPathNamespace('d', 'urn:DAV'); + $status = $propStat->xpath('d:status'); + list($httpVersion, $statusCode, $message) = \explode(' ', (string)$status[0], 3); + + // Only using the propertymap for results with status 200. + $propertyMap = $statusCode === '200' ? $this->propertyMap : array(); + + $properties[$statusCode] = static::parseProperties(\dom_import_simplexml($propStat), $propertyMap); + } + + $result[$href] = $properties; + } + + if (0 === $depth) { + \reset($result); + return \current($result)[200] ?? array(); + } + + return \array_map(function($statusList){ + return $statusList[200] ?? array(); + }, $result); + } + + /** + * Returns the 'clark notation' for an element. + * + * For example, and element encoded as: + * + * will be returned as: + * {http://www.example.org}myelem + * + * This format is used throughout the SabreDAV sourcecode. + * Elements encoded with the urn:DAV namespace will + * be returned as if they were in the DAV: namespace. This is to avoid + * compatibility problems. + * + * This function will return null if a nodetype other than an Element is passed. + */ + public static function toClarkNotation(\DOMNode $dom) : ?string + { + // Mapping back to the real namespace, in case it was dav + // Mapping to clark notation + return XML_ELEMENT_NODE === $dom->nodeType + ? '{' . ('urn:DAV' == $dom->namespaceURI ? 'DAV:' : $dom->namespaceURI) . '}' . $dom->localName + : null; + } + + /** + * Parses all WebDAV properties out of a DOM Element + * + * Generally WebDAV properties are enclosed in {DAV:}prop elements. This + * method helps by going through all these and pulling out the actual + * propertynames, making them array keys and making the property values, + * well.. the array values. + * + * If no value was given (self-closing element) null will be used as the + * value. This is used in for example PROPFIND requests. + * + * Complex values are supported through the propertyMap argument. The + * propertyMap should have the clark-notation properties as it's keys, and + * classnames as values. + * + * When any of these properties are found, the fromDOMElement() method will be + * (statically) called. The result of this method is used as the value. + */ + protected static function parseProperties(\DOMElement $parentNode, array $propertyMap = array()) : array + { + $propList = array(); + foreach ($parentNode->childNodes as $propNode) { + if ('{DAV:}prop' === self::toClarkNotation($propNode)) { + foreach ($propNode->childNodes as $propNodeData) { + /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ + if (XML_ELEMENT_NODE == $propNodeData->nodeType) { + $propertyName = self::toClarkNotation($propNodeData); + if (isset($propertyMap[$propertyName])) { + $propList[$propertyName] = \call_user_func(array($propertyMap[$propertyName], 'fromDOMElement'), $propNodeData); + } else { + $propList[$propertyName] = $propNodeData->textContent; + } + } + } + } + } + return $propList; + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/exception.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/exception.php new file mode 100644 index 000000000..bdc6c3df8 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/exception.php @@ -0,0 +1,84 @@ + 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', +// 306 => 'Switch Proxy', # obsolete + 307 => 'Temporary Redirect', + + // Client Error 4xx + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', # reserved for future use + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + // https://tools.ietf.org/html/rfc7540#section-9.1.2 + 421 => 'Misdirected Request', + // https://tools.ietf.org/html/rfc4918 + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + // http://tools.ietf.org/html/rfc2817 + 426 => 'Upgrade Required', + // http://tools.ietf.org/html/rfc6585 + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + + // Server Error 5xx + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', # may have Retry-After header + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', +// 506 => 'Variant Also Negotiates', +// 507 => 'Insufficient Storage', +// 508 => 'Loop Detected', +// 509 => 'Bandwidth Limit Exceeded', +// 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ); + + function __construct(string $message = "", int $code = 0, Response $response = null) + { + if ($response) { + if (\in_array($code, array(301, 302, 303, 307))) { + $message = $response->getRedirectLocation() . "\n" . $message; + } else if (405 === $code && ($allow = $this->getHeader('allow'))) { + $message = (\is_array($allow) ? $allow[0] : $allow) . "\n" . $message; + } + } + if (isset(static::CODES[$code])) { + $message = "{$code} " . static::CODES[$code] . ($message ? ": {$message}" : ''); + } + parent::__construct($message, $code); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/request.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/request.php new file mode 100644 index 000000000..fba6497b6 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/request.php @@ -0,0 +1,159 @@ + 0, + 'user' => '', + 'pass' => '' + ], + $stream = null, + $headers = array(), + $ca_bundle = null; + + protected static $scheme_ports = array( + 'http' => 80, + 'https' => 443 + ); + + public static function factory(string $type = 'curl') + { + if ('curl' === $type && \function_exists('curl_init')) { + return new Request\CURL(); + } + return new Request\Socket(); + } + + function __construct() + { + $this->user_agent = 'SnappyMail/' . APP_VERSION; + } + + public function setAuth(int $type, string $user, string $pass) : void + { + $this->auth = [ + 'type' => $type, + 'user' => $user, + 'pass' => $pass + ]; + } + + public function addHeader($header) + { + $this->headers[] = $header; + return $this; + } + + public function streamBodyTo($stream) + { + if (!\is_resource($stream)) { + throw new \Exception('Invalid body target'); + } + $this->stream = $stream; + } + + public function setCABundleFile($file) + { + $this->ca_bundle = $file; + } + + /** + * Return whether a URI can be fetched. Returns false if the URI scheme is not allowed + * or is not supported by this fetcher implementation; returns true otherwise. + * + * @return bool + */ + public function canFetchURI($uri) + { + if ('https:' === \substr($uri, 0, 6) && !$this->supportsSSL()) { + \trigger_error('HTTPS URI unsupported fetching '.$uri, E_USER_WARNING); + return false; + } + if (!self::URIHasAllowedScheme($uri)) { + \trigger_error('URI fetching not allowed for '.$uri, E_USER_WARNING); + return false; + } + return true; + } + + /** + * Does this fetcher implementation (and runtime) support fetching HTTPS URIs? + * May inspect the runtime environment. + * + * @return bool $support True if this fetcher supports HTTPS + * fetching; false if not. + */ + abstract public function supportsSSL() : bool; + + abstract protected function __doRequest(string &$method, string &$request_url, &$body, array $extra_headers) : Response; + + public function doRequest($method, $request_url, $body = null, array $extra_headers = array()) : ?Response + { + $method = \strtoupper($method); + $url = $request_url; + $etime = \time() + $this->timeout; + if (\is_array($body)) { $body = \http_build_query($body, '', '&'); } + if ($body && 'GET' === $method) { + $url .= (\strpos($url, '?')?'&':'?').$body; + $body = null; + } + do + { + if (!$this->canFetchURI($url)) { + throw new \RuntimeException("Can't fetch URL: {$url}"); + } + + if (!self::URIHasAllowedScheme($url)) { + throw new \RuntimeException("Fetching URL not allowed: {$url}"); + } + + $result = $this->__doRequest($method, $url, $body, \array_merge($this->headers, $extra_headers)); + + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3 + // In response to a request other than GET or HEAD, the user agent MUST NOT + // automatically redirect the request unless it can be confirmed by the user + if ($this->follow_location && \is_null($body) && \in_array($result->status, array(301, 302, 303, 307))) { + $url = $result->getRedirectLocation(); + } else { + $result->final_uri = $url; + $result->request_uri = $request_url; + return $result; + } + + } while ($etime-time() > 0); + + return null; + } + + /** + * Return whether a URI should be allowed. Override this method to conform to your local policy. + * By default, will attempt to fetch any http or https URI. + */ + public static function URIHasAllowedScheme($uri) : bool + { + return (bool) \preg_match('#^https?://#i', $uri); + } + + public static function getSchemePort($scheme) : int + { + return self::$scheme_ports[$scheme] ?? 0; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/request/curl.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/request/curl.php new file mode 100644 index 000000000..313b83797 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/request/curl.php @@ -0,0 +1,115 @@ + $this->user_agent, + CURLOPT_CONNECTTIMEOUT => $this->timeout, + CURLOPT_TIMEOUT => $this->timeout, + CURLOPT_URL => $request_url, + CURLOPT_HEADERFUNCTION => array($this, 'fetchHeader'), + CURLOPT_WRITEFUNCTION => array($this, \is_resource($this->stream) ? 'streamData' : 'fetchData'), + CURLOPT_SSL_VERIFYPEER => ($this->verify_peer || $this->ca_bundle), +// CURLOPT_SSL_VERIFYHOST => $this->verify_peer ? 2 : 0, +// CURLOPT_FOLLOWLOCATION => false, // follow redirects +// CURLOPT_MAXREDIRS => 0, // stop after 0 redirects + )); +// \curl_setopt($c, CURLOPT_ENCODING , 'gzip'); + if (\defined('CURLOPT_NOSIGNAL')) { + \curl_setopt($c, CURLOPT_NOSIGNAL, true); + } + if ($this->ca_bundle) { + \curl_setopt($c, CURLOPT_CAINFO, $this->ca_bundle); + } + if ($extra_headers) { + \curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers); + } + if ($this->auth['user'] && $this->auth['type']) { + $auth = 0; + if ($this->auth['type'] & self::AUTH_BASIC) { + $auth |= CURLAUTH_BASIC; + } + if ($this->auth['type'] & self::AUTH_DIGEST) { + $auth |= CURLAUTH_DIGEST; + } + \curl_setopt($c, CURLOPT_HTTPAUTH, $auth); + \curl_setopt($c, CURLOPT_USERPWD, $this->auth['user'] . ':' . $this->auth['pass']); + } + if ($this->proxy) { + \curl_setopt($c, CURLOPT_PROXY, $this->proxy); + } + if ('HEAD' === $method) { + \curl_setopt($c, CURLOPT_NOBODY, true); + } else if ('GET' !== $method) { + if ('POST' === $method) { + \curl_setopt($c, CURLOPT_POST, true); + } else { + \curl_setopt($c, CURLOPT_CUSTOMREQUEST, $method); + } + if (!\is_null($body)) { + \curl_setopt($c, CURLOPT_POSTFIELDS, $body); + } + } + + \curl_exec($c); + + try { + $code = \curl_getinfo($c, CURLINFO_RESPONSE_CODE); + if (!$code) { + throw new \RuntimeException("Error " . \curl_errno($c) . ": " . \curl_error($c) . " for {$request_url}"); + } + return new Response($request_url, $code, $this->response_headers, $this->response_body); + } finally { + \curl_close($c); + $this->response_headers = array(); + $this->response_body = ''; + } + } + + protected function fetchHeader($ch, $header) + { + $this->response_headers[] = \rtrim($header); + return \strlen($header); + } + + protected function fetchData($ch, $data) + { + if ($this->max_response_kb) { + $data = \substr($data, 0, \min(\strlen($data), ($this->max_response_kb*1024) - \strlen($this->response_body))); + } + $this->response_body .= $data; + return \strlen($data); + } + + protected function streamData($ch, $data) + { + return \fwrite($this->stream, $data); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/request/socket.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/request/socket.php new file mode 100644 index 000000000..93e4e809c --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/request/socket.php @@ -0,0 +1,177 @@ +user_agent}", + 'Connection: Close', + ); + + if ($extra_headers) { + $headers = \array_merge($headers, $extra_headers); + } + $headers = \implode("\r\n", $headers); + if (!\is_null($body)) { + if (!\stripos($headers,'Content-Type')) { + $headers .= "\r\nContent-Type: application/x-www-form-urlencoded"; + } + $headers .= "\r\nContent-Length: ".\strlen($body); + } + + $context = \stream_context_create(); + if ('https' === $parts['scheme']) { + $parts['host'] = 'ssl://'.$parts['host']; + \stream_context_set_option($context, 'ssl', 'verify_peer_name', true); + if ($this->verify_peer || $this->ca_bundle) { + \stream_context_set_option($context, 'ssl', 'verify_peer', true); + if ($this->ca_bundle) { + if (\is_dir($this->ca_bundle) || (\is_link($this->ca_bundle) && \is_dir(\readlink($this->ca_bundle)))) { + \stream_context_set_option($context, 'ssl', 'capath', $this->ca_bundle); + } else { + \stream_context_set_option($context, 'ssl', 'cafile', $this->ca_bundle); + } + } + } else { + \stream_context_set_option($context, 'ssl', 'allow_self_signed', true); + } + } else { + $parts['host'] = 'tcp://'.$parts['host']; + } + + $errno = 0; + $errstr = ''; + + $sock = \stream_socket_client("{$parts['host']}:{$parts['port']}", $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $context); + if (false === $sock) { + throw new \RuntimeException($errstr); + } + + \stream_set_timeout($sock, $this->timeout); + + \fwrite($sock, $headers . "\r\n\r\n"); + if (!\is_null($body)) { + \fwrite($sock, $body); + } + + # Read all headers + $chunked = false; + $response_headers = array(); + $data = \rtrim(\fgets($sock, 1024)); # read line + $code = \intval(\explode(' ', $data)[1]??0); + while (\strlen($data)) { + $response_headers[] = $data; + $chunked |= \preg_match('#Transfer-Encoding:.*chunked#i', $data); + + if (401 === $code && $this->auth['user']) { + // Basic authentication + if ($this->auth['type'] & self::AUTH_BASIC && \preg_match("/WWW-Authenticate:\\s+Basic\\s+realm=([^\\r\\n]*)/i", $data, $match)) { + $extra_headers['Authorization'] = "Authorization: Basic " . \base64_encode($this->auth['user'] . ':' . $this->auth['pass']); + \fclose($sock); + return $this->__doRequest($method, $request_url, $body, $extra_headers); + } + // Digest authentication + else if ($this->auth['type'] & self::AUTH_DIGEST && \preg_match("/WWW-Authenticate:\\s+Digest\\s+([^\\r\\n]*)/i", $data, $match)) { + $challenge = []; + foreach (\split(',', $match[1]) as $i) { + $ii = \split('=', \trim($i), 2); + if (!empty($ii[1]) && !empty($ii[0])) { + $challenge[$ii[0]] = \preg_replace('/^"/','', \preg_replace('/"$/','', $ii[1])); + } + } + $a1 = \md5($this->auth['user'] . ':' . $challenge['realm'] . ':' . $this->auth['pass']); + $a2 = \md5($method . ':' . $request_url); + if (empty($challenge['qop'])) { + $digest = \md5($a1 . ':' . $challenge['nonce'] . ':' . $a2); + } else { + $challenge['cnonce'] = 'Req2.' . \random_int(); + if (empty($challenge['nc'])) { + $challenge['nc'] = 1; + } + $nc = \sprintf('%08x', $challenge['nc']++); + $digest = \md5($a1 . ':' . $challenge['nonce'] . ':' . $nc . ':' . $challenge['cnonce'] . ':auth:' . $a2); + } + $extra_headers['Authorization'] = "Authorization: Digest " + . ' username="' . \str_replace(array('\\', '"'), array('\\\\', '\\"'), $this->auth['user']) . '",' + . ' realm="' . $challenge['realm'] . '",' + . ' nonce="' . $challenge['nonce'] . '",' + . ' uri="' . $request_url . '",' + . ' response="' . $digest . '"' + . (empty($challenge['opaque']) ? '' : ', opaque="' . $challenge['opaque'] . '"') + . (empty($challenge['qop']) ? '' : ', qop="auth", nc=' . $nc . ', cnonce="' . $challenge['cnonce'] . '"'); + + \fclose($sock); + return $this->__doRequest($method, $request_url, $body, $extra_headers); + } + } + + $data = \rtrim(\fgets($sock, 1024)); # read next line + } + + # Read body + $body = ''; + if (\is_resource($this->stream)) { + while (!\feof($sock)) { + if ($chunked) { + $chunk = \hexdec(\trim(\fgets($sock, 8))); + if (!$chunk) { break; } + while ($chunk > 0) { + $tmp = \fread($sock, $chunk); + \fwrite($this->stream, $tmp); + $chunk -= \strlen($tmp); + } + } else { + \fwrite($this->stream, \fread($sock, 1024)); + } + } + } else { + $max_bytes = $this->max_response_kb * 1024; + while (!\feof($sock) && (!$max_bytes || \strlen($body) < $max_bytes)) { + if ($chunked) { + $chunk = \hexdec(\trim(\fgets($sock, 8))); + if (!$chunk) { break; } + while ($chunk > 0) { + $tmp = \fread($sock, $chunk); + $body .= $tmp; + $chunk -= \strlen($tmp); + } + } else { + $body .= \fread($sock, 1024); + } + } + } + + \fclose($sock); + + return new Response($request_url, $code, $response_headers, $body); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/response.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/response.php new file mode 100644 index 000000000..1e6cffff9 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/response.php @@ -0,0 +1,88 @@ +headers = array(); + foreach ($headers as $header) { + if (\strpos($header, ':')) { + list($name, $value) = \explode(':', $header, 2); + $name = \strtolower(\trim($name)); + $value = \trim($value); + if (isset($this->headers[$name])) { + if (\is_array($this->headers[$name])) { + $this->headers[$name][] = $value; + } else { + $this->headers[$name] = array($this->headers[$name], $value); + } + } else { + $this->headers[$name] = $value; + } + } else if ($name) { +// $this->headers[$name] .= \trim($header); + } + } + } + $this->request_uri = $request_uri; + $this->final_uri = $request_uri; + $this->status = (int) $status; + if (\function_exists('gzinflate') && isset($this->headers['content-encoding']) + && (false !== \stripos($this->headers['content-encoding'], 'gzip'))) { + $this->body = \gzinflate(\substr($body, 10, -4)); + } else { + $this->body = $body; + } + } + + function __get($k) + { + return \property_exists($this, $k) ? $this->$k : null; + } + + public function getHeader($names) + { + $names = \is_array($names) ? $names : array($names); + foreach ($names as $n) { + $n = \strtolower($n); + if (isset($this->headers[$n])) { + return $this->headers[$n]; + } + } + return null; + } + + public function getRedirectLocation() : ?string + { + if ($location = $this->getHeader('location')) { + $uri = \is_array($location) ? $location[0] : $location; + if (!\preg_match('#^[a-z][a-z0-9\\+\\.\\-]+://[^/]+#i', $uri)) { + // no host + \preg_match('#^([a-z][a-z0-9\\+\\.\\-]+://[^/]+)(/[^\\?\\#]*)#i', $this->final_uri, $match); + if ('/' === $uri[0]) { + // absolute path + $uri = $match[1] . $uri; + } else { + // relative path + $rpos = \strrpos($match[2], '/'); + $uri = $match[1] . \substr($match[2], 0, $rpos+1) . $uri; + } + } + return $uri; + } + return null; + } + +} From 687afdef6aa2938afd8a38262395afbc8a687f79 Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 6 Apr 2021 23:03:20 +0200 Subject: [PATCH 38/90] Resolve quota issue #75 --- dev/View/User/MailBox/MessageList.js | 4 +--- .../v/0.0.0/app/templates/Views/User/MailMessageList.html | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index f0824c5ac..a35d1a104 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -187,8 +187,6 @@ export class MessageListMailBoxUserView extends AbstractViewRight { this.hasCheckedOrSelectedLines = MessageUserStore.hasCheckedOrSelected, - this.quotaTooltip = this.quotaTooltip.bind(this); - this.selector = new Selector( MessageUserStore.list, MessageUserStore.selectorMessageSelected, @@ -887,7 +885,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight { SIZE: FileInfo.friendlySize(this.userUsageSize()), PROC: this.userUsageProc(), LIMIT: FileInfo.friendlySize(this.userQuota()) - }); + }).replace(/<[^>]+>/g, ''); } initUploaderForAppend() { diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index e5cfa630d..073932e2a 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -251,7 +251,7 @@