iTagCount = 0; $this->aCapabilityItems = null; $this->oCurrentFolderInfo = null; $this->aFetchCallbacks = null; $this->iResponseBufParsedPos = 0; $this->aLastResponse = array(); $this->bNeedNext = true; $this->aPartialResponses = array(); $this->aTagTimeouts = array(); $this->bIsLoggined = false; $this->bIsSelected = false; $this->sLogginedUser = ''; @\ini_set('xdebug.max_nesting_level', 500); } /** * @return \MailSo\Imap\ImapClient */ public static function NewInstance() { return new self(); } /** * @return string */ public function GetLogginedUser() { return $this->sLogginedUser; } /** * @param string $sServerName * @param int $iPort = 143 * @param int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function Connect($sServerName, $iPort = 143, $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT) { $this->aTagTimeouts['*'] = microtime(true); parent::Connect($sServerName, $iPort, $iSecurityType); $this->parseResponseWithValidation('*', true); if (\MailSo\Net\Enumerations\ConnectionSecurityType::UseStartTLS( $this->IsSupported('STARTTLS'), $this->iSecurityType)) { $this->SendRequestWithCheck('STARTTLS'); if (!@stream_socket_enable_crypto($this->rConnect, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { $this->writeLogException( new \MailSo\Imap\Exceptions\RuntimeException('Cannot enable STARTTLS'), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->aCapabilityItems = null; } else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType) { $this->writeLogException( new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'), \MailSo\Log\Enumerations\Type::ERROR, true); } return $this; } /** * @param string $sLogin * @param string $sPassword * @param string $sProxyAuthUser = '' * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function Login($sLogin, $sPassword, $sProxyAuthUser = '') { if (!\MailSo\Base\Validator::NotEmptyString($sLogin, true) || !\MailSo\Base\Validator::NotEmptyString($sPassword, true)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } $sLogin = trim($sLogin); $sPassword = $sPassword; $this->sLogginedUser = $sLogin; try { $this->SendRequestWithCheck('LOGIN', array( $this->EscapeString($sLogin), $this->EscapeString($sPassword) )); if (0 < \strlen($sProxyAuthUser)) { $this->SendRequestWithCheck('PROXYAUTH', array($this->EscapeString($sProxyAuthUser))); } } catch (\MailSo\Imap\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Imap\Exceptions\LoginBadCredentialsException( $oException->GetResponses(), '', 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } $this->bIsLoggined = true; $this->aCapabilityItems = null; return $this; } /** * @param string $sXOAuth2Token * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function LoginWithXOauth2($sXOAuth2Token) { if (!\MailSo\Base\Validator::NotEmptyString($sXOAuth2Token, true)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } if (!$this->IsSupported('AUTH=XOAUTH2')) { $this->writeLogException( new \MailSo\Imap\Exceptions\LoginBadMethodException(), \MailSo\Log\Enumerations\Type::NOTICE, true); } try { $this->SendRequestWithCheck('AUTHENTICATE', array('XOAUTH2', trim($sXOAuth2Token))); } catch (\MailSo\Imap\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Imap\Exceptions\LoginBadCredentialsException( $oException->GetResponses(), '', 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } $this->bIsLoggined = true; $this->aCapabilityItems = null; return $this; } /** * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Net\Exceptions\Exception */ public function Logout() { if ($this->bIsLoggined) { $this->bIsLoggined = false; $this->SendRequestWithCheck('LOGOUT', array()); } return $this; } /** * @return \MailSo\Imap\ImapClient */ public function ForceCloseConnection() { $this->Disconnect(); return $this; } /** * @return bool */ public function IsLoggined() { return $this->IsConnected() && $this->bIsLoggined; } /** * @return bool */ public function IsSelected() { return $this->IsLoggined() && $this->bIsSelected; } /** * @return array|null * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function Capability() { $this->SendRequestWithCheck('CAPABILITY', array(), true); return $this->aCapabilityItems; } /** * @param string $sExtentionName * @return bool * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function IsSupported($sExtentionName) { $bResult = \MailSo\Base\Validator::NotEmptyString($sExtentionName, true); if ($bResult && null === $this->aCapabilityItems) { $this->aCapabilityItems = $this->Capability(); } return $bResult && is_array($this->aCapabilityItems) && in_array(strtoupper($sExtentionName), $this->aCapabilityItems); } /** * @return \MailSo\Imap\NamespaceResult|null * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function GetNamespace() { if (!$this->IsSupported('NAMESPACE')) { return null; } $oReturn = false; $this->SendRequest('NAMESPACE'); $aResult = $this->parseResponseWithValidation(); $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && 'NAMESPACE' === $oImapResponse->StatusOrIndex) { $oReturn = NamespaceResult::NewInstance(); $oReturn->InitByImapResponse($oImapResponse); break; } } if (false === $oReturn) { $this->writeLogException( new \MailSo\Imap\Exceptions\ResponseException(), \MailSo\Log\Enumerations\Type::ERROR, true); } return $oReturn; } /** * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function Noop() { return $this->SendRequestWithCheck('NOOP'); } /** * @param string $sFolderName * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderCreate($sFolderName) { return $this->SendRequestWithCheck('CREATE', array($this->EscapeString($sFolderName))); } /** * @param string $sFolderName * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderDelete($sFolderName) { return $this->SendRequestWithCheck('DELETE', array($this->EscapeString($sFolderName))); } /** * @param string $sFolderName * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderSubscribe($sFolderName) { return $this->SendRequestWithCheck('SUBSCRIBE', array($this->EscapeString($sFolderName))); } /** * @param string $sFolderName * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderUnSubscribe($sFolderName) { return $this->SendRequestWithCheck('UNSUBSCRIBE', array($this->EscapeString($sFolderName))); } /** * @param string $sOldFolderName * @param string $sNewFolderName * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderRename($sOldFolderName, $sNewFolderName) { return $this->SendRequestWithCheck('RENAME', array( $this->EscapeString($sOldFolderName), $this->EscapeString($sNewFolderName))); } /** * @param array $aResult * * @return array */ protected function getStatusFolderInformation($aResult) { $aReturn = array(); if (is_array($aResult)) { $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && 'STATUS' === $oImapResponse->StatusOrIndex && isset($oImapResponse->ResponseList[3]) && is_array($oImapResponse->ResponseList[3])) { $sName = null; foreach ($oImapResponse->ResponseList[3] as $sArrayItem) { if (null === $sName) { $sName = $sArrayItem; } else { $aReturn[$sName] = $sArrayItem; $sName = null; } } } } } return $aReturn; } /** * @param string $sFolderName * @param array $aStatusItems * * @return array|bool * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderStatus($sFolderName, array $aStatusItems) { $aResult = false; if (count($aStatusItems) > 0) { $this->SendRequest('STATUS', array($this->EscapeString($sFolderName), $aStatusItems)); $aResult = $this->getStatusFolderInformation( $this->parseResponseWithValidation()); } return $aResult; } /** * @param array $aResult * @param bool $bIsSubscribeList * * @return array */ private function getFoldersFromResult(array $aResult, $sStatus, $bUseListStatus = false) { $aReturn = array(); $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && $sStatus === $oImapResponse->StatusOrIndex && 5 === count($oImapResponse->ResponseList)) { try { $oFolder = Folder::NewInstance($oImapResponse->ResponseList[4], $oImapResponse->ResponseList[3], $oImapResponse->ResponseList[2]); $aReturn[] = $oFolder; } catch (\MailSo\Base\Exceptions\InvalidArgumentException $oException) { $this->writeLogException($oException, \MailSo\Log\Enumerations\Type::WARNING, false); } } } if ($bUseListStatus) { foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && 'STATUS' === $oImapResponse->StatusOrIndex && isset($oImapResponse->ResponseList[2]) && isset($oImapResponse->ResponseList[3]) && is_array($oImapResponse->ResponseList[3])) { $sFolderNameRaw = $oImapResponse->ResponseList[2]; $oCurrentFolder = null; foreach ($aReturn as &$oFolder) { if ($oFolder && $sFolderNameRaw === $oFolder->FullNameRaw()) { $oCurrentFolder =& $oFolder; break; } } if (null !== $oCurrentFolder) { $sName = null; $aStatus = array(); foreach ($oImapResponse->ResponseList[3] as $sArrayItem) { if (null === $sName) { $sName = $sArrayItem; } else { $aStatus[$sName] = $sArrayItem; $sName = null; } } if (0 < count($aStatus)) { $oCurrentFolder->SetExtended('STATUS', $aStatus); } } unset($oCurrentFolder); } } } return $aReturn; } /** * @param bool $bIsSubscribeList * @param string $sParentFolderName = '' * @param string $sListPattern = '*' * * @return array * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ private function specificFolderList($bIsSubscribeList, $sParentFolderName = '', $sListPattern = '*', $bUseListStatus = false) { $sCmd = 'LSUB'; if (!$bIsSubscribeList) { if ($bUseListStatus) { $bUseListStatus = $this->IsSupported('LIST-STATUS'); } $sCmd = (!$bUseListStatus && $this->IsSupported('XLIST') && !$this->IsSupported('LIST-EXTENDED')) ? 'XLIST' : 'LIST'; } $sListPattern = 0 === strlen(trim($sListPattern)) ? '*' : $sListPattern; $aParameters = array( $this->EscapeString($sParentFolderName), $this->EscapeString($sListPattern) ); if ($bUseListStatus) { $aParameters[] = 'RETURN'; $aParameters[] = array( 'STATUS', array( \MailSo\Imap\Enumerations\FolderStatus::MESSAGES, \MailSo\Imap\Enumerations\FolderStatus::UNSEEN, \MailSo\Imap\Enumerations\FolderStatus::UIDNEXT ) ); } $this->SendRequest($sCmd, $aParameters); return $this->getFoldersFromResult( $this->parseResponseWithValidation(), $sCmd, $bUseListStatus); } /** * @param string $sParentFolderName = '' * @param string $sListPattern = '*' * * @return array * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderList($sParentFolderName = '', $sListPattern = '*') { return $this->specificFolderList(false, $sParentFolderName, $sListPattern); } /** * @param string $sParentFolderName = '' * @param string $sListPattern = '*' * * @return array * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderSubscribeList($sParentFolderName = '', $sListPattern = '*') { return $this->specificFolderList(true, $sParentFolderName, $sListPattern); } /** * @param string $sParentFolderName = '' * @param string $sListPattern = '*' * * @return array * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderStatusList($sParentFolderName = '', $sListPattern = '*') { return $this->specificFolderList(false, $sParentFolderName, $sListPattern, true); } /** * @param array $aResult * @param string $sFolderName * @param bool $bIsWritable * * @return void */ protected function initCurrentFolderInformation($aResult, $sFolderName, $bIsWritable) { if (is_array($aResult)) { $oImapResponse = null; $oResult = FolderInformation::NewInstance($sFolderName, $bIsWritable); foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType) { if (count($oImapResponse->ResponseList) > 2 && 'FLAGS' === $oImapResponse->ResponseList[1] && is_array($oImapResponse->ResponseList[2])) { $oResult->Flags = $oImapResponse->ResponseList[2]; } if (is_array($oImapResponse->OptionalResponse) && count($oImapResponse->OptionalResponse) > 1) { if ('PERMANENTFLAGS' === $oImapResponse->OptionalResponse[0] && is_array($oImapResponse->OptionalResponse[1])) { $oResult->PermanentFlags = $oImapResponse->OptionalResponse[1]; } else if ('UIDVALIDITY' === $oImapResponse->OptionalResponse[0] && isset($oImapResponse->OptionalResponse[1])) { $oResult->Uidvalidity = $oImapResponse->OptionalResponse[1]; } else if ('UNSEEN' === $oImapResponse->OptionalResponse[0] && isset($oImapResponse->OptionalResponse[1]) && is_numeric($oImapResponse->OptionalResponse[1])) { $oResult->Unread = (int) $oImapResponse->OptionalResponse[1]; } else if ('UIDNEXT' === $oImapResponse->OptionalResponse[0] && isset($oImapResponse->OptionalResponse[1])) { $oResult->Uidnext = $oImapResponse->OptionalResponse[1]; } } if (count($oImapResponse->ResponseList) > 2 && is_string($oImapResponse->ResponseList[2]) && is_numeric($oImapResponse->ResponseList[1])) { switch($oImapResponse->ResponseList[2]) { case 'EXISTS': $oResult->Exists = (int) $oImapResponse->ResponseList[1]; break; case 'RECENT': $oResult->Recent = (int) $oImapResponse->ResponseList[1]; break; } } } } $this->oCurrentFolderInfo = $oResult; } } /** * @param string $sFolderName * @param bool $bIsWritable * @param bool $bReSelectSameFolders * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ protected function selectOrExamineFolder($sFolderName, $bIsWritable, $bReSelectSameFolders) { if (!$bReSelectSameFolders) { if ($this->oCurrentFolderInfo && $sFolderName === $this->oCurrentFolderInfo->FolderName && $bIsWritable === $this->oCurrentFolderInfo->IsWritable) { return $this; } } if (!\MailSo\Base\Validator::NotEmptyString($sFolderName, true)) { throw new \MailSo\Base\Exceptions\InvalidArgumentException(); } $this->SendRequest(($bIsWritable) ? 'SELECT' : 'EXAMINE', array($this->EscapeString($sFolderName))); $this->initCurrentFolderInformation( $this->parseResponseWithValidation(), $sFolderName, $bIsWritable); $this->bIsSelected = true; return $this; } /** * @param string $sFolderName * @param bool $bReSelectSameFolders = false * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderSelect($sFolderName, $bReSelectSameFolders = false) { return $this->selectOrExamineFolder($sFolderName, true, $bReSelectSameFolders); } /** * @param string $sFolderName * @param bool $bReSelectSameFolders = false * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function FolderExamine($sFolderName, $bReSelectSameFolders = false) { return $this->selectOrExamineFolder($sFolderName, false, $bReSelectSameFolders); } /** * @param array $aInputFetchItems * @param string $sIndexRange * @param bool $bIndexIsUid * * @return array * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function Fetch(array $aInputFetchItems, $sIndexRange, $bIndexIsUid) { $sIndexRange = (string) $sIndexRange; if (!\MailSo\Base\Validator::NotEmptyString($sIndexRange, true)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } $aFetchItems = \MailSo\Imap\Enumerations\FetchType::ChangeFetchItemsBefourRequest($aInputFetchItems); foreach ($aFetchItems as $sName => $mItem) { if (0 < strlen($sName) && '' !== $mItem) { if (null === $this->aFetchCallbacks) { $this->aFetchCallbacks = array(); } $this->aFetchCallbacks[$sName] = $mItem; } } $this->SendRequest((($bIndexIsUid) ? 'UID ' : '').'FETCH', array($sIndexRange, array_keys($aFetchItems))); $aResult = $this->validateResponse($this->parseResponse()); $this->aFetchCallbacks = null; $aReturn = array(); $oImapResponse = null; foreach ($aResult as &$oImapResponse) { if (FetchResponse::IsValidFetchImapResponse($oImapResponse)) { $aReturn[] = FetchResponse::NewInstance($oImapResponse); } } return $aReturn; } /** * @return array|false * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function Quota() { $aReturn = false; if ($this->IsSupported('QUOTA')) { $this->SendRequest('GETQUOTAROOT "INBOX"'); $aResult = $this->parseResponseWithValidation(); $aReturn = array(0, 0); $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && 'QUOTA' === $oImapResponse->StatusOrIndex && is_array($oImapResponse->ResponseList) && isset($oImapResponse->ResponseList[3]) && is_array($oImapResponse->ResponseList[3]) && 2 < count($oImapResponse->ResponseList[3]) && 'STORAGE' === strtoupper($oImapResponse->ResponseList[3][0]) && is_numeric($oImapResponse->ResponseList[3][1]) && is_numeric($oImapResponse->ResponseList[3][2]) ) { $aReturn = array( (int) $oImapResponse->ResponseList[3][1], (int) $oImapResponse->ResponseList[3][2] ); } } } return $aReturn; } /** * @param array $aSortTypes * @param string $sSearchCriterias * @param bool $bReturnUid * * @return array * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageSimpleSort($aSortTypes, $sSearchCriterias = 'ALL', $bReturnUid = true) { $sCommandPrefix = ($bReturnUid) ? 'UID ' : ''; $sSearchCriterias = !\MailSo\Base\Validator::NotEmptyString($sSearchCriterias, true) || '*' === $sSearchCriterias ? 'ALL' : $sSearchCriterias; if (!is_array($aSortTypes) || 0 === count($aSortTypes)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } else if (!$this->IsSupported('SORT')) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } $aRequest = array(); $aRequest[] = $aSortTypes; $aRequest[] = \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? 'US-ASCII' : 'UTF-8'; $aRequest[] = $sSearchCriterias; $sCmd = 'SORT'; $this->SendRequest($sCommandPrefix.$sCmd, $aRequest); $aResult = $this->parseResponseWithValidation(); $aReturn = array(); $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && $sCmd === $oImapResponse->StatusOrIndex && is_array($oImapResponse->ResponseList) && 2 < count($oImapResponse->ResponseList)) { for ($iIndex = 2, $iLen = count($oImapResponse->ResponseList); $iIndex < $iLen; $iIndex++) { $aReturn[] = (int) $oImapResponse->ResponseList[$iIndex]; } } } $aReturn = array_reverse($aReturn); return $aReturn; } /** * @param string $sSearchCriterias * @param bool $bReturnUid * @param string $sCharset = '' * * @return array * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageSimpleSearch($sSearchCriterias = 'ALL', $bReturnUid = true, $sCharset = '') { $sCommandPrefix = ($bReturnUid) ? 'UID ' : ''; $sSearchCriterias = 0 === strlen($sSearchCriterias) || '*' === $sSearchCriterias ? 'ALL' : $sSearchCriterias; $aRequest = array(); if (0 < strlen($sCharset)) { $aRequest[] = 'CHARSET'; $aRequest[] = strtoupper($sCharset); } $aRequest[] = $sSearchCriterias; $sCmd = 'SEARCH'; $this->SendRequest($sCommandPrefix.$sCmd, $aRequest); $aResult = $this->parseResponseWithValidation(); $aReturn = array(); $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && $sCmd === $oImapResponse->StatusOrIndex && is_array($oImapResponse->ResponseList) && 2 < count($oImapResponse->ResponseList)) { for ($iIndex = 2, $iLen = count($oImapResponse->ResponseList); $iIndex < $iLen; $iIndex++) { $aReturn[] = (int) $oImapResponse->ResponseList[$iIndex]; } } } $aReturn = array_reverse($aReturn); return $aReturn; } /** * @param mixed $aValue * * @return mixed */ private function validateThreadItem($aValue) { $mResult = false; if (is_numeric($aValue)) { $mResult = (int) $aValue; } else if (is_array($aValue)) { if (1 === count($aValue) && is_numeric($aValue[0])) { $mResult = (int) $aValue[0]; } else { $mResult = array(); foreach ($aValue as $aValueItem) { $mResult[] = $this->validateThreadItem($aValueItem); } } } return $mResult; } /** * @param string $sSearchCriterias = 'ALL' * @param bool $bReturnUid = true * @param string $sCharset = \MailSo\Base\Enumerations\Charset::UTF_8 * * @return array * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageSimpleThread($sSearchCriterias = 'ALL', $bReturnUid = true, $sCharset = \MailSo\Base\Enumerations\Charset::UTF_8) { $sCommandPrefix = ($bReturnUid) ? 'UID ' : ''; $sSearchCriterias = !\MailSo\Base\Validator::NotEmptyString($sSearchCriterias, true) || '*' === $sSearchCriterias ? 'ALL' : $sSearchCriterias; $sThreadType = ''; switch (true) { case $this->IsSupported('THREAD=REFS'): $sThreadType = 'REFS'; break; case $this->IsSupported('THREAD=REFERENCES'): $sThreadType = 'REFERENCES'; break; case $this->IsSupported('THREAD=ORDEREDSUBJECT'): $sThreadType = 'ORDEREDSUBJECT'; break; default: $this->writeLogException( new Exceptions\RuntimeException('Thread is not supported'), \MailSo\Log\Enumerations\Type::ERROR, true); break; } $aRequest = array(); $aRequest[] = $sThreadType; $aRequest[] = strtoupper($sCharset); $aRequest[] = $sSearchCriterias; $sCmd = 'THREAD'; $this->SendRequest($sCommandPrefix.$sCmd, $aRequest); $aResult = $this->parseResponseWithValidation(); $aReturn = array(); $oImapResponse = null; foreach ($aResult as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && $sCmd === $oImapResponse->StatusOrIndex && is_array($oImapResponse->ResponseList) && 2 < count($oImapResponse->ResponseList)) { for ($iI = 2, $iC = count($oImapResponse->ResponseList); $iI < $iC; $iI++) { $aNewValue = $this->validateThreadItem($oImapResponse->ResponseList[$iI]); if (false !== $aNewValue) { $aReturn[] = $aNewValue; } } } } return $aReturn; } /** * @param string $sToFolder * @param string $sIndexRange * @param bool $bIndexIsUid * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageCopy($sToFolder, $sIndexRange, $bIndexIsUid) { if (0 === strlen($sIndexRange)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } $sCommandPrefix = ($bIndexIsUid) ? 'UID ' : ''; return $this->SendRequestWithCheck($sCommandPrefix.'COPY', array($sIndexRange, $this->EscapeString($sToFolder))); } /** * @param string $sToFolder * @param string $sIndexRange * @param bool $bIndexIsUid * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageMove($sToFolder, $sIndexRange, $bIndexIsUid) { if (0 === strlen($sIndexRange)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } if (!$this->IsSupported('MOVE')) { $this->writeLogException( new Exceptions\RuntimeException('Move is not supported'), \MailSo\Log\Enumerations\Type::ERROR, true); } $sCommandPrefix = ($bIndexIsUid) ? 'UID ' : ''; return $this->SendRequestWithCheck($sCommandPrefix.'MOVE', array($sIndexRange, $this->EscapeString($sToFolder))); } /** * @param string $sUidRangeIfSupported * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageExpunge($sUidRangeIfSupported = '', $bForceUidExpunge = false) { $sUidRangeIfSupported = trim($sUidRangeIfSupported); $sCmd = 'EXPUNGE'; $aArguments = array(); if ($bForceUidExpunge && 0 < strlen($sUidRangeIfSupported) && $this->IsSupported('UIDPLUS')) { $sCmd = 'UID '.$sCmd; $aArguments = array($sUidRangeIfSupported); } return $this->SendRequestWithCheck($sCmd, $aArguments); } /** * @param string $sIndexRange * @param bool $bIndexIsUid * @param array $aInputStoreItems * @param string $sStoreAction * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageStoreFlag($sIndexRange, $bIndexIsUid, $aInputStoreItems, $sStoreAction) { if (!\MailSo\Base\Validator::NotEmptyString($sIndexRange, true) || !\MailSo\Base\Validator::NotEmptyString($sStoreAction, true) || 0 === count($aInputStoreItems)) { return false; } $sCmd = ($bIndexIsUid) ? 'UID STORE' : 'STORE'; return $this->SendRequestWithCheck($sCmd, array($sIndexRange, $sStoreAction, $aInputStoreItems)); } /** * @param string $sFolderName * @param resource $rMessageAppendStream * @param int $iStreamSize * @param array $aAppendFlags = null * @param int $iUid = null * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function MessageAppendStream($sFolderName, $rMessageAppendStream, $iStreamSize, $aAppendFlags = null, &$iUid = null) { $this->SendRequest('APPEND', array($this->EscapeString($sFolderName), $aAppendFlags, '{'.$iStreamSize.'}')); $this->parseResponseWithValidation('+'); $this->writeLog('Write to connection stream', \MailSo\Log\Enumerations\Type::NOTE); \MailSo\Base\Utils::MultipleStreamWriter($rMessageAppendStream, array($this->rConnect)); $this->sendRaw(''); $this->parseResponseWithValidation(); if (null !== $iUid) { $aLastResponse = $this->GetLastResponse(); if (is_array($aLastResponse) && 0 < count($aLastResponse) && $aLastResponse[count($aLastResponse) - 1]) { $oLast = $aLastResponse[count($aLastResponse) - 1]; if ($oLast && \MailSo\Imap\Enumerations\ResponseType::TAGGED === $oLast->ResponseType && is_array($oLast->OptionalResponse)) { if (0 < strlen($oLast->OptionalResponse[0]) && 0 < strlen($oLast->OptionalResponse[2]) && 'APPENDUID' === strtoupper($oLast->OptionalResponse[0]) && is_numeric($oLast->OptionalResponse[2]) ) { $iUid = (int) $oLast->OptionalResponse[2]; } } } } return $this; } /** * @return \MailSo\Imap\FolderInformation */ public function FolderCurrentInformation() { return $this->oCurrentFolderInfo; } /** * @param string $sCommand * @param array $aParams = array() * * @return void * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception */ public function SendRequest($sCommand, $aParams = array()) { if (!\MailSo\Base\Validator::NotEmptyString($sCommand, true) || !is_array($aParams)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->IsConnected(true); $sTag = $this->getNewTag(); $sCommand = trim($sCommand); $sRealCommand = $sTag.' '.$sCommand.$this->prepearParamLine($aParams); $sFakeCommand = ''; $aFakeParams = $this->secureRequestParams($sCommand, $aParams); if (null !== $aFakeParams) { $sFakeCommand = $sTag.' '.$sCommand.$this->prepearParamLine($aFakeParams); } $this->aTagTimeouts[$sTag] = microtime(true); $this->sendRaw($sRealCommand, true, $sFakeCommand); } /** * @param string $sCommand * @param array $aParams * * @return array|null */ private function secureRequestParams($sCommand, $aParams) { $aResult = null; switch ($sCommand) { case 'LOGIN': $aResult = $aParams; if (is_array($aResult) && 2 === count($aResult)) { $aResult[1] = '"*******"'; } break; } return $aResult; } /** * @param string $sCommand * @param array $aParams = array() * @param bool $bFindCapa = false * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception */ public function SendRequestWithCheck($sCommand, $aParams = array(), $bFindCapa = false) { $this->SendRequest($sCommand, $aParams); $this->parseResponseWithValidation(null, $bFindCapa); return $this; } /** * @return array */ public function GetLastResponse() { return $this->aLastResponse; } /** * @param mixed $aResult * * @return array * * @throws \MailSo\Imap\Exceptions\ResponseNotFoundException * @throws \MailSo\Imap\Exceptions\InvalidResponseException * @throws \MailSo\Imap\Exceptions\NegativeResponseException */ private function validateResponse($aResult) { if (!is_array($aResult) || 0 === $iCnt = count($aResult)) { $this->writeLogException( new Exceptions\ResponseNotFoundException(), \MailSo\Log\Enumerations\Type::WARNING, true); } if ($aResult[$iCnt - 1]->ResponseType !== \MailSo\Imap\Enumerations\ResponseType::CONTINUATION) { if (!$aResult[$iCnt - 1]->IsStatusResponse) { $this->writeLogException( new Exceptions\InvalidResponseException($aResult), \MailSo\Log\Enumerations\Type::WARNING, true); } if (\MailSo\Imap\Enumerations\ResponseStatus::OK !== $aResult[$iCnt - 1]->StatusOrIndex) { $this->writeLogException( new Exceptions\NegativeResponseException($aResult), \MailSo\Log\Enumerations\Type::WARNING, true); } } return $aResult; } /** * @param string $sEndTag = null * @param bool $bFindCapa = false * * @return array|bool */ protected function parseResponse($sEndTag = null, $bFindCapa = false) { if (is_resource($this->rConnect)) { $oImapResponse = null; $sEndTag = (null === $sEndTag) ? $this->getCurrentTag() : $sEndTag; while (true) { $oImapResponse = Response::NewInstance(); $this->partialParseResponseBranch($oImapResponse); if ($oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNKNOWN === $oImapResponse->ResponseType) { return false; } if ($bFindCapa) { // $this->oLogger->WriteDump($oImapResponse); $this->initCapabilityImapResponse($oImapResponse); } $this->aPartialResponses[] = $oImapResponse; if ($sEndTag === $oImapResponse->Tag || \MailSo\Imap\Enumerations\ResponseType::CONTINUATION === $oImapResponse->ResponseType) { if (isset($this->aTagTimeouts[$sEndTag])) { $this->writeLog((microtime(true) - $this->aTagTimeouts[$sEndTag]).' ('.$sEndTag.')', \MailSo\Log\Enumerations\Type::TIME); unset($this->aTagTimeouts[$sEndTag]); } break; } } else { return false; } unset($oImapResponse); } } $this->iResponseBufParsedPos = 0; $this->aLastResponse = $this->aPartialResponses; $this->aPartialResponses = array(); return $this->aLastResponse; } /** * @param string $sEndTag = null * @param bool $bFindCapa = false * * @return array */ private function parseResponseWithValidation($sEndTag = null, $bFindCapa = false) { return $this->validateResponse($this->parseResponse($sEndTag, $bFindCapa)); } /** * @param \MailSo\Imap\Response $oImapResponse * * @return void */ private function initCapabilityImapResponse($oImapResponse) { if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType && is_array($oImapResponse->ResponseList)) { $aList = null; if (isset($oImapResponse->ResponseList[1]) && is_string($oImapResponse->ResponseList[1]) && 'CAPABILITY' === strtoupper($oImapResponse->ResponseList[1])) { $aList = array_slice($oImapResponse->ResponseList, 2); } else if ($oImapResponse->OptionalResponse && is_array($oImapResponse->OptionalResponse) && 1 < count($oImapResponse->OptionalResponse) && is_string($oImapResponse->OptionalResponse[0]) && 'CAPABILITY' === strtoupper($oImapResponse->OptionalResponse[0])) { $aList = array_slice($oImapResponse->OptionalResponse, 1); } if (is_array($aList) && 0 < count($aList)) { $this->aCapabilityItems = array_map('strtoupper', $aList); } } } /** * @return array|string * * @throws \MailSo\Net\Exceptions\Exception */ private function partialParseResponseBranch(&$oImapResponse, $iStackIndex = -1, $bTreatAsAtom = false, $sParentToken = '') { $mNull = null; $iStackIndex++; $iPos = $this->iResponseBufParsedPos; $sPreviousAtomUpperCase = null; $bIsEndOfList = false; $bIsClosingBracketSquare = false; $iLiteralLen = 0; $iBufferEndIndex = 0; $rImapLiteralStream = null; $bIsGotoDefault = false; $bIsGotoLiteral = false; $bIsGotoLiteralEnd = false; $bIsGotoAtomBracket = false; $bIsGotoNotAtomBracket = false; $bCountOneInited = false; $bCountTwoInited = false; $sAtomBuilder = $bTreatAsAtom ? '' : null; $aList = array(); if (null !== $oImapResponse) { $aList =& $oImapResponse->ResponseList; } while (!$bIsEndOfList) { if ($this->bNeedNext) { $iPos = 0; $this->getNextBuffer(); $this->iResponseBufParsedPos = $iPos; $this->bNeedNext = false; } $sChar = null; if ($bIsGotoDefault) { $sChar = 'GOTO_DEFAULT'; $bIsGotoDefault = false; } else if ($bIsGotoLiteral) { $bIsGotoLiteral = false; $bIsGotoLiteralEnd = true; if ($this->partialResponseLiteralCallbackCallable( $sParentToken, null === $sPreviousAtomUpperCase ? '' : strtoupper($sPreviousAtomUpperCase), $this->rConnect, $iLiteralLen)) { if (!$bTreatAsAtom) { $aList[] = ''; } } else { $sLiteral = ''; $iRead = $iLiteralLen; while (0 < $iRead) { $sAddRead = fread($this->rConnect, $iRead); if (false === $sAddRead) { $sLiteral = false; break; } $sLiteral .= $sAddRead; $iRead -= strlen($sAddRead); } if (false !== $sLiteral) { $iLiteralSize = strlen($sLiteral); \MailSo\Base\Loader::IncStatistic('NetRead', $iLiteralSize); if ($iLiteralLen !== $iLiteralSize) { $this->writeLog('Literal stream read warning "read '.$iLiteralSize.' of '. $iLiteralLen.'" bytes', \MailSo\Log\Enumerations\Type::WARNING); } if (!$bTreatAsAtom) { $aList[] = $sLiteral; } } else { $this->writeLog('Can\'t read imap stream', \MailSo\Log\Enumerations\Type::NOTE); } unset($sLiteral); } continue; } else if ($bIsGotoLiteralEnd) { $rImapLiteralStream = null; $sPreviousAtomUpperCase = null; $this->bNeedNext = true; $bIsGotoLiteralEnd = false; continue; } else if ($bIsGotoAtomBracket) { if ($bTreatAsAtom) { $sAtomBlock = $this->partialParseResponseBranch($mNull, $iStackIndex, true, null === $sPreviousAtomUpperCase ? '' : strtoupper($sPreviousAtomUpperCase)); $sAtomBuilder .= $sAtomBlock; $iPos = $this->iResponseBufParsedPos; $sAtomBuilder .= ($bIsClosingBracketSquare) ? ']' : ')'; } $sPreviousAtomUpperCase = null; $bIsGotoAtomBracket = false; continue; } else if ($bIsGotoNotAtomBracket) { $aSubItems = $this->partialParseResponseBranch($mNull, $iStackIndex, false, null === $sPreviousAtomUpperCase ? '' : strtoupper($sPreviousAtomUpperCase)); $aList[] = $aSubItems; $iPos = $this->iResponseBufParsedPos; $sPreviousAtomUpperCase = null; if (null !== $oImapResponse && $oImapResponse->IsStatusResponse) { $oImapResponse->OptionalResponse = $aSubItems; // Got optional response in status response, the rest of the line is human-readable text. $bIsGotoDefault = true; $bIsGotoNotAtomBracket = false; continue; } $bIsGotoNotAtomBracket = false; continue; } else { $iBufferEndIndex = strlen($this->sResponseBuffer) - 3; $this->bResponseBufferChanged = false; if ($iPos > $iBufferEndIndex) { break; } $sChar = $this->sResponseBuffer[$iPos]; } switch ($sChar) { case ']': case ')': $iPos++; $sPreviousAtomUpperCase = null; $bIsEndOfList = true; break; case ' ': if ($bTreatAsAtom) { $sAtomBuilder .= ' '; } $iPos++; break; case '[': $bIsClosingBracketSquare = true; case '(': if ($bTreatAsAtom) { $sAtomBuilder .= ($bIsClosingBracketSquare) ? '[' : '('; } $iPos++; $this->iResponseBufParsedPos = $iPos; if ($bTreatAsAtom) { $bIsGotoAtomBracket = true; } else { $bIsGotoNotAtomBracket = true; } break; case '{': $bIsLiteralParsed = false; $mLiteralEndPos = strpos($this->sResponseBuffer, '}', $iPos); if (false !== $mLiteralEndPos && $mLiteralEndPos > $iPos) { $sLiteralLenAsString = substr($this->sResponseBuffer, $iPos + 1, $mLiteralEndPos - $iPos - 1); if (is_numeric($sLiteralLenAsString)) { // Detected literal size $iLiteralLen = (int) $sLiteralLenAsString; $bIsLiteralParsed = true; $iPos = $mLiteralEndPos + 3; $bIsGotoLiteral = true; break; } } if (!$bIsLiteralParsed) { // On error parsing literal, skip the problem place. $iPos = $iBufferEndIndex; } $sPreviousAtomUpperCase = null; break; case '"': $bIsQuotedParsed = false; while (true) { $iClosingPos = $iPos + 1; if ($iClosingPos > $iBufferEndIndex) { // Closing '=' will not fit even if it immediately follows the opening one. break; } while (true) { $iClosingPos = strpos($this->sResponseBuffer, '"', $iClosingPos); if (false === $iClosingPos) { break; } // $iSlashCount will contain the number of back-slashes before closing quote. $iSlashCount = 0; while ('\\' === $this->sResponseBuffer[$iClosingPos - $iSlashCount - 1]) { $iSlashCount++; } // Even number of back-slashes denotes encoded slashes. Odd number denotes // zero or more encoded back-slashes and an encoded quote. if ($iSlashCount % 2 == 1) { $iClosingPos++; // That was encoded quote - \", not actually the end of quoted string. continue; } else { break; } } if (false === $iClosingPos) { break; } else { // $iSkipClosingPos = 0; $bIsQuotedParsed = true; if ($bTreatAsAtom) { // Quoted string is copied as-is, including quotes. $sAtomBuilder .= strtr( substr($this->sResponseBuffer, $iPos, $iClosingPos - $iPos + 1), array('\\\\' => '\\', '\\"' => '"') ); } else { $aList[] = strtr( substr($this->sResponseBuffer, $iPos + 1, $iClosingPos - $iPos - 1), array('\\\\' => '\\', '\\"' => '"') ); } $iPos = $iClosingPos + 1; break; } } if (!$bIsQuotedParsed) { // On error parsing literal, skip the problem place. $iPos = $iBufferEndIndex; } $sPreviousAtomUpperCase = null; break; case 'GOTO_DEFAULT': default: // Deal with atoms and human-readable text here. $iCharBlockStartPos = $iPos; if (null !== $oImapResponse && $oImapResponse->IsStatusResponse) { // In case of status response, any atom after first two atoms (and possibly 1 optional response) // means human-readable text till the end of line. Special thing about atoms in human-readble // text is that they are not atoms actually. They can be zero-length and can contain spaces and // other delimiters. So we do some kind of special processing for them. Thus, we declare everything // after known atoms and optional response is human-readable text. $iPos = $iBufferEndIndex; while ($iPos > $iCharBlockStartPos && $this->sResponseBuffer[$iCharBlockStartPos] == ' ') { // Remove trailing space in the beginning of the human-readable text. $iCharBlockStartPos++; } } $bIsAtomDone = false; while (!$bIsAtomDone && ($iPos <= $iBufferEndIndex)) { $sCharDef = $this->sResponseBuffer[$iPos]; switch ($sCharDef) { case '[': if (null === $sAtomBuilder) { // We create StringBuilder only if required. Two cases possible: // 1) treatAsString mode (atom is StringBuilder from the very beginning) // 2) we got '[' as non-first symbol in response data // We could create StringBuilder any time we encounter an atom in response data // but it would be the waste of resources (most atoms in response data are simple // char blocks with no special symbols and thus do not require concatenation). $sAtomBuilder = ''; } // Append BODY[ from BODY[...] response. $sAtomBuilder .= substr($this->sResponseBuffer, $iCharBlockStartPos, $iPos - $iCharBlockStartPos + 1); $iPos++; $this->iResponseBufParsedPos = $iPos; $sListBlock = $this->partialParseResponseBranch($mNull, $iStackIndex, true, null === $sPreviousAtomUpperCase ? '' : strtoupper($sPreviousAtomUpperCase)); if (null !== $sListBlock) { $sAtomBuilder .= $sListBlock.']'; } $iPos = $this->iResponseBufParsedPos; $iCharBlockStartPos = $iPos; break; case ' ': case ']': case ')': $bIsAtomDone = true; break; default: $iPos++; break; } } // If pos == charBlockStartPos (zero length) and nothing stored in atomBuilder, this means // we got no human-readable text in status response. In all other cases, pos > charBlockStartPos // because real atoms are always not empty. if ($iPos > $iCharBlockStartPos || null !== $sAtomBuilder) { $sLastCharBlock = substr($this->sResponseBuffer, $iCharBlockStartPos, $iPos - $iCharBlockStartPos); if (null === $sAtomBuilder) { // When atomBuilder is null, this also means !treatAsAtom. $aList[] = $sLastCharBlock; // $this->writeLog($sLastCharBlock); $sPreviousAtomUpperCase = $sLastCharBlock; } else { // This case is never top-level case (when response != null). We get here only // when parsing internals of BODY[...] literals. $sAtomBuilder .= $sLastCharBlock; if (!$bTreatAsAtom) { $aList[] = $sAtomBuilder; $sPreviousAtomUpperCase = $sAtomBuilder; $sAtomBuilder = null; } } if (null !== $oImapResponse) { // if (1 === count($aList)) if (!$bCountOneInited && 1 === count($aList)) // if (isset($aList[0]) && !isset($aList[1])) // fast 1 === count($aList) { $bCountOneInited = true; $oImapResponse->Tag = $aList[0]; if ('+' === $oImapResponse->Tag) { $oImapResponse->ResponseType = \MailSo\Imap\Enumerations\ResponseType::CONTINUATION; } else if ('*' === $oImapResponse->Tag) { $oImapResponse->ResponseType = \MailSo\Imap\Enumerations\ResponseType::UNTAGGED; } else if ($this->getCurrentTag() === $oImapResponse->Tag) { $oImapResponse->ResponseType = \MailSo\Imap\Enumerations\ResponseType::TAGGED; } else { $oImapResponse->ResponseType = \MailSo\Imap\Enumerations\ResponseType::UNKNOWN; } } // else if (2 === count($aList)) else if (!$bCountTwoInited && 2 === count($aList)) // else if (isset($aList[1]) && !isset($aList[2])) // fast 2 === count($aList) { $bCountTwoInited = true; $oImapResponse->StatusOrIndex = strtoupper($aList[1]); if ($oImapResponse->StatusOrIndex == \MailSo\Imap\Enumerations\ResponseStatus::OK || $oImapResponse->StatusOrIndex == \MailSo\Imap\Enumerations\ResponseStatus::NO || $oImapResponse->StatusOrIndex == \MailSo\Imap\Enumerations\ResponseStatus::BAD || $oImapResponse->StatusOrIndex == \MailSo\Imap\Enumerations\ResponseStatus::BYE || $oImapResponse->StatusOrIndex == \MailSo\Imap\Enumerations\ResponseStatus::PREAUTH) { $oImapResponse->IsStatusResponse = true; } } else if (\MailSo\Imap\Enumerations\ResponseType::CONTINUATION === $oImapResponse->ResponseType) { $oImapResponse->HumanReadable = $sLastCharBlock; } else if ($oImapResponse->IsStatusResponse) { $oImapResponse->HumanReadable = $sLastCharBlock; } } } } } $this->iResponseBufParsedPos = $iPos; if (null !== $oImapResponse) { $this->bNeedNext = true; $this->iResponseBufParsedPos = 0; } return $bTreatAsAtom ? $sAtomBuilder : $aList; } /** * @param string $sParent * @param string $sLiteralAtomUpperCase * @param resource $rImapStream * @param int $iLiteralLen * * @return bool */ private function partialResponseLiteralCallbackCallable($sParent, $sLiteralAtomUpperCase, $rImapStream, $iLiteralLen) { $sLiteralAtomUpperCasePeek = ''; if (0 === strpos($sLiteralAtomUpperCase, 'BODY')) { $sLiteralAtomUpperCasePeek = str_replace('BODY', 'BODY.PEEK', $sLiteralAtomUpperCase); } $sFetchKey = ''; if (is_array($this->aFetchCallbacks)) { if (0 < strlen($sLiteralAtomUpperCasePeek) && isset($this->aFetchCallbacks[$sLiteralAtomUpperCasePeek])) { $sFetchKey = $sLiteralAtomUpperCasePeek; } else if (0 < strlen($sLiteralAtomUpperCase) && isset($this->aFetchCallbacks[$sLiteralAtomUpperCase])) { $sFetchKey = $sLiteralAtomUpperCase; } } $bResult = false; if (0 < \strlen($sFetchKey) && '' !== $this->aFetchCallbacks[$sFetchKey] && \is_callable($this->aFetchCallbacks[$sFetchKey])) { $rImapLiteralStream = \MailSo\Base\StreamWrappers\Literal::CreateStream($rImapStream, $iLiteralLen); $bResult = true; $this->writeLog('Callback for '.$sParent.' / '.$sLiteralAtomUpperCase. ' - try to read '.$iLiteralLen.' bytes.', \MailSo\Log\Enumerations\Type::NOTE); \call_user_func($this->aFetchCallbacks[$sFetchKey], $sParent, $sLiteralAtomUpperCase, $rImapLiteralStream); $iTimer = 0; $iNotReadLiteralLen = 0; while (!\feof($rImapLiteralStream)) { $sBuf = \fread($rImapLiteralStream, 8192); if (false !== $sBuf) { \MailSo\Base\Utils::ResetTimeLimit($iTimer); $iNotReadLiteralLen += \strlen($sBuf); continue; } break; } if ($iNotReadLiteralLen > 0) { $this->writeLog('Not read literal size '.$iNotReadLiteralLen.' bytes.', \MailSo\Log\Enumerations\Type::WARNING); } \MailSo\Base\Loader::IncStatistic('NetRead', $iLiteralLen); if (\is_resource($rImapLiteralStream)) { \fclose($rImapLiteralStream); } } return $bResult; } /** * @param array $aParams = null * * @return string */ private function prepearParamLine($aParams = array()) { $sReturn = ''; if (\is_array($aParams) && 0 < \count($aParams)) { foreach ($aParams as $mParamItem) { if (\is_array($mParamItem) && 0 < \count($mParamItem)) { $sReturn .= ' ('.\trim($this->prepearParamLine($mParamItem)).')'; } else if (\is_string($mParamItem)) { $sReturn .= ' '.$mParamItem; } } } return $sReturn; } /** * @return string */ private function getNewTag() { $this->iTagCount++; return $this->getCurrentTag(); } /** * @return string */ private function getCurrentTag() { return self::TAG_PREFIX.$this->iTagCount; } /** * @param string $sStringForEscape * * @return string */ public function EscapeString($sStringForEscape) { return '"'.str_replace(array('\\', '"'), array('\\\\', '\\"'), $sStringForEscape).'"'; } /** * @return string */ protected function getLogName() { return 'IMAP'; } /** * @param \MailSo\Log\Logger $oLogger * * @return \MailSo\Imap\ImapClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException */ public function SetLogger($oLogger) { parent::SetLogger($oLogger); return $this; } /** * @param resource $rConnect * @param array $aCapabilityItems = array() * * @return \MailSo\Imap\ImapClient */ public function TestSetValues($rConnect, $aCapabilityItems = array()) { $this->rConnect = $rConnect; $this->aCapabilityItems = $aCapabilityItems; return $this; } /** * @param string $sEndTag = null * @param string $bFindCapa = false * * @return array */ public function TestParseResponseWithValidationProxy($sEndTag = null, $bFindCapa = false) { return $this->parseResponseWithValidation($sEndTag, $bFindCapa); } }