1<?php 2 3/* 4 * This file is part of MailSo. 5 * 6 * (c) 2014 Usenko Timur 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace MailSo\Mail; 13 14/** 15 * @category MailSo 16 * @package Mail 17 */ 18class MailClient 19{ 20 /** 21 * @var \MailSo\Log\Logger 22 */ 23 private $oLogger; 24 25 /** 26 * @var \MailSo\Imap\ImapClient 27 */ 28 private $oImapClient; 29 30 /** 31 * @access private 32 */ 33 private function __construct() 34 { 35 $this->oLogger = null; 36 37 $this->oImapClient = \MailSo\Imap\ImapClient::NewInstance(); 38 $this->oImapClient->SetTimeOuts(10, \MailSo\Config::$ImapTimeout); 39 } 40 41 /** 42 * @return \MailSo\Mail\MailClient 43 */ 44 public static function NewInstance() 45 { 46 return new self(); 47 } 48 49 /** 50 * @return \MailSo\Imap\ImapClient 51 */ 52 public function ImapClient() 53 { 54 return $this->oImapClient; 55 } 56 57 /** 58 * @param string $sServerName 59 * @param int $iPort = 143 60 * @param int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT 61 * @param bool $bVerifySsl = false 62 * @param string $sClientCert = "" 63 * 64 * @return \MailSo\Mail\MailClient 65 * 66 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 67 * @throws \MailSo\Net\Exceptions\Exception 68 * @throws \MailSo\Imap\Exceptions\Exception 69 */ 70 public function Connect($sServerName, $iPort = 143, 71 $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT, $bVerifySsl = false, $bAllowSelfSigned = false, $sClientCert = '') 72 { 73 $this->oImapClient->Connect($sServerName, $iPort, $iSecurityType, $bVerifySsl, $bAllowSelfSigned, $sClientCert); 74 return $this; 75 } 76 77 /** 78 * @param string $sLogin 79 * @param string $sPassword 80 * @param string $sProxyAuthUser = '' 81 * @param bool $bUseAuthPlainIfSupported = true 82 * @param bool $bUseAuthCramMd5IfSupported = true 83 * 84 * @return \MailSo\Mail\MailClient 85 * 86 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 87 * @throws \MailSo\Net\Exceptions\Exception 88 * @throws \MailSo\Imap\Exceptions\LoginException 89 */ 90 public function Login($sLogin, $sPassword, $sProxyAuthUser = '', 91 $bUseAuthPlainIfSupported = true, $bUseAuthCramMd5IfSupported = true) 92 { 93 $this->oImapClient->Login($sLogin, $sPassword, $sProxyAuthUser, $bUseAuthPlainIfSupported, $bUseAuthCramMd5IfSupported); 94 return $this; 95 } 96 97 /** 98 * @param string $sXOAuth2Token 99 * 100 * @return \MailSo\Mail\MailClient 101 * 102 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 103 * @throws \MailSo\Net\Exceptions\Exception 104 * @throws \MailSo\Imap\Exceptions\LoginException 105 */ 106 public function LoginWithXOauth2($sXOAuth2Token) 107 { 108 $this->oImapClient->LoginWithXOauth2($sXOAuth2Token); 109 return $this; 110 } 111 112 /** 113 * @return \MailSo\Mail\MailClient 114 * 115 * @throws \MailSo\Net\Exceptions\Exception 116 */ 117 public function Logout() 118 { 119 $this->oImapClient->Logout(); 120 return $this; 121 } 122 123 /** 124 * @return \MailSo\Mail\MailClient 125 * 126 * @throws \MailSo\Net\Exceptions\Exception 127 */ 128 public function Disconnect() 129 { 130 $this->oImapClient->Disconnect(); 131 return $this; 132 } 133 134 /** 135 * @return \MailSo\Mail\MailClient 136 * 137 * @throws \MailSo\Net\Exceptions\Exception 138 */ 139 public function LogoutAndDisconnect() 140 { 141 return $this->Logout()->Disconnect(); 142 } 143 144 /** 145 * @return bool 146 */ 147 public function IsConnected() 148 { 149 return $this->oImapClient->IsConnected(); 150 } 151 152 /** 153 * @return bool 154 */ 155 public function IsLoggined() 156 { 157 return $this->oImapClient->IsLoggined(); 158 } 159 160 /** 161 * @return string 162 */ 163 private function getEnvelopeOrHeadersRequestStringForSimpleList() 164 { 165 return \MailSo\Imap\Enumerations\FetchType::BuildBodyCustomHeaderRequest(array( 166 \MailSo\Mime\Enumerations\Header::RETURN_PATH, 167 \MailSo\Mime\Enumerations\Header::RECEIVED, 168 \MailSo\Mime\Enumerations\Header::MIME_VERSION, 169 \MailSo\Mime\Enumerations\Header::FROM_, 170 \MailSo\Mime\Enumerations\Header::TO_, 171 \MailSo\Mime\Enumerations\Header::CC, 172 \MailSo\Mime\Enumerations\Header::SENDER, 173 \MailSo\Mime\Enumerations\Header::REPLY_TO, 174 \MailSo\Mime\Enumerations\Header::DATE, 175 \MailSo\Mime\Enumerations\Header::SUBJECT, 176 \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 177 \MailSo\Mime\Enumerations\Header::LIST_UNSUBSCRIBE, 178 ), true); 179 } 180 181 /** 182 * @return string 183 */ 184 private function getEnvelopeOrHeadersRequestString() 185 { 186 if (\MailSo\Config::$MessageAllHeaders) 187 { 188 return \MailSo\Imap\Enumerations\FetchType::BODY_HEADER_PEEK; 189 } 190 191 return \MailSo\Imap\Enumerations\FetchType::BuildBodyCustomHeaderRequest(array( 192 \MailSo\Mime\Enumerations\Header::RETURN_PATH, 193 \MailSo\Mime\Enumerations\Header::RECEIVED, 194 \MailSo\Mime\Enumerations\Header::MIME_VERSION, 195 \MailSo\Mime\Enumerations\Header::MESSAGE_ID, 196 \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 197 \MailSo\Mime\Enumerations\Header::FROM_, 198 \MailSo\Mime\Enumerations\Header::TO_, 199 \MailSo\Mime\Enumerations\Header::CC, 200 \MailSo\Mime\Enumerations\Header::BCC, 201 \MailSo\Mime\Enumerations\Header::SENDER, 202 \MailSo\Mime\Enumerations\Header::REPLY_TO, 203 \MailSo\Mime\Enumerations\Header::DELIVERED_TO, 204 \MailSo\Mime\Enumerations\Header::IN_REPLY_TO, 205 \MailSo\Mime\Enumerations\Header::REFERENCES, 206 \MailSo\Mime\Enumerations\Header::DATE, 207 \MailSo\Mime\Enumerations\Header::SUBJECT, 208 \MailSo\Mime\Enumerations\Header::SENSITIVITY, 209 \MailSo\Mime\Enumerations\Header::X_MSMAIL_PRIORITY, 210 \MailSo\Mime\Enumerations\Header::IMPORTANCE, 211 \MailSo\Mime\Enumerations\Header::X_PRIORITY, 212 \MailSo\Mime\Enumerations\Header::X_DRAFT_INFO, 213 \MailSo\Mime\Enumerations\Header::RETURN_RECEIPT_TO, 214 \MailSo\Mime\Enumerations\Header::DISPOSITION_NOTIFICATION_TO, 215 \MailSo\Mime\Enumerations\Header::X_CONFIRM_READING_TO, 216 \MailSo\Mime\Enumerations\Header::AUTHENTICATION_RESULTS, 217 \MailSo\Mime\Enumerations\Header::X_DKIM_AUTHENTICATION_RESULTS, 218 \MailSo\Mime\Enumerations\Header::LIST_UNSUBSCRIBE, 219 ), true); 220// 221// return \MailSo\Imap\Enumerations\FetchType::ENVELOPE; 222 } 223 224 /** 225 * @param string $sFolderName 226 * @param string $sMessageFlag 227 * @param bool $bSetAction = true 228 * @param bool $sSkipUnsupportedFlag = false 229 * @param array $aCustomUids = null 230 * 231 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 232 * @throws \MailSo\Net\Exceptions\Exception 233 * @throws \MailSo\Imap\Exceptions\Exception 234 * @throws \MailSo\Mail\Exceptions\Exception 235 */ 236 public function MessageSetFlagToAll($sFolderName, $sMessageFlag, $bSetAction = true, $sSkipUnsupportedFlag = false, $aCustomUids = null) 237 { 238 $this->oImapClient->FolderSelect($sFolderName); 239 240 $oFolderInfo = $this->oImapClient->FolderCurrentInformation(); 241 if (!$oFolderInfo || !$oFolderInfo->IsFlagSupported($sMessageFlag)) 242 { 243 if (!$sSkipUnsupportedFlag) 244 { 245 throw new \MailSo\Mail\Exceptions\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.'); 246 } 247 } 248 249 if ($oFolderInfo && 0 < $oFolderInfo->Exists) 250 { 251 $sStoreAction = $bSetAction 252 ? \MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT 253 : \MailSo\Imap\Enumerations\StoreAction::REMOVE_FLAGS_SILENT 254 ; 255 256 if (is_array($aCustomUids)) 257 { 258 if (0 < count($aCustomUids)) 259 { 260 $this->oImapClient->MessageStoreFlag(implode(',', $aCustomUids), true, array($sMessageFlag), $sStoreAction); 261 } 262 } 263 else 264 { 265 $this->oImapClient->MessageStoreFlag('1:*', false, array($sMessageFlag), $sStoreAction); 266 } 267 } 268 } 269 270 /** 271 * @param string $sFolderName 272 * @param array $aIndexRange 273 * @param bool $bIndexIsUid 274 * @param string $sMessageFlag 275 * @param bool $bSetAction = true 276 * @param bool $sSkipUnsupportedFlag = false 277 * 278 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 279 * @throws \MailSo\Net\Exceptions\Exception 280 * @throws \MailSo\Imap\Exceptions\Exception 281 * @throws \MailSo\Mail\Exceptions\Exception 282 */ 283 public function MessageSetFlag($sFolderName, $aIndexRange, $bIndexIsUid, $sMessageFlag, $bSetAction = true, $sSkipUnsupportedFlag = false) 284 { 285 $this->oImapClient->FolderSelect($sFolderName); 286 287 $oFolderInfo = $this->oImapClient->FolderCurrentInformation(); 288 if (!$oFolderInfo || !$oFolderInfo->IsFlagSupported($sMessageFlag)) 289 { 290 if (!$sSkipUnsupportedFlag) 291 { 292 throw new \MailSo\Mail\Exceptions\RuntimeException('Message flag "'.$sMessageFlag.'" is not supported.'); 293 } 294 } 295 else 296 { 297 $sStoreAction = $bSetAction 298 ? \MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT 299 : \MailSo\Imap\Enumerations\StoreAction::REMOVE_FLAGS_SILENT 300 ; 301 302 $this->oImapClient->MessageStoreFlag(\MailSo\Base\Utils::PrepearFetchSequence($aIndexRange), 303 $bIndexIsUid, array($sMessageFlag), $sStoreAction); 304 } 305 } 306 307 /** 308 * @param string $sFolderName 309 * @param array $aIndexRange 310 * @param bool $bIndexIsUid 311 * @param bool $bSetAction = true 312 * @param bool $sSkipUnsupportedFlag = false 313 * 314 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 315 * @throws \MailSo\Net\Exceptions\Exception 316 * @throws \MailSo\Imap\Exceptions\Exception 317 */ 318 public function MessageSetFlagged($sFolderName, $aIndexRange, $bIndexIsUid, $bSetAction = true, $sSkipUnsupportedFlag = false) 319 { 320 $this->MessageSetFlag($sFolderName, $aIndexRange, $bIndexIsUid, 321 \MailSo\Imap\Enumerations\MessageFlag::FLAGGED, $bSetAction, $sSkipUnsupportedFlag); 322 } 323 324 /** 325 * @param string $sFolderName 326 * @param bool $bSetAction = true 327 * @param array $aCustomUids = null 328 * 329 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 330 * @throws \MailSo\Net\Exceptions\Exception 331 * @throws \MailSo\Imap\Exceptions\Exception 332 */ 333 public function MessageSetSeenToAll($sFolderName, $bSetAction = true, $aCustomUids = null) 334 { 335 $this->MessageSetFlagToAll($sFolderName, \MailSo\Imap\Enumerations\MessageFlag::SEEN, $bSetAction, true, $aCustomUids); 336 } 337 338 /** 339 * @param string $sFolderName 340 * @param array $aIndexRange 341 * @param bool $bIndexIsUid 342 * @param bool $bSetAction = true 343 * 344 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 345 * @throws \MailSo\Net\Exceptions\Exception 346 * @throws \MailSo\Imap\Exceptions\Exception 347 */ 348 public function MessageSetSeen($sFolderName, $aIndexRange, $bIndexIsUid, $bSetAction = true) 349 { 350 $this->MessageSetFlag($sFolderName, $aIndexRange, $bIndexIsUid, 351 \MailSo\Imap\Enumerations\MessageFlag::SEEN, $bSetAction, true); 352 } 353 354 /** 355 * @param string $sFolderName 356 * @param int $iIndex 357 * @param bool $bIndexIsUid = true 358 * @param \MailSo\Cache\CacheClient $oCacher = null 359 * @param int $iBodyTextLimit = null 360 * 361 * @return \MailSo\Mail\Message|false 362 * 363 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 364 * @throws \MailSo\Net\Exceptions\Exception 365 * @throws \MailSo\Imap\Exceptions\Exception 366 */ 367 public function Message($sFolderName, $iIndex, $bIndexIsUid = true, $oCacher = null, $iBodyTextLimit = null) 368 { 369 if (!\MailSo\Base\Validator::RangeInt($iIndex, 1)) 370 { 371 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 372 } 373 374 $this->oImapClient->FolderSelect($sFolderName); 375 376 $oBodyStructure = null; 377 $oMessage = false; 378 379 $aBodyPeekMimeIndexes = array(); 380 $aSignatureMimeIndexes = array(); 381 382 $aFetchResponse = $this->oImapClient->Fetch(array(\MailSo\Imap\Enumerations\FetchType::BODYSTRUCTURE), $iIndex, $bIndexIsUid); 383 if (0 < \count($aFetchResponse) && isset($aFetchResponse[0])) 384 { 385 $oBodyStructure = $aFetchResponse[0]->GetFetchBodyStructure(); 386 if ($oBodyStructure) 387 { 388 $aTextParts = $oBodyStructure->SearchHtmlOrPlainParts(); 389 if (is_array($aTextParts) && 0 < \count($aTextParts)) 390 { 391 foreach ($aTextParts as $oPart) 392 { 393 $aBodyPeekMimeIndexes[] = array($oPart->PartID(), $oPart->Size()); 394 } 395 } 396 397 $aSignatureParts = $oBodyStructure->SearchByContentType('application/pgp-signature'); 398 if (is_array($aSignatureParts) && 0 < \count($aSignatureParts)) 399 { 400 foreach ($aSignatureParts as $oPart) 401 { 402 $aSignatureMimeIndexes[] = $oPart->PartID(); 403 } 404 } 405 } 406 } 407 408 $aFetchItems = array( 409 \MailSo\Imap\Enumerations\FetchType::INDEX, 410 \MailSo\Imap\Enumerations\FetchType::UID, 411 \MailSo\Imap\Enumerations\FetchType::RFC822_SIZE, 412 \MailSo\Imap\Enumerations\FetchType::INTERNALDATE, 413 \MailSo\Imap\Enumerations\FetchType::FLAGS, 414 $this->getEnvelopeOrHeadersRequestString() 415 ); 416 417 if (0 < \count($aBodyPeekMimeIndexes)) 418 { 419 foreach ($aBodyPeekMimeIndexes as $aTextMimeData) 420 { 421 $sLine = \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$aTextMimeData[0].']'; 422 if (\is_numeric($iBodyTextLimit) && 0 < $iBodyTextLimit && $iBodyTextLimit < $aTextMimeData[1]) 423 { 424 $sLine .= '<0.'.((int) $iBodyTextLimit).'>'; 425 } 426 427 $aFetchItems[] = $sLine; 428 } 429 } 430 431 if (0 < \count($aSignatureMimeIndexes)) 432 { 433 foreach ($aSignatureMimeIndexes as $sTextMimeIndex) 434 { 435 $aFetchItems[] = \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$sTextMimeIndex.']'; 436 } 437 } 438 439 if (!$oBodyStructure) 440 { 441 $aFetchItems[] = \MailSo\Imap\Enumerations\FetchType::BODYSTRUCTURE; 442 } 443 444 $aFetchResponse = $this->oImapClient->Fetch($aFetchItems, $iIndex, $bIndexIsUid); 445 if (0 < \count($aFetchResponse)) 446 { 447 $oMessage = \MailSo\Mail\Message::NewFetchResponseInstance( 448 $sFolderName, $aFetchResponse[0], $oBodyStructure); 449 } 450 451 return $oMessage; 452 } 453 454 /** 455 * @param mixed $mCallback 456 * @param string $sFolderName 457 * @param int $iIndex 458 * @param bool $bIndexIsUid = true, 459 * @param string $sMimeIndex = '' 460 * 461 * @return bool 462 * 463 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 464 * @throws \MailSo\Net\Exceptions\Exception 465 * @throws \MailSo\Imap\Exceptions\Exception 466 */ 467 public function MessageMimeStream($mCallback, $sFolderName, $iIndex, $bIndexIsUid = true, $sMimeIndex = '') 468 { 469 if (!is_callable($mCallback)) 470 { 471 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 472 } 473 474 $this->oImapClient->FolderSelect($sFolderName); 475 476 $sFileName = ''; 477 $sContentType = ''; 478 $sMailEncodingName = ''; 479 480 $sMimeIndex = trim($sMimeIndex); 481 $aFetchResponse = $this->oImapClient->Fetch(array( 482 0 === \strlen($sMimeIndex) 483 ? \MailSo\Imap\Enumerations\FetchType::BODY_HEADER_PEEK 484 : \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$sMimeIndex.'.MIME]' 485 ), $iIndex, $bIndexIsUid); 486 487 if (0 < \count($aFetchResponse)) 488 { 489 $sMime = $aFetchResponse[0]->GetFetchValue( 490 0 === \strlen($sMimeIndex) 491 ? \MailSo\Imap\Enumerations\FetchType::BODY_HEADER 492 : \MailSo\Imap\Enumerations\FetchType::BODY.'['.$sMimeIndex.'.MIME]' 493 ); 494 495 if (0 < \strlen($sMime)) 496 { 497 $oHeaders = \MailSo\Mime\HeaderCollection::NewInstance()->Parse($sMime); 498 499 if (0 < \strlen($sMimeIndex)) 500 { 501 $sFileName = $oHeaders->ParameterValue( 502 \MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 503 \MailSo\Mime\Enumerations\Parameter::FILENAME); 504 505 if (0 === \strlen($sFileName)) 506 { 507 $sFileName = $oHeaders->ParameterValue( 508 \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 509 \MailSo\Mime\Enumerations\Parameter::NAME); 510 } 511 512 $sMailEncodingName = $oHeaders->ValueByName( 513 \MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING); 514 515 $sContentType = $oHeaders->ValueByName( 516 \MailSo\Mime\Enumerations\Header::CONTENT_TYPE); 517 } 518 else 519 { 520 $sSubject = $oHeaders->ValueByName(\MailSo\Mime\Enumerations\Header::SUBJECT); 521 522 $sFileName = 0 === \strlen($sSubject) ? (string) $iIndex : $sSubject; 523 $sFileName .= '.eml'; 524 525 $sContentType = 'message/rfc822'; 526 } 527 } 528 } 529 530 $aFetchResponse = $this->oImapClient->Fetch(array( 531 array(\MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$sMimeIndex.']', 532 function ($sParent, $sLiteralAtomUpperCase, $rImapLiteralStream) use ($mCallback, $sMimeIndex, $sMailEncodingName, $sContentType, $sFileName) 533 { 534 if (0 < \strlen($sLiteralAtomUpperCase)) 535 { 536 if (is_resource($rImapLiteralStream) && 'FETCH' === $sParent) 537 { 538 $rMessageMimeIndexStream = (0 === \strlen($sMailEncodingName)) 539 ? $rImapLiteralStream 540 : \MailSo\Base\StreamWrappers\Binary::CreateStream($rImapLiteralStream, 541 \MailSo\Base\StreamWrappers\Binary::GetInlineDecodeOrEncodeFunctionName( 542 $sMailEncodingName, true)); 543 544 \call_user_func($mCallback, $rMessageMimeIndexStream, $sContentType, $sFileName, $sMimeIndex); 545 } 546 } 547 } 548 )), $iIndex, $bIndexIsUid); 549 550 return ($aFetchResponse && 1 === \count($aFetchResponse)); 551 } 552 553 /** 554 * @param string $sFolder 555 * @param array $aIndexRange 556 * @param bool $bIndexIsUid 557 * @param bool $bUseExpunge = true 558 * @param bool $bExpungeAll = false 559 * 560 * @return \MailSo\Mail\MailClient 561 * 562 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 563 * @throws \MailSo\Net\Exceptions\Exception 564 * @throws \MailSo\Imap\Exceptions\Exception 565 */ 566 public function MessageDelete($sFolder, $aIndexRange, $bIndexIsUid, $bUseExpunge = true, $bExpungeAll = false) 567 { 568 if (0 === \strlen($sFolder) || !\is_array($aIndexRange) || 0 === \count($aIndexRange)) 569 { 570 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 571 } 572 573 $this->oImapClient->FolderSelect($sFolder); 574 575 $sIndexRange = \MailSo\Base\Utils::PrepearFetchSequence($aIndexRange); 576 577 $this->oImapClient->MessageStoreFlag($sIndexRange, $bIndexIsUid, 578 array(\MailSo\Imap\Enumerations\MessageFlag::DELETED), 579 \MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT 580 ); 581 582 if ($bUseExpunge) 583 { 584 $this->oImapClient->MessageExpunge($bIndexIsUid ? $sIndexRange : '', $bIndexIsUid, $bExpungeAll); 585 } 586 587 return $this; 588 } 589 590 /** 591 * @param string $sFromFolder 592 * @param string $sToFolder 593 * @param array $aIndexRange 594 * @param bool $bIndexIsUid 595 * @param bool $bUseMoveSupported = false 596 * @param bool $bExpungeAll = false 597 * 598 * @return \MailSo\Mail\MailClient 599 * 600 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 601 * @throws \MailSo\Net\Exceptions\Exception 602 * @throws \MailSo\Imap\Exceptions\Exception 603 */ 604 public function MessageMove($sFromFolder, $sToFolder, $aIndexRange, $bIndexIsUid, $bUseMoveSupported = false, $bExpungeAll = false) 605 { 606 if (0 === \strlen($sFromFolder) || 0 === \strlen($sToFolder) || 607 !\is_array($aIndexRange) || 0 === \count($aIndexRange)) 608 { 609 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 610 } 611 612 $this->oImapClient->FolderSelect($sFromFolder); 613 614 if ($bUseMoveSupported && $this->oImapClient->IsSupported('MOVE')) 615 { 616 $this->oImapClient->MessageMove($sToFolder, 617 \MailSo\Base\Utils::PrepearFetchSequence($aIndexRange), $bIndexIsUid); 618 } 619 else 620 { 621 $this->oImapClient->MessageCopy($sToFolder, 622 \MailSo\Base\Utils::PrepearFetchSequence($aIndexRange), $bIndexIsUid); 623 624 $this->MessageDelete($sFromFolder, $aIndexRange, $bIndexIsUid, true, $bExpungeAll); 625 } 626 627 return $this; 628 } 629 630 /** 631 * @param string $sFromFolder 632 * @param string $sToFolder 633 * @param array $aIndexRange 634 * @param bool $bIndexIsUid 635 * 636 * @return \MailSo\Mail\MailClient 637 * 638 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 639 * @throws \MailSo\Net\Exceptions\Exception 640 * @throws \MailSo\Imap\Exceptions\Exception 641 */ 642 public function MessageCopy($sFromFolder, $sToFolder, $aIndexRange, $bIndexIsUid) 643 { 644 if (0 === \strlen($sFromFolder) || 0 === \strlen($sToFolder) || 645 !\is_array($aIndexRange) || 0 === \count($aIndexRange)) 646 { 647 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 648 } 649 650 $this->oImapClient->FolderSelect($sFromFolder); 651 $this->oImapClient->MessageCopy($sToFolder, 652 \MailSo\Base\Utils::PrepearFetchSequence($aIndexRange), $bIndexIsUid); 653 654 return $this; 655 } 656 657 /** 658 * @return \MailSo\Mail\MailClient 659 * 660 * @throws \MailSo\Net\Exceptions\Exception 661 * @throws \MailSo\Imap\Exceptions\Exception 662 */ 663 public function FolderUnSelect() 664 { 665 if ($this->oImapClient->IsSelected()) 666 { 667 $this->oImapClient->FolderUnSelect(); 668 } 669 670 return $this; 671 } 672 673 /** 674 * @param resource $rMessageStream 675 * @param int $iMessageStreamSize 676 * @param string $sFolderToSave 677 * @param array $aAppendFlags = null 678 * @param int $iUid = null 679 * 680 * @return \MailSo\Mail\MailClient 681 */ 682 public function MessageAppendStream($rMessageStream, $iMessageStreamSize, $sFolderToSave, $aAppendFlags = null, &$iUid = null) 683 { 684 if (!\is_resource($rMessageStream) || 0 === \strlen($sFolderToSave)) 685 { 686 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 687 } 688 689 $this->oImapClient->MessageAppendStream( 690 $sFolderToSave, $rMessageStream, $iMessageStreamSize, $aAppendFlags, $iUid); 691 692 return $this; 693 } 694 695 /** 696 * @param string $sMessageFileName 697 * @param string $sFolderToSave 698 * @param array $aAppendFlags = null 699 * @param int &$iUid = null 700 * 701 * @return \MailSo\Mail\MailClient 702 */ 703 public function MessageAppendFile($sMessageFileName, $sFolderToSave, $aAppendFlags = null, &$iUid = null) 704 { 705 if (!@\is_file($sMessageFileName) || !@\is_readable($sMessageFileName)) 706 { 707 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 708 } 709 710 $iMessageStreamSize = \filesize($sMessageFileName); 711 $rMessageStream = \fopen($sMessageFileName, 'rb'); 712 713 $this->MessageAppendStream($rMessageStream, $iMessageStreamSize, $sFolderToSave, $aAppendFlags, $iUid); 714 715 if (\is_resource($rMessageStream)) 716 { 717 @fclose($rMessageStream); 718 } 719 720 return $this; 721 } 722 723 /** 724 * @param string $sFolderName 725 * @param int $iCount 726 * @param int $iUnseenCount 727 * @param string $sUidNext 728 * @param string $sHighestModSeq 729 * 730 * @return void 731 */ 732 protected function initFolderValues($sFolderName, &$iCount, &$iUnseenCount, 733 &$sUidNext, &$sHighestModSeq = '') 734 { 735 $aTypes = array( 736 \MailSo\Imap\Enumerations\FolderResponseStatus::MESSAGES, 737 \MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN, 738 \MailSo\Imap\Enumerations\FolderResponseStatus::UIDNEXT 739 ); 740 741 if ($this->oImapClient->IsSupported('CONDSTORE')) 742 { 743 $aTypes[] = \MailSo\Imap\Enumerations\FolderResponseStatus::HIGHESTMODSEQ; 744 } 745 746 $aFolderStatus = $this->oImapClient->FolderStatus($sFolderName, $aTypes); 747 748 $iCount = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::MESSAGES]) 749 ? (int) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::MESSAGES] : 0; 750 751 $iUnseenCount = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN]) 752 ? (int) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN] : 0; 753 754 $sUidNext = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UIDNEXT]) 755 ? (string) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UIDNEXT] : '0'; 756 757 $sHighestModSeq = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::HIGHESTMODSEQ]) 758 ? (string) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::HIGHESTMODSEQ] : ''; 759 760 if ($this->IsGmail() && 761 ('INBOX' === $sFolderName || '[gmail]' === \strtolower(\substr($sFolderName, 0, 7)))) 762 { 763 $oFolder = $this->oImapClient->FolderCurrentInformation(); 764 if ($oFolder && null !== $oFolder->Exists && $oFolder->FolderName === $sFolderName) 765 { 766 $iSubCount = (int) $oFolder->Exists; 767 if (0 < $iSubCount && $iSubCount < $iCount) 768 { 769 $iCount = $iSubCount; 770 } 771 } 772 } 773 } 774 775 /** 776 * @return string 777 */ 778 public function GenerateImapClientHash() 779 { 780 return \md5('ImapClientHash/'. 781 $this->oImapClient->GetLogginedUser().'@'. 782 $this->oImapClient->GetConnectedHost().':'. 783 $this->oImapClient->GetConnectedPort() 784 ); 785 } 786 787 /** 788 * @param string $sFolder 789 * @param int $iCount 790 * @param int $iUnseenCount 791 * @param string $sUidNext 792 * @param string $sHighestModSeq = '' 793 * 794 * @return string 795 */ 796 public function GenerateFolderHash($sFolder, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq = '') 797 { 798 $iUnseenCount = 0; // unneccessery 799 return \md5('FolderHash/'.$sFolder.'-'.$iCount.'-'.$iUnseenCount.'-'.$sUidNext.'-'. 800 $sHighestModSeq.'-'.$this->GenerateImapClientHash().'-'. 801 \MailSo\Config::$MessageListPermanentFilter 802 ); 803 } 804 805 /** 806 * @param string $sFolderName 807 * @param string $sPrevUidNext 808 * @param string $sCurrentUidNext 809 * 810 * @return array 811 */ 812 private function getFolderNextMessageInformation($sFolderName, $sPrevUidNext, $sCurrentUidNext) 813 { 814 $aNewMessages = array(); 815 816 if (0 < \strlen($sPrevUidNext) && (string) $sPrevUidNext !== (string) $sCurrentUidNext) 817 { 818 $this->oImapClient->FolderSelect($sFolderName); 819 820 $aFetchResponse = $this->oImapClient->Fetch(array( 821 \MailSo\Imap\Enumerations\FetchType::INDEX, 822 \MailSo\Imap\Enumerations\FetchType::UID, 823 \MailSo\Imap\Enumerations\FetchType::FLAGS, 824 \MailSo\Imap\Enumerations\FetchType::BuildBodyCustomHeaderRequest(array( 825 \MailSo\Mime\Enumerations\Header::FROM_, 826 \MailSo\Mime\Enumerations\Header::SUBJECT, 827 \MailSo\Mime\Enumerations\Header::CONTENT_TYPE 828 )) 829 ), $sPrevUidNext.':*', true); 830 831 if (\is_array($aFetchResponse) && 0 < \count($aFetchResponse)) 832 { 833 foreach ($aFetchResponse as /* @var $oFetchResponse \MailSo\Imap\FetchResponse */ $oFetchResponse) 834 { 835 $aFlags = \array_map('strtolower', $oFetchResponse->GetFetchValue( 836 \MailSo\Imap\Enumerations\FetchType::FLAGS)); 837 838 if (!\in_array(\strtolower(\MailSo\Imap\Enumerations\MessageFlag::SEEN), $aFlags)) 839 { 840 $sUid = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::UID); 841 $sHeaders = $oFetchResponse->GetHeaderFieldsValue(); 842 843 $oHeaders = \MailSo\Mime\HeaderCollection::NewInstance()->Parse($sHeaders); 844 845 $sContentTypeCharset = $oHeaders->ParameterValue( 846 \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 847 \MailSo\Mime\Enumerations\Parameter::CHARSET 848 ); 849 850 $sCharset = ''; 851 if (0 < \strlen($sContentTypeCharset)) 852 { 853 $sCharset = $sContentTypeCharset; 854 } 855 856 if (0 < \strlen($sCharset)) 857 { 858 $oHeaders->SetParentCharset($sCharset); 859 } 860 861 $aNewMessages[] = array( 862 'Folder' => $sFolderName, 863 'Uid' => $sUid, 864 'Subject' => $oHeaders->ValueByName(\MailSo\Mime\Enumerations\Header::SUBJECT, 0 === \strlen($sCharset)), 865 'From' => $oHeaders->GetAsEmailCollection(\MailSo\Mime\Enumerations\Header::FROM_, 0 === \strlen($sCharset)) 866 ); 867 } 868 } 869 } 870 } 871 872 return $aNewMessages; 873 } 874 875 /** 876 * @param string $sFolderName 877 * @param string $sPrevUidNext = '' 878 * @param array $aUids = '' 879 * 880 * @return string 881 * 882 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 883 * @throws \MailSo\Net\Exceptions\Exception 884 * @throws \MailSo\Imap\Exceptions\Exception 885 */ 886 public function FolderInformation($sFolderName, $sPrevUidNext = '', $aUids = array()) 887 { 888 $aFlags = array(); 889 890 $bSelect = false; 891 if ($this->IsGmail() && 892 ('INBOX' === $sFolderName || '[gmail]' === \strtolower(\substr($sFolderName, 0, 7)))) 893 { 894 $this->oImapClient->FolderSelect($sFolderName); 895 $bSelect = true; 896 } 897 898 if (\is_array($aUids) && 0 < \count($aUids)) 899 { 900 if (!$bSelect) 901 { 902 $this->oImapClient->FolderSelect($sFolderName); 903 } 904 905 $aFetchResponse = $this->oImapClient->Fetch(array( 906 \MailSo\Imap\Enumerations\FetchType::INDEX, 907 \MailSo\Imap\Enumerations\FetchType::UID, 908 \MailSo\Imap\Enumerations\FetchType::FLAGS 909 ), \MailSo\Base\Utils::PrepearFetchSequence($aUids), true); 910 911 if (\is_array($aFetchResponse) && 0 < \count($aFetchResponse)) 912 { 913 foreach ($aFetchResponse as $oFetchResponse) 914 { 915 $sUid = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::UID); 916 $aFlags[(\is_numeric($sUid) ? (int) $sUid : 0)] = 917 $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::FLAGS); 918 } 919 } 920 } 921 922 $iCount = 0; 923 $iUnseenCount = 0; 924 $sUidNext = '0'; 925 $sHighestModSeq = ''; 926 927 $this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq); 928 929 $aResult = array( 930 'Folder' => $sFolderName, 931 'Hash' => $this->GenerateFolderHash($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq), 932 'MessageCount' => $iCount, 933 'MessageUnseenCount' => $iUnseenCount, 934 'UidNext' => $sUidNext, 935 'Flags' => $aFlags, 936 'HighestModSeq' => $sHighestModSeq, 937 'NewMessages' => 'INBOX' === $sFolderName && \MailSo\Config::$CheckNewMessages ? 938 $this->getFolderNextMessageInformation($sFolderName, $sPrevUidNext, $sUidNext) : array() 939 ); 940 941 return $aResult; 942 } 943 944 /** 945 * @param string $sFolderName 946 * 947 * @return string 948 * 949 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 950 * @throws \MailSo\Net\Exceptions\Exception 951 * @throws \MailSo\Imap\Exceptions\Exception 952 */ 953 public function FolderHash($sFolderName) 954 { 955 $iCount = 0; 956 $iUnseenCount = 0; 957 $sUidNext = '0'; 958 $sHighestModSeq = ''; 959 960 $this->initFolderValues($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq); 961 962 return $this->GenerateFolderHash($sFolderName, $iCount, $iUnseenCount, $sUidNext, $sHighestModSeq); 963 } 964 965 /** 966 * @return int 967 * 968 * @throws \MailSo\Net\Exceptions\Exception 969 * @throws \MailSo\Imap\Exceptions\Exception 970 */ 971 public function InboxUnreadCount() 972 { 973 $aFolderStatus = $this->oImapClient->FolderStatus('INBOX', array( 974 \MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN 975 )); 976 977 $iResult = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN]) ? 978 (int) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN] : 0; 979 980 return 0 < $iResult ? $iResult : 0; 981 } 982 983 /** 984 * @return bool 985 */ 986 public function IsGmail() 987 { 988 return 'ssl://imap.gmail.com' === \strtolower($this->oImapClient->GetConnectedHost()); 989 } 990 991 /** 992 * @param string $sSearch 993 * @param bool $bDetectGmail = true 994 * 995 * @return string 996 */ 997 private function escapeSearchString($sSearch, $bDetectGmail = true) 998 { 999 return !\MailSo\Base\Utils::IsAscii($sSearch) 1000 ? '{'.\strlen($sSearch).'}'."\r\n".$sSearch : $this->oImapClient->EscapeString($sSearch); 1001// return ($bDetectGmail && !\MailSo\Base\Utils::IsAscii($sSearch) && $this->IsGmail()) 1002// ? '{'.\strlen($sSearch).'+}'."\r\n".$sSearch : $this->oImapClient->EscapeString($sSearch); 1003 } 1004 1005 /** 1006 * @param string $sDate 1007 * @param int $iTimeZoneOffset 1008 * 1009 * @return int 1010 */ 1011 private function parseSearchDate($sDate, $iTimeZoneOffset) 1012 { 1013 $iResult = 0; 1014 if (0 < \strlen($sDate)) 1015 { 1016 $oDateTime = \DateTime::createFromFormat('Y.m.d', $sDate, \MailSo\Base\DateTimeHelper::GetUtcTimeZoneObject()); 1017 return $oDateTime ? $oDateTime->getTimestamp() - $iTimeZoneOffset : 0; 1018 } 1019 1020 return $iResult; 1021 } 1022 1023 /** 1024 * @param string $sSize 1025 * 1026 * @return int 1027 */ 1028 private function parseFriendlySize($sSize) 1029 { 1030 $sSize = preg_replace('/[^0-9bBkKmM]/', '', $sSize); 1031 1032 $iResult = 0; 1033 $aMatch = array(); 1034 1035 if (\preg_match('/([\d]+)(B|KB|M|MB|G|GB)$/i', $sSize, $aMatch) && isset($aMatch[1], $aMatch[2])) 1036 { 1037 $iResult = (int) $aMatch[1]; 1038 switch (\strtoupper($aMatch[2])) 1039 { 1040 case 'K': 1041 case 'KB': 1042 $iResult *= 1024; 1043 case 'M': 1044 case 'MB': 1045 $iResult *= 1024; 1046 case 'G': 1047 case 'GB': 1048 $iResult *= 1024; 1049 } 1050 } 1051 else 1052 { 1053 $iResult = (int) $sSize; 1054 } 1055 1056 return $iResult; 1057 } 1058 1059 /** 1060 * @param string $sSearch 1061 * 1062 * @return array 1063 */ 1064 private function parseSearchString($sSearch) 1065 { 1066 $aResult = array( 1067 'OTHER' => '' 1068 ); 1069 1070 $aCache = array(); 1071 1072 $sReg = 'e?mail|from|to|subject|has|is|date|text|body|size|larger|bigger|smaller|maxsize|minsize'; 1073 1074 $sSearch = \MailSo\Base\Utils::StripSpaces($sSearch); 1075 $sSearch = \trim(\preg_replace('/('.$sReg.'): /i', '\\1:', $sSearch)); 1076 1077 $mMatch = array(); 1078 \preg_match_all('/".*?(?<!\\\)"/', $sSearch, $mMatch); 1079 if (\is_array($mMatch) && isset($mMatch[0]) && \is_array($mMatch[0]) && 0 < \count($mMatch[0])) 1080 { 1081 foreach ($mMatch[0] as $sItem) 1082 { 1083 do 1084 { 1085 $sKey = \MailSo\Base\Utils::Md5Rand(); 1086 } 1087 while (isset($aCache[$sKey])); 1088 1089 $aCache[$sKey] = \stripcslashes($sItem); 1090 $sSearch = \str_replace($sItem, $sKey, $sSearch); 1091 } 1092 } 1093 1094 \preg_match_all('/\'.*?(?<!\\\)\'/', $sSearch, $mMatch); 1095 if (\is_array($mMatch) && isset($mMatch[0]) && \is_array($mMatch[0]) && 0 < \count($mMatch[0])) 1096 { 1097 foreach ($mMatch[0] as $sItem) 1098 { 1099 do 1100 { 1101 $sKey = \MailSo\Base\Utils::Md5Rand(); 1102 } 1103 while (isset($aCache[$sKey])); 1104 1105 $aCache[$sKey] = \stripcslashes($sItem); 1106 $sSearch = \str_replace($sItem, $sKey, $sSearch); 1107 } 1108 } 1109 1110 $mMatch = array(); 1111 \preg_match_all('/('.$sReg.'):([^\s]*)/i', $sSearch, $mMatch); 1112 if (\is_array($mMatch) && isset($mMatch[1]) && \is_array($mMatch[1]) && 0 < \count($mMatch[1])) 1113 { 1114 if (\is_array($mMatch[0])) 1115 { 1116 foreach ($mMatch[0] as $sToken) 1117 { 1118 $sSearch = \str_replace($sToken, '', $sSearch); 1119 } 1120 1121 $sSearch = \MailSo\Base\Utils::StripSpaces($sSearch); 1122 } 1123 1124 foreach ($mMatch[1] as $iIndex => $sName) 1125 { 1126 if (isset($mMatch[2][$iIndex]) && 0 < \strlen($mMatch[2][$iIndex])) 1127 { 1128 $sName = \strtoupper($sName); 1129 $sValue = $mMatch[2][$iIndex]; 1130 switch ($sName) 1131 { 1132 case 'TEXT': 1133 case 'BODY': 1134 case 'EMAIL': 1135 case 'MAIL': 1136 case 'FROM': 1137 case 'TO': 1138 case 'SUBJECT': 1139 case 'IS': 1140 case 'HAS': 1141 case 'SIZE': 1142 case 'SMALLER': 1143 case 'LARGER': 1144 case 'BIGGER': 1145 case 'MAXSIZE': 1146 case 'MINSIZE': 1147 case 'DATE': 1148 if ('MAIL' === $sName) 1149 { 1150 $sName = 'EMAIL'; 1151 } 1152 if ('BODY' === $sName) 1153 { 1154 $sName = 'TEXT'; 1155 } 1156 if ('SIZE' === $sName || 'BIGGER' === $sName || 'MINSIZE' === $sName) 1157 { 1158 $sName = 'LARGER'; 1159 } 1160 if ('MAXSIZE' === $sName) 1161 { 1162 $sName = 'SMALLER'; 1163 } 1164 $aResult[$sName] = $sValue; 1165 break; 1166 } 1167 } 1168 } 1169 } 1170 1171 $aResult['OTHER'] = $sSearch; 1172 foreach ($aResult as $sName => $sValue) 1173 { 1174 if (isset($aCache[$sValue])) 1175 { 1176 $aResult[$sName] = \trim($aCache[$sValue], '"\' '); 1177 } 1178 } 1179 1180 return $aResult; 1181 } 1182 1183 /** 1184 * @param string $sSearch 1185 * @param string $sFilter 1186 * @param int $iTimeZoneOffset = 0 1187 * @param bool $bUseCache = true 1188 * 1189 * @return string 1190 */ 1191 private function getImapSearchCriterias($sSearch, $sFilter, $iTimeZoneOffset = 0, &$bUseCache = true) 1192 { 1193 $bUseCache = true; 1194 $iTimeFilter = 0; 1195 $aCriteriasResult = array(); 1196 1197 if (0 < \MailSo\Config::$MessageListDateFilter) 1198 { 1199 $iD = \time() - 3600 * 24 * 30 * \MailSo\Config::$MessageListDateFilter; 1200 $iTimeFilter = \gmmktime(1, 1, 1, \gmdate('n', $iD), 1, \gmdate('Y', $iD)); 1201 } 1202 1203 if (0 < \strlen(\trim($sSearch))) 1204 { 1205 $sGmailRawSearch = ''; 1206 $sResultBodyTextSearch = ''; 1207 1208 $aLines = $this->parseSearchString($sSearch); 1209 $bIsGmail = $this->oImapClient->IsSupported('X-GM-EXT-1'); 1210 1211 if (1 === \count($aLines) && isset($aLines['OTHER'])) 1212 { 1213 $sValue = $this->escapeSearchString($aLines['OTHER']); 1214 1215 if (\MailSo\Config::$MessageListFastSimpleSearch) 1216 { 1217 $aCriteriasResult[] = 'OR OR OR'; 1218 $aCriteriasResult[] = 'FROM'; 1219 $aCriteriasResult[] = $sValue; 1220 $aCriteriasResult[] = 'TO'; 1221 $aCriteriasResult[] = $sValue; 1222 $aCriteriasResult[] = 'CC'; 1223 $aCriteriasResult[] = $sValue; 1224 $aCriteriasResult[] = 'SUBJECT'; 1225 $aCriteriasResult[] = $sValue; 1226 } 1227 else 1228 { 1229 $aCriteriasResult[] = 'TEXT'; 1230 $aCriteriasResult[] = $sValue; 1231 } 1232 } 1233 else 1234 { 1235 if (isset($aLines['EMAIL'])) 1236 { 1237 $sValue = $this->escapeSearchString($aLines['EMAIL']); 1238 1239 $aCriteriasResult[] = 'OR OR'; 1240 $aCriteriasResult[] = 'FROM'; 1241 $aCriteriasResult[] = $sValue; 1242 $aCriteriasResult[] = 'TO'; 1243 $aCriteriasResult[] = $sValue; 1244 $aCriteriasResult[] = 'CC'; 1245 $aCriteriasResult[] = $sValue; 1246 1247 unset($aLines['EMAIL']); 1248 } 1249 1250 if (isset($aLines['TO'])) 1251 { 1252 $sValue = $this->escapeSearchString($aLines['TO']); 1253 1254 $aCriteriasResult[] = 'OR'; 1255 $aCriteriasResult[] = 'TO'; 1256 $aCriteriasResult[] = $sValue; 1257 $aCriteriasResult[] = 'CC'; 1258 $aCriteriasResult[] = $sValue; 1259 1260 unset($aLines['TO']); 1261 } 1262 1263 $sMainText = ''; 1264 foreach ($aLines as $sName => $sRawValue) 1265 { 1266 if ('' === \trim($sRawValue)) 1267 { 1268 continue; 1269 } 1270 1271 $sValue = $this->escapeSearchString($sRawValue); 1272 switch ($sName) 1273 { 1274 case 'FROM': 1275 $aCriteriasResult[] = 'FROM'; 1276 $aCriteriasResult[] = $sValue; 1277 break; 1278 case 'SUBJECT': 1279 $aCriteriasResult[] = 'SUBJECT'; 1280 $aCriteriasResult[] = $sValue; 1281 break; 1282 case 'OTHER': 1283 case 'TEXT': 1284 $sMainText .= ' '.$sRawValue; 1285 break; 1286 case 'HAS': 1287 $aValue = \explode(',', \strtolower($sRawValue)); 1288 $aValue = \array_map('trim', $aValue); 1289 1290 $aCompareArray = array('file', 'files', 'attach', 'attachs', 'attachment', 'attachments'); 1291 if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue))) 1292 { 1293 if ($bIsGmail) 1294 { 1295 $sGmailRawSearch .= ' has:attachment'; 1296 } 1297 else 1298 { 1299 // Simple, is not detailed search (Sometimes doesn't work) 1300 $aCriteriasResult[] = 'OR OR OR'; 1301 $aCriteriasResult[] = 'HEADER Content-Type application/'; 1302 $aCriteriasResult[] = 'HEADER Content-Type multipart/m'; 1303 $aCriteriasResult[] = 'HEADER Content-Type multipart/signed'; 1304 $aCriteriasResult[] = 'HEADER Content-Type multipart/report'; 1305 } 1306 } 1307 1308 case 'IS': 1309 $aValue = \explode(',', \strtolower($sRawValue)); 1310 $aValue = \array_map('trim', $aValue); 1311 1312 $aCompareArray = array('flag', 'flagged', 'star', 'starred', 'pinned'); 1313 $aCompareArray2 = array('unflag', 'unflagged', 'unstar', 'unstarred', 'unpinned'); 1314 if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue))) 1315 { 1316 $aCriteriasResult[] = 'FLAGGED'; 1317 $bUseCache = false; 1318 } 1319 else if (\count($aCompareArray2) > \count(\array_diff($aCompareArray2, $aValue))) 1320 { 1321 $aCriteriasResult[] = 'UNFLAGGED'; 1322 $bUseCache = false; 1323 } 1324 1325 $aCompareArray = array('unread', 'unseen'); 1326 $aCompareArray2 = array('read', 'seen'); 1327 if (\count($aCompareArray) > \count(\array_diff($aCompareArray, $aValue))) 1328 { 1329 $aCriteriasResult[] = 'UNSEEN'; 1330 $bUseCache = false; 1331 } 1332 else if (\count($aCompareArray2) > \count(\array_diff($aCompareArray2, $aValue))) 1333 { 1334 $aCriteriasResult[] = 'SEEN'; 1335 $bUseCache = false; 1336 } 1337 break; 1338 1339 case 'LARGER': 1340 $aCriteriasResult[] = 'LARGER'; 1341 $aCriteriasResult[] = $this->parseFriendlySize($sRawValue); 1342 break; 1343 case 'SMALLER': 1344 $aCriteriasResult[] = 'SMALLER'; 1345 $aCriteriasResult[] = $this->parseFriendlySize($sRawValue); 1346 break; 1347 case 'DATE': 1348 $iDateStampFrom = $iDateStampTo = 0; 1349 1350 $sDate = $sRawValue; 1351 $aDate = \explode('/', $sDate); 1352 1353 if (\is_array($aDate) && 2 === \count($aDate)) 1354 { 1355 if (0 < \strlen($aDate[0])) 1356 { 1357 $iDateStampFrom = $this->parseSearchDate($aDate[0], $iTimeZoneOffset); 1358 } 1359 1360 if (0 < \strlen($aDate[1])) 1361 { 1362 $iDateStampTo = $this->parseSearchDate($aDate[1], $iTimeZoneOffset); 1363 $iDateStampTo += 60 * 60 * 24; 1364 } 1365 } 1366 else 1367 { 1368 if (0 < \strlen($sDate)) 1369 { 1370 $iDateStampFrom = $this->parseSearchDate($sDate, $iTimeZoneOffset); 1371 $iDateStampTo = $iDateStampFrom + 60 * 60 * 24; 1372 } 1373 } 1374 1375 if (0 < $iDateStampFrom) 1376 { 1377 $aCriteriasResult[] = 'SINCE'; 1378 $aCriteriasResult[] = \gmdate('j-M-Y', $iTimeFilter > $iDateStampFrom ? 1379 $iTimeFilter : $iDateStampFrom); 1380 1381 $iTimeFilter = 0; 1382 } 1383 1384 if (0 < $iDateStampTo) 1385 { 1386 $aCriteriasResult[] = 'BEFORE'; 1387 $aCriteriasResult[] = \gmdate('j-M-Y', $iDateStampTo); 1388 } 1389 break; 1390 } 1391 } 1392 1393 if ('' !== \trim($sMainText)) 1394 { 1395 $sMainText = \trim(\MailSo\Base\Utils::StripSpaces($sMainText), '"'); 1396 if ($bIsGmail) 1397 { 1398 $sGmailRawSearch .= ' '.$sMainText; 1399 } 1400 else 1401 { 1402 $sResultBodyTextSearch .= ' '.$sMainText; 1403 } 1404 } 1405 } 1406 1407 $sGmailRawSearch = \trim($sGmailRawSearch); 1408 if ($bIsGmail && 0 < \strlen($sGmailRawSearch)) 1409 { 1410 $aCriteriasResult[] = 'X-GM-RAW'; 1411 $aCriteriasResult[] = $this->escapeSearchString($sGmailRawSearch, false); 1412 } 1413 1414 $sResultBodyTextSearch = \trim($sResultBodyTextSearch); 1415 if (0 < \strlen($sResultBodyTextSearch)) 1416 { 1417 $aCriteriasResult[] = 'BODY'; 1418 $aCriteriasResult[] = $this->escapeSearchString($sResultBodyTextSearch); 1419 } 1420 } 1421 1422 $sCriteriasResult = \trim(\implode(' ', $aCriteriasResult)); 1423 1424 if (0 < $iTimeFilter) 1425 { 1426 $sCriteriasResult .= ' SINCE '.\gmdate('j-M-Y', $iTimeFilter); 1427 } 1428 1429 $sCriteriasResult = \trim($sCriteriasResult); 1430 if (\MailSo\Config::$MessageListUndeletedOnly) 1431 { 1432 $sCriteriasResult = \trim($sCriteriasResult.' UNDELETED'); 1433 } 1434 1435 $sFilter = \trim($sFilter); 1436 if ('' !== $sFilter) 1437 { 1438 $sCriteriasResult .= ' '.$sFilter; 1439 } 1440 1441 $sCriteriasResult = \trim($sCriteriasResult); 1442 if ('' !== \MailSo\Config::$MessageListPermanentFilter) 1443 { 1444 $sCriteriasResult = \trim($sCriteriasResult.' '.\MailSo\Config::$MessageListPermanentFilter); 1445 } 1446 1447 $sCriteriasResult = \trim($sCriteriasResult); 1448 if ('' === $sCriteriasResult) 1449 { 1450 $sCriteriasResult = 'ALL'; 1451 } 1452 1453 return $sCriteriasResult; 1454 } 1455 1456 /** 1457 * @param array $aThreads 1458 * @return array 1459 */ 1460 private function threadArrayMap($aThreads) 1461 { 1462 $aNew = array(); 1463 foreach ($aThreads as $mItem) 1464 { 1465 if (!\is_array($mItem)) 1466 { 1467 $aNew[] = $mItem; 1468 } 1469 else 1470 { 1471 $mMap = $this->threadArrayMap($mItem); 1472 if (\is_array($mMap) && 0 < \count($mMap)) 1473 { 1474 $aNew = \array_merge($aNew, $mMap); 1475 } 1476 } 1477 } 1478 1479 return $aNew; 1480 } 1481 1482 /** 1483 * @param array $aThreads 1484 * 1485 * @return array 1486 */ 1487 private function compileThreadArray($aThreads) 1488 { 1489 $aResult = array(); 1490 foreach ($aThreads as $mItem) 1491 { 1492 if (\is_array($mItem)) 1493 { 1494 $aMap = $this->threadArrayMap($mItem); 1495 if (\is_array($aMap)) 1496 { 1497 if (1 < \count($aMap)) 1498 { 1499 $aResult[] = $aMap; 1500 } 1501 else if (0 < \count($aMap)) 1502 { 1503 $aResult[] = $aMap[0]; 1504 } 1505 } 1506 } 1507 else 1508 { 1509 $aResult[] = $mItem; 1510 } 1511 } 1512 1513 return $aResult; 1514 } 1515 1516 /** 1517 * @param string $sFolderName 1518 * @param string $sFolderHash 1519 * @param array $aIndexOrUids 1520 * @param \MailSo\Cache\CacheClient $oCacher 1521 * @param bool $bCacheOnly = false 1522 * 1523 * @return array 1524 * 1525 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 1526 * @throws \MailSo\Net\Exceptions\Exception 1527 * @throws \MailSo\Imap\Exceptions\Exception 1528 */ 1529 public function MessageListThreadsMap($sFolderName, $sFolderHash, $aIndexOrUids, $oCacher, $bCacheOnly = false) 1530 { 1531 $iThreadLimit = \MailSo\Config::$LargeThreadLimit; 1532 1533 $sSearchHash = ''; 1534 if (0 < \MailSo\Config::$MessageListDateFilter) 1535 { 1536 $iD = \time() - 3600 * 24 * 30 * \MailSo\Config::$MessageListDateFilter; 1537 $iTimeFilter = \gmmktime(1, 1, 1, \gmdate('n', $iD), 1, \gmdate('Y', $iD)); 1538 1539 $sSearchHash .= ' SINCE '.\gmdate('j-M-Y', $iTimeFilter); 1540 } 1541 1542 if ('' === \trim($sSearchHash)) 1543 { 1544 $sSearchHash = 'ALL'; 1545 } 1546 1547 if ($oCacher && $oCacher->IsInited()) 1548 { 1549 $sSerializedHashKey = 1550 'ThreadsMapSorted/'.$sSearchHash.'/'. 1551 'Limit='.$iThreadLimit.'/'.$sFolderName.'/'.$sFolderHash; 1552 1553 if ($this->oLogger) 1554 { 1555 $this->oLogger->Write($sSerializedHashKey); 1556 } 1557 1558 $sSerializedUids = $oCacher->Get($sSerializedHashKey); 1559 if (!empty($sSerializedUids)) 1560 { 1561 $aSerializedUids = @\json_decode($sSerializedUids, true); 1562 if (isset($aSerializedUids['ThreadsUids']) && \is_array($aSerializedUids['ThreadsUids'])) 1563 { 1564 if ($this->oLogger) 1565 { 1566 $this->oLogger->Write('Get Serialized Thread UIDS from cache ("'.$sFolderName.'" / '.$sSearchHash.') [count:'.\count($aSerializedUids['ThreadsUids']).']'); 1567 } 1568 1569 return $aSerializedUids['ThreadsUids']; 1570 } 1571 } 1572 } 1573 1574 if ($bCacheOnly) 1575 { 1576 return null; 1577 } 1578 1579 $this->oImapClient->FolderExamine($sFolderName); 1580 1581 $aThreadUids = array(); 1582 try 1583 { 1584 $aThreadUids = $this->oImapClient->MessageSimpleThread($sSearchHash); 1585 } 1586 catch (\MailSo\Imap\Exceptions\RuntimeException $oException) 1587 { 1588 unset($oException); 1589 $aThreadUids = array(); 1590 } 1591 1592 $aResult = array(); 1593 $aCompiledThreads = $this->compileThreadArray($aThreadUids); 1594 1595 foreach ($aCompiledThreads as $mData) 1596 { 1597 if (\is_array($mData)) 1598 { 1599 foreach ($mData as $mSubData) 1600 { 1601 $aResult[(int) $mSubData] = 1602 \array_diff($mData, array((int) $mSubData)); 1603 } 1604 } 1605 else if (\is_int($mData) || \is_string($mData)) 1606 { 1607 $aResult[(int) $mData] = (int) $mData; 1608 } 1609 } 1610 1611 $aParentsMap = array(); 1612 foreach ($aIndexOrUids as $iUid) 1613 { 1614 if (isset($aResult[$iUid]) && \is_array($aResult[$iUid])) 1615 { 1616 foreach ($aResult[$iUid] as $iTempUid) 1617 { 1618 $aParentsMap[$iTempUid] = $iUid; 1619 if (isset($aResult[$iTempUid])) 1620 { 1621 unset($aResult[$iTempUid]); 1622 } 1623 } 1624 } 1625 } 1626 1627 $aSortedThreads = array(); 1628 foreach ($aIndexOrUids as $iUid) 1629 { 1630 if (isset($aResult[$iUid])) 1631 { 1632 $aSortedThreads[$iUid] = $iUid; 1633 } 1634 } 1635 1636 foreach ($aIndexOrUids as $iUid) 1637 { 1638 if (!isset($aSortedThreads[$iUid]) && 1639 isset($aParentsMap[$iUid]) && 1640 isset($aSortedThreads[$aParentsMap[$iUid]])) 1641 { 1642 if (!\is_array($aSortedThreads[$aParentsMap[$iUid]])) 1643 { 1644 $aSortedThreads[$aParentsMap[$iUid]] = array(); 1645 } 1646 1647 $aSortedThreads[$aParentsMap[$iUid]][] = $iUid; 1648 } 1649 } 1650 1651 $aResult = $aSortedThreads; 1652 unset($aParentsMap, $aSortedThreads); 1653 1654 $aTemp = array(); 1655 foreach ($aResult as $iUid => $mValue) 1656 { 1657 if (0 < $iThreadLimit && \is_array($mValue) && $iThreadLimit < \count($mValue)) 1658 { 1659 $aParts = \array_chunk($mValue, $iThreadLimit); 1660 if (0 < count($aParts)) 1661 { 1662 foreach ($aParts as $iIndex => $aItem) 1663 { 1664 if (0 === $iIndex) 1665 { 1666 $aResult[$iUid] = $aItem; 1667 } 1668 else if (0 < $iIndex && \is_array($aItem)) 1669 { 1670 $mFirst = \array_shift($aItem); 1671 if (!empty($mFirst)) 1672 { 1673 $aTemp[$mFirst] = 0 < \count($aItem) ? $aItem : $mFirst; 1674 } 1675 } 1676 } 1677 } 1678 } 1679 } 1680 1681 foreach ($aTemp as $iUid => $mValue) 1682 { 1683 $aResult[$iUid] = $mValue; 1684 } 1685 1686 unset($aTemp); 1687 1688 $aLastResult = array(); 1689 foreach ($aIndexOrUids as $iUid) 1690 { 1691 if (isset($aResult[$iUid])) 1692 { 1693 $aLastResult[$iUid] = $aResult[$iUid]; 1694 } 1695 } 1696 1697 $aResult = $aLastResult; 1698 unset($aLastResult); 1699 1700 if ($oCacher && $oCacher->IsInited() && !empty($sSerializedHashKey)) 1701 { 1702 $oCacher->Set($sSerializedHashKey, @\json_encode(array( 1703 'ThreadsUids' => $aResult 1704 ))); 1705 1706 if ($this->oLogger) 1707 { 1708 $this->oLogger->Write('Save Serialized Thread UIDS to cache ("'.$sFolderName.'" / '.$sSearchHash.') [count:'.\count($aResult).']'); 1709 } 1710 } 1711 1712 return $aResult; 1713 } 1714 1715 /** 1716 * @param \MailSo\Mail\MessageCollection &$oMessageCollection 1717 * @param array $aRequestIndexOrUids 1718 * @param bool $bIndexAsUid 1719 * @param bool $bSimple = false 1720 * 1721 * @throws \MailSo\Net\Exceptions\Exception 1722 * @throws \MailSo\Imap\Exceptions\Exception 1723 */ 1724 public function MessageListByRequestIndexOrUids(&$oMessageCollection, $aRequestIndexOrUids, $bIndexAsUid, $bSimple = false) 1725 { 1726 if (\is_array($aRequestIndexOrUids) && 0 < \count($aRequestIndexOrUids)) 1727 { 1728 $aFetchResponse = $this->oImapClient->Fetch(array( 1729 \MailSo\Imap\Enumerations\FetchType::INDEX, 1730 \MailSo\Imap\Enumerations\FetchType::UID, 1731 \MailSo\Imap\Enumerations\FetchType::RFC822_SIZE, 1732 \MailSo\Imap\Enumerations\FetchType::INTERNALDATE, 1733 \MailSo\Imap\Enumerations\FetchType::FLAGS, 1734 \MailSo\Imap\Enumerations\FetchType::BODYSTRUCTURE, 1735 $bSimple ? 1736 $this->getEnvelopeOrHeadersRequestStringForSimpleList() : 1737 $this->getEnvelopeOrHeadersRequestString() 1738 ), \MailSo\Base\Utils::PrepearFetchSequence($aRequestIndexOrUids), $bIndexAsUid); 1739 1740 if (\is_array($aFetchResponse) && 0 < \count($aFetchResponse)) 1741 { 1742 $aFetchIndexArray = array(); 1743 $oFetchResponseItem = null; 1744 foreach ($aFetchResponse as /* @var $oFetchResponseItem \MailSo\Imap\FetchResponse */ &$oFetchResponseItem) 1745 { 1746 $aFetchIndexArray[($bIndexAsUid) 1747 ? $oFetchResponseItem->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::UID) 1748 : $oFetchResponseItem->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::INDEX)] =& $oFetchResponseItem; 1749 1750 unset($oFetchResponseItem); 1751 } 1752 1753 foreach ($aRequestIndexOrUids as $iFUid) 1754 { 1755 if (isset($aFetchIndexArray[$iFUid])) 1756 { 1757 $oMessageCollection->Add( 1758 Message::NewFetchResponseInstance( 1759 $oMessageCollection->FolderName, $aFetchIndexArray[$iFUid])); 1760 } 1761 } 1762 } 1763 } 1764 } 1765 1766 /** 1767 * @return bool 1768 * 1769 * @throws \MailSo\Net\Exceptions\Exception 1770 */ 1771 public function IsThreadsSupported() 1772 { 1773 return $this->oImapClient->IsSupported('THREAD=REFS') || 1774 $this->oImapClient->IsSupported('THREAD=REFERENCES') || 1775 $this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT'); 1776 } 1777 1778 /** 1779 * @param string $sFolderName 1780 * @param array $aUids 1781 * 1782 * @return \MailSo\Mail\MessageCollection 1783 * 1784 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 1785 * @throws \MailSo\Net\Exceptions\Exception 1786 * @throws \MailSo\Imap\Exceptions\Exception 1787 */ 1788 public function MessageListSimple($sFolderName, $aUids) 1789 { 1790 if (0 === \strlen($sFolderName) || !\MailSo\Base\Validator::NotEmptyArray($aUids)) 1791 { 1792 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 1793 } 1794 1795 $this->oImapClient->FolderExamine($sFolderName); 1796 1797 $oMessageCollection = \MailSo\Mail\MessageCollection::NewInstance(); 1798 $oMessageCollection->FolderName = $sFolderName; 1799 1800 $this->MessageListByRequestIndexOrUids($oMessageCollection, $aUids, true, true); 1801 1802 return $oMessageCollection->GetAsArray(); 1803 } 1804 1805 /** 1806 * @param \MailSo\Cache\CacheClient|null $oCacher 1807 * @param string $sSearch 1808 * @param string $sFilter 1809 * @param string $sFolderName 1810 * @param string $sFolderHash 1811 * @param bool $bUseSortIfSupported = false 1812 * 1813 * @return array 1814 * 1815 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 1816 * @throws \MailSo\Net\Exceptions\Exception 1817 * @throws \MailSo\Imap\Exceptions\Exception 1818 */ 1819 public function GetUids($oCacher, $sSearch, $sFilter, $sFolderName, $sFolderHash, $bUseSortIfSupported = false) 1820 { 1821 $aResultUids = false; 1822 $bUidsFromCacher = false; 1823 $bUseCacheAfterSearch = true; 1824 1825 $sSerializedHash = ''; 1826 $sSerializedLog = ''; 1827 1828 $bUseSortIfSupported = $bUseSortIfSupported ? !!$this->oImapClient->IsSupported('SORT') : false; 1829 1830 if (0 < \strlen($sSearch)) 1831 { 1832 $bUseSortIfSupported = false; 1833 } 1834 1835 $sSearchCriterias = $this->getImapSearchCriterias($sSearch, $sFilter, 0, $bUseCacheAfterSearch); 1836 if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited()) 1837 { 1838 $sSerializedHash = 'GetUids/'. 1839 ($bUseSortIfSupported ? 'S': 'N').'/'. 1840 $this->GenerateImapClientHash().'/'. 1841 $sFolderName.'/'.$sSearchCriterias; 1842 1843 $sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.''; 1844 1845 $sSerialized = $oCacher->Get($sSerializedHash); 1846 if (!empty($sSerialized)) 1847 { 1848 $aSerialized = @\json_decode($sSerialized, true); 1849 if (\is_array($aSerialized) && isset($aSerialized['FolderHash'], $aSerialized['Uids']) && 1850 $sFolderHash === $aSerialized['FolderHash'] && 1851 \is_array($aSerialized['Uids']) 1852 ) 1853 { 1854 if ($this->oLogger) 1855 { 1856 $this->oLogger->Write('Get Serialized UIDS from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']'); 1857 } 1858 1859 $aResultUids = $aSerialized['Uids']; 1860 $bUidsFromCacher = true; 1861 } 1862 } 1863 } 1864 1865 if (!\is_array($aResultUids)) 1866 { 1867 $aResultUids = $bUseSortIfSupported ? 1868 $this->oImapClient->MessageSimpleSort(array('REVERSE DATE'), $sSearchCriterias, true) : 1869 $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8') 1870 ; 1871 1872 if (!$bUidsFromCacher && $bUseCacheAfterSearch && \is_array($aResultUids) && $oCacher && $oCacher->IsInited() && 0 < \strlen($sSerializedHash)) 1873 { 1874 $oCacher->Set($sSerializedHash, @\json_encode(array( 1875 'FolderHash' => $sFolderHash, 1876 'Uids' => $aResultUids 1877 ))); 1878 1879 if ($this->oLogger) 1880 { 1881 $this->oLogger->Write('Save Serialized UIDS to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']'); 1882 } 1883 } 1884 } 1885 1886 return \is_array($aResultUids) ? $aResultUids : array(); 1887 } 1888 1889 /** 1890 * @param string $sFolderName 1891 * @param int $iOffset = 0 1892 * @param int $iLimit = 10 1893 * @param string $sSearch = '' 1894 * @param string $sPrevUidNext = '' 1895 * @param \MailSo\Cache\CacheClient|null $oCacher = null 1896 * @param bool $bUseSortIfSupported = false 1897 * @param bool $bUseThreadSortIfSupported = false 1898 * @param bool $bUseESearchOrESortRequest = false 1899 * @param string $sThreadUid = '' 1900 * @param string $sFilter = '' 1901 * 1902 * @return \MailSo\Mail\MessageCollection 1903 * 1904 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 1905 * @throws \MailSo\Net\Exceptions\Exception 1906 * @throws \MailSo\Imap\Exceptions\Exception 1907 */ 1908 public function MessageList($sFolderName, $iOffset = 0, $iLimit = 10, $sSearch = '', $sPrevUidNext = '', $oCacher = null, 1909 $bUseSortIfSupported = false, $bUseThreadSortIfSupported = false, $sThreadUid = '', $sFilter = '') 1910 { 1911 $sFilter = \trim($sFilter); 1912 $sSearch = \trim($sSearch); 1913 if (!\MailSo\Base\Validator::RangeInt($iOffset, 0) || 1914 !\MailSo\Base\Validator::RangeInt($iLimit, 0, 999)) 1915 { 1916 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 1917 } 1918 1919 $bUseFilter = '' !== $sFilter; 1920 1921 $this->oImapClient->FolderSelect($sFolderName); 1922 1923 $oMessageCollection = MessageCollection::NewInstance(); 1924 $oMessageCollection->FolderName = $sFolderName; 1925 $oMessageCollection->Offset = $iOffset; 1926 $oMessageCollection->Limit = $iLimit; 1927 $oMessageCollection->Search = $sSearch; 1928 $oMessageCollection->ThreadUid = $sThreadUid; 1929 $oMessageCollection->Filtered = '' !== \MailSo\Config::$MessageListPermanentFilter; 1930 1931 $aUids = array(); 1932 $mAllSortedUids = null; 1933 $mAllThreads = null; 1934 1935 $iThreadUid = empty($sThreadUid) ? 0 : (int) $sThreadUid; 1936 1937 $iMessageRealCount = 0; 1938 $iMessageUnseenCount = 0; 1939 $sUidNext = '0'; 1940 $sHighestModSeq = ''; 1941 1942 $bUseSortIfSupported = $bUseSortIfSupported ? $this->oImapClient->IsSupported('SORT') : false; 1943 1944 $bUseThreadSortIfSupported = $bUseThreadSortIfSupported ? 1945 ($this->oImapClient->IsSupported('THREAD=REFS') || $this->oImapClient->IsSupported('THREAD=REFERENCES') || $this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT')) : false; 1946 1947 if (!empty($sThreadUid) && !$bUseThreadSortIfSupported) 1948 { 1949 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 1950 } 1951 1952 if (!$oCacher || !($oCacher instanceof \MailSo\Cache\CacheClient)) 1953 { 1954 $oCacher = null; 1955 } 1956 1957 $this->initFolderValues($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext, $sHighestModSeq); 1958 1959 if ($bUseFilter) 1960 { 1961 $iMessageUnseenCount = 0; 1962 } 1963 1964 $oMessageCollection->FolderHash = $this->GenerateFolderHash( 1965 $sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext, $sHighestModSeq); 1966 1967 $oMessageCollection->UidNext = $sUidNext; 1968 1969 if (empty($sThreadUid) && 0 < \strlen($sPrevUidNext) && 'INBOX' === $sFolderName) 1970 { 1971 $oMessageCollection->NewMessages = $this->getFolderNextMessageInformation( 1972 $sFolderName, $sPrevUidNext, $sUidNext); 1973 } 1974 1975 $bSearch = false; 1976 $bMessageListOptimization = 0 < \MailSo\Config::$MessageListCountLimitTrigger && 1977 \MailSo\Config::$MessageListCountLimitTrigger < $iMessageRealCount; 1978 1979 if ($bMessageListOptimization) 1980 { 1981 $bUseSortIfSupported = false; 1982 $bUseThreadSortIfSupported = false; 1983 } 1984 1985 if (0 < $iMessageRealCount && !$bMessageListOptimization) 1986 { 1987 $mAllSortedUids = $this->GetUids($oCacher, '', $sFilter, 1988 $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported); 1989 1990 $mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap( 1991 $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $mAllSortedUids, $oCacher) : null; 1992 1993 if ($bUseThreadSortIfSupported && 0 < $iThreadUid && \is_array($mAllThreads)) 1994 { 1995 $aUids = array(); 1996 $iResultRootUid = 0; 1997 1998 if (isset($mAllThreads[$iThreadUid])) 1999 { 2000 $iResultRootUid = $iThreadUid; 2001 if (\is_array($mAllThreads[$iThreadUid])) 2002 { 2003 $aUids = $mAllThreads[$iThreadUid]; 2004 } 2005 } 2006 else 2007 { 2008 foreach ($mAllThreads as $iRootUid => $mSubUids) 2009 { 2010 if (\is_array($mSubUids) && \in_array($iThreadUid, $mSubUids)) 2011 { 2012 $iResultRootUid = $iRootUid; 2013 $aUids = $mSubUids; 2014 continue; 2015 } 2016 } 2017 } 2018 2019 if (0 < $iResultRootUid && \in_array($iResultRootUid, $mAllSortedUids)) 2020 { 2021 \array_unshift($aUids, $iResultRootUid); 2022 } 2023 } 2024 else if ($bUseThreadSortIfSupported && \is_array($mAllThreads)) 2025 { 2026 $aUids = \array_keys($mAllThreads); 2027 } 2028 else 2029 { 2030 $bUseThreadSortIfSupported = false; 2031 $aUids = $mAllSortedUids; 2032 } 2033 2034 if (0 < \strlen($sSearch) && \is_array($aUids)) 2035 { 2036 $aSearchedUids = $this->GetUids($oCacher, $sSearch, $sFilter, 2037 $oMessageCollection->FolderName, $oMessageCollection->FolderHash); 2038 2039 if (\is_array($aSearchedUids) && 0 < \count($aSearchedUids)) 2040 { 2041 $aFlippedSearchedUids = \array_flip($aSearchedUids); 2042 2043 $bSearch = true; 2044 $aNewUids = array(); 2045 2046 foreach ($aUids as $iUid) 2047 { 2048 if (isset($aFlippedSearchedUids[$iUid])) 2049 { 2050 $aNewUids[] = $iUid; 2051 } 2052 else if ($bUseThreadSortIfSupported && 0 === $iThreadUid && isset($mAllThreads[$iUid]) && \is_array($mAllThreads[$iUid])) 2053 { 2054 foreach ($mAllThreads[$iUid] as $iSubUid) 2055 { 2056 if (isset($aFlippedSearchedUids[$iSubUid])) 2057 { 2058 $aNewUids[] = $iUid; 2059 continue; 2060 } 2061 } 2062 } 2063 } 2064 2065 $aUids = \array_unique($aNewUids); 2066 unset($aNewUids); 2067 } 2068 else 2069 { 2070 $aUids = array(); 2071 } 2072 } 2073 2074 if (\is_array($aUids)) 2075 { 2076 $oMessageCollection->MessageCount = $iMessageRealCount; 2077 $oMessageCollection->MessageUnseenCount = $iMessageUnseenCount; 2078 $oMessageCollection->MessageResultCount = \count($aUids); 2079 2080 if (0 < \count($aUids)) 2081 { 2082 $aRequestUids = \array_slice($aUids, $iOffset, $iLimit); 2083 $this->MessageListByRequestIndexOrUids($oMessageCollection, $aRequestUids, true); 2084 } 2085 } 2086 } 2087 else if (0 < $iMessageRealCount) 2088 { 2089 if ($this->oLogger) 2090 { 2091 $this->oLogger->Write('List optimization (count: '.$iMessageRealCount. 2092 ', limit:'.\MailSo\Config::$MessageListCountLimitTrigger.')'); 2093 } 2094 2095 $oMessageCollection->MessageCount = $iMessageRealCount; 2096 $oMessageCollection->MessageUnseenCount = $iMessageUnseenCount; 2097 2098 if (0 < \strlen($sSearch) || $bUseFilter) 2099 { 2100 $aUids = $this->GetUids($oCacher, $sSearch, $sFilter, 2101 $oMessageCollection->FolderName, $oMessageCollection->FolderHash); 2102 2103 if (0 < \count($aUids)) 2104 { 2105 $oMessageCollection->MessageResultCount = \count($aUids); 2106 2107 $aRequestUids = \array_slice($aUids, $iOffset, $iLimit); 2108 $this->MessageListByRequestIndexOrUids($oMessageCollection, $aRequestUids, true); 2109 } 2110 else 2111 { 2112 $oMessageCollection->MessageResultCount = 0; 2113 } 2114 } 2115 else 2116 { 2117 $oMessageCollection->MessageResultCount = $iMessageRealCount; 2118 2119 if (1 < $iMessageRealCount) 2120 { 2121 $aRequestIndexes = \array_slice(array_reverse(range(1, $iMessageRealCount)), $iOffset, $iLimit); 2122 } 2123 else 2124 { 2125 $aRequestIndexes = \array_slice(array(1), $iOffset, $iLimit); 2126 } 2127 2128 $this->MessageListByRequestIndexOrUids($oMessageCollection, $aRequestIndexes, false); 2129 } 2130 } 2131 2132 if ($bUseThreadSortIfSupported && 0 === $iThreadUid && \is_array($mAllThreads) && 0 < \count($mAllThreads)) 2133 { 2134 $oMessageCollection->ForeachList(function (/* @var $oMessage \MailSo\Mail\Message */ $oMessage) use ($mAllThreads) { 2135 2136 $iUid = $oMessage->Uid(); 2137 if (isset($mAllThreads[$iUid]) && \is_array($mAllThreads[$iUid]) && 0 < \count($mAllThreads[$iUid])) 2138 { 2139 $aSubThreads = $mAllThreads[$iUid]; 2140 \array_unshift($aSubThreads, $iUid); 2141 2142 $oMessage->SetThreads(\array_map('trim', $aSubThreads)); 2143 unset($aSubThreads); 2144 } 2145 }); 2146 } 2147 2148 return $oMessageCollection; 2149 } 2150 2151 /** 2152 * @return array|false 2153 */ 2154 public function Quota() 2155 { 2156 return $this->oImapClient->Quota(); 2157 } 2158 2159 /** 2160 * @param string $sFolderName 2161 * @param string $sMessageId 2162 * 2163 * @return int|null 2164 */ 2165 public function FindMessageUidByMessageId($sFolderName, $sMessageId) 2166 { 2167 if (0 === \strlen($sMessageId)) 2168 { 2169 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 2170 } 2171 2172 $this->oImapClient->FolderExamine($sFolderName); 2173 2174 $aUids = $this->oImapClient->MessageSimpleSearch( 2175 'HEADER Message-ID '.$sMessageId, true); 2176 2177 return \is_array($aUids) && 1 === \count($aUids) && \is_numeric($aUids[0]) ? (int) $aUids[0] : null; 2178 } 2179 2180 /** 2181 * @param array $aMailFoldersHelper 2182 * @param int $iOptimizationLimit = 0 2183 * 2184 * @return array 2185 */ 2186 public function folderListOptimization($aMailFoldersHelper, $iOptimizationLimit = 0) 2187 { 2188 // optimization 2189 if (10 < $iOptimizationLimit && \is_array($aMailFoldersHelper) && $iOptimizationLimit < \count($aMailFoldersHelper)) 2190 { 2191 if ($this->oLogger) 2192 { 2193 $this->oLogger->Write('Start optimization (limit:'.$iOptimizationLimit.') for '.\count($aMailFoldersHelper).' folders'); 2194 } 2195 2196 $iForeachLimit = 1; 2197 2198 $aFilteredNames = array( 2199 'inbox', 2200 'sent', 'send', 'outbox', 'sentmail', 'sendmail', 2201 'drafts', 'draft', 2202 'junk', 'spam', 'spambucket', 2203 'trash', 'bin', 'deleted', 2204 'archives', 'archive', 'allmail', 'all', 2205 'starred', 'flagged', 'important', 2206 'contacts', 'chats' 2207 ); 2208 2209 $aNewMailFoldersHelper = array(); 2210 2211 $iCountLimit = $iForeachLimit; 2212 2213 foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) 2214 { 2215 // mandatory folders 2216 if ($oFolder && \in_array(\str_replace(' ', '', \strtolower($oFolder->NameRaw())), $aFilteredNames)) 2217 { 2218 $aNewMailFoldersHelper[] = $oFolder; 2219 $aMailFoldersHelper[$iIndex] = null; 2220 } 2221 } 2222 2223 foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) 2224 { 2225 // subscribed folders 2226 if ($oFolder && $oFolder->IsSubscribed()) 2227 { 2228 $aNewMailFoldersHelper[] = $oFolder; 2229 2230 $aMailFoldersHelper[$iIndex] = null; 2231 $iCountLimit--; 2232 } 2233 2234 if (0 > $iCountLimit) 2235 { 2236 if ($iOptimizationLimit < \count($aNewMailFoldersHelper)) 2237 { 2238 break; 2239 } 2240 else 2241 { 2242 $iCountLimit = $iForeachLimit; 2243 } 2244 } 2245 } 2246 2247 $iCountLimit = $iForeachLimit; 2248 if ($iOptimizationLimit >= \count($aNewMailFoldersHelper)) 2249 { 2250 // name filter 2251 foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) 2252 { 2253 if ($oFolder && !\preg_match('/[{}\[\]]/', $oFolder->NameRaw())) 2254 { 2255 $aNewMailFoldersHelper[] = $oFolder; 2256 2257 $aMailFoldersHelper[$iIndex] = null; 2258 $iCountLimit--; 2259 } 2260 2261 if (0 > $iCountLimit) 2262 { 2263 if ($iOptimizationLimit < \count($aNewMailFoldersHelper)) 2264 { 2265 break; 2266 } 2267 else 2268 { 2269 $iCountLimit = $iForeachLimit; 2270 } 2271 } 2272 } 2273 } 2274 2275 $iCountLimit = $iForeachLimit; 2276 if ($iOptimizationLimit >= \count($aNewMailFoldersHelper)) 2277 { 2278 // other 2279 foreach ($aMailFoldersHelper as $iIndex => /* @var $oImapFolder \MailSo\Mail\Folder */ $oFolder) 2280 { 2281 if ($oFolder) 2282 { 2283 $aNewMailFoldersHelper[] = $oFolder; 2284 2285 $aMailFoldersHelper[$iIndex] = null; 2286 $iCountLimit--; 2287 } 2288 2289 if (0 > $iCountLimit) 2290 { 2291 if ($iOptimizationLimit < \count($aNewMailFoldersHelper)) 2292 { 2293 break; 2294 } 2295 else 2296 { 2297 $iCountLimit = $iForeachLimit; 2298 } 2299 } 2300 } 2301 } 2302 2303 $aMailFoldersHelper = $aNewMailFoldersHelper; 2304 2305 if ($this->oLogger) 2306 { 2307 $this->oLogger->Write('Result optimization: '.\count($aMailFoldersHelper).' folders'); 2308 } 2309 } 2310 2311 return $aMailFoldersHelper; 2312 } 2313 2314 /** 2315 * @param string $sParent = '' 2316 * @param string $sListPattern = '*' 2317 * @param bool $bUseListSubscribeStatus = false 2318 * @param int $iOptimizationLimit = 0 2319 * 2320 * @return \MailSo\Mail\FolderCollection|false 2321 */ 2322 public function Folders($sParent = '', $sListPattern = '*', $bUseListSubscribeStatus = true, $iOptimizationLimit = 0) 2323 { 2324 $oFolderCollection = false; 2325 2326 $aSubscribedFolders = null; 2327 if ($bUseListSubscribeStatus) 2328 { 2329 try 2330 { 2331 $aSubscribedFolders = $this->oImapClient->FolderSubscribeList($sParent, $sListPattern); 2332 } 2333 catch (\Exception $oException) 2334 { 2335 unset($oException); 2336 } 2337 } 2338 2339 $aImapSubscribedFoldersHelper = null; 2340 if (\is_array($aSubscribedFolders)) 2341 { 2342 $aImapSubscribedFoldersHelper = array(); 2343 foreach ($aSubscribedFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) 2344 { 2345 $aImapSubscribedFoldersHelper[] = $oImapFolder->FullNameRaw(); 2346 } 2347 } 2348 2349 $aFolders = $this->oImapClient->FolderList($sParent, $sListPattern); 2350 2351 $bOptimized = false; 2352 $aMailFoldersHelper = null; 2353 2354 if (\is_array($aFolders)) 2355 { 2356 $aMailFoldersHelper = array(); 2357 2358 foreach ($aFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) 2359 { 2360 $aMailFoldersHelper[] = Folder::NewInstance($oImapFolder, 2361 (null === $aImapSubscribedFoldersHelper || \in_array($oImapFolder->FullNameRaw(), $aImapSubscribedFoldersHelper)) || 2362 $oImapFolder->IsInbox() 2363 ); 2364 } 2365 2366 $iCount = \count($aMailFoldersHelper); 2367 $aMailFoldersHelper = $this->folderListOptimization($aMailFoldersHelper, $iOptimizationLimit); 2368 2369 $bOptimized = $iCount !== \count($aMailFoldersHelper); 2370 } 2371 2372 if (\is_array($aMailFoldersHelper)) 2373 { 2374 $oFolderCollection = FolderCollection::NewInstance(); 2375 $oFolderCollection->InitByUnsortedMailFolderArray($aMailFoldersHelper); 2376 2377 $oFolderCollection->Optimized = $bOptimized; 2378 } 2379 2380 if ($oFolderCollection) 2381 { 2382 $oFolderCollection->SortByCallback(function ($oFolderA, $oFolderB) { 2383 $sA = \strtoupper($oFolderA->FullNameRaw()); 2384 $sB = \strtoupper($oFolderB->FullNameRaw()); 2385 switch (true) 2386 { 2387 case 'INBOX' === $sA: 2388 return -1; 2389 case 'INBOX' === $sB: 2390 return 1; 2391 case '[GMAIL]' === $sA: 2392 return -1; 2393 case '[GMAIL]' === $sB: 2394 return 1; 2395 } 2396 2397 return \strnatcasecmp($oFolderA->FullName(), $oFolderB->FullName()); 2398 }); 2399 2400 $oNamespace = $this->oImapClient->GetNamespace(); 2401 if ($oNamespace) 2402 { 2403 $oFolderCollection->SetNamespace($oNamespace->GetPersonalNamespace()); 2404 } 2405 2406 $oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported(); 2407 } 2408 2409 return $oFolderCollection; 2410 } 2411 2412 /** 2413 * @param string $sFolderNameInUtf8 2414 * @param string $sFolderParentFullNameRaw = '' 2415 * @param bool $bSubscribeOnCreation = true 2416 * @param string $sDelimiter = '' 2417 * 2418 * @return \MailSo\Mail\MailClient 2419 * 2420 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2421 */ 2422 public function FolderCreate($sFolderNameInUtf8, $sFolderParentFullNameRaw = '', $bSubscribeOnCreation = true, $sDelimiter = '') 2423 { 2424 if (!\MailSo\Base\Validator::NotEmptyString($sFolderNameInUtf8, true) || 2425 !\is_string($sFolderParentFullNameRaw)) 2426 { 2427 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 2428 } 2429 2430 $sFolderNameInUtf8 = \trim($sFolderNameInUtf8); 2431 2432 if (0 === \strlen($sDelimiter) || 0 < \strlen(\trim($sFolderParentFullNameRaw))) 2433 { 2434 $aFolders = $this->oImapClient->FolderList('', 0 === \strlen(\trim($sFolderParentFullNameRaw)) ? 'INBOX' : $sFolderParentFullNameRaw); 2435 if (!\is_array($aFolders) || !isset($aFolders[0])) 2436 { 2437 // TODO 2438 throw new \MailSo\Mail\Exceptions\RuntimeException( 2439 0 === \strlen(trim($sFolderParentFullNameRaw)) 2440 ? 'Cannot get folder delimiter' 2441 : 'Cannot create folder in non-existen parent folder'); 2442 } 2443 2444 $sDelimiter = $aFolders[0]->Delimiter(); 2445 if (0 < \strlen($sDelimiter) && 0 < \strlen(\trim($sFolderParentFullNameRaw))) 2446 { 2447 $sFolderParentFullNameRaw .= $sDelimiter; 2448 } 2449 } 2450 2451 $sFullNameRawToCreate = \MailSo\Base\Utils::ConvertEncoding($sFolderNameInUtf8, 2452 \MailSo\Base\Enumerations\Charset::UTF_8, 2453 \MailSo\Base\Enumerations\Charset::UTF_7_IMAP); 2454 2455 if (0 < \strlen($sDelimiter) && false !== \strpos($sFullNameRawToCreate, $sDelimiter)) 2456 { 2457 // TODO 2458 throw new \MailSo\Mail\Exceptions\RuntimeException( 2459 'New folder name contains delimiter'); 2460 } 2461 2462 $sFullNameRawToCreate = $sFolderParentFullNameRaw.$sFullNameRawToCreate; 2463 2464 $this->oImapClient->FolderCreate($sFullNameRawToCreate); 2465 2466 if ($bSubscribeOnCreation) 2467 { 2468 $this->oImapClient->FolderSubscribe($sFullNameRawToCreate); 2469 } 2470 2471 return $this; 2472 } 2473 2474 /** 2475 * @param string $sPrevFolderFullNameRaw 2476 * @param string $sNextFolderFullNameInUtf 2477 * @param bool $bSubscribeOnMove = true 2478 * 2479 * @return \MailSo\Mail\MailClient 2480 * 2481 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2482 */ 2483 public function FolderMove($sPrevFolderFullNameRaw, $sNextFolderFullNameInUtf, $bSubscribeOnMove = true) 2484 { 2485 return $this->folderModify($sPrevFolderFullNameRaw, $sNextFolderFullNameInUtf, false, $bSubscribeOnMove); 2486 } 2487 2488 /** 2489 * @param string $sPrevFolderFullNameRaw 2490 * @param string $sNewTopFolderNameInUtf 2491 * @param bool $bSubscribeOnRename = true 2492 * 2493 * @return \MailSo\Mail\MailClient 2494 * 2495 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2496 */ 2497 public function FolderRename($sPrevFolderFullNameRaw, $sNewTopFolderNameInUtf, $bSubscribeOnRename = true) 2498 { 2499 return $this->folderModify($sPrevFolderFullNameRaw, $sNewTopFolderNameInUtf, true, $bSubscribeOnRename); 2500 } 2501 2502 /** 2503 * @param string $sPrevFolderFullNameRaw 2504 * @param string $sNextFolderNameInUtf 2505 * @param bool $bRenameOrMove 2506 * @param bool $bSubscribeOnModify 2507 * 2508 * @return \MailSo\Mail\MailClient 2509 * 2510 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2511 */ 2512 public function folderModify($sPrevFolderFullNameRaw, $sNextFolderNameInUtf, $bRenameOrMove, $bSubscribeOnModify) 2513 { 2514 if (0 === \strlen($sPrevFolderFullNameRaw) || 0 === \strlen($sNextFolderNameInUtf)) 2515 { 2516 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 2517 } 2518 2519 $aFolders = $this->oImapClient->FolderList('', $sPrevFolderFullNameRaw); 2520 if (!\is_array($aFolders) || !isset($aFolders[0])) 2521 { 2522 // TODO 2523 throw new \MailSo\Mail\Exceptions\RuntimeException('Cannot rename non-existen folder'); 2524 } 2525 2526 $sDelimiter = $aFolders[0]->Delimiter(); 2527 $iLast = \strrpos($sPrevFolderFullNameRaw, $sDelimiter); 2528 2529 $mSubscribeFolders = null; 2530 if ($bSubscribeOnModify) 2531 { 2532 $mSubscribeFolders = $this->oImapClient->FolderSubscribeList($sPrevFolderFullNameRaw, '*'); 2533 if (\is_array($mSubscribeFolders) && 0 < count($mSubscribeFolders)) 2534 { 2535 foreach ($mSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) 2536 { 2537 $this->oImapClient->FolderUnSubscribe($oFolder->FullNameRaw()); 2538 } 2539 } 2540 } 2541 2542 $sNewFolderFullNameRaw = \MailSo\Base\Utils::ConvertEncoding($sNextFolderNameInUtf, 2543 \MailSo\Base\Enumerations\Charset::UTF_8, 2544 \MailSo\Base\Enumerations\Charset::UTF_7_IMAP); 2545 2546 if($bRenameOrMove) 2547 { 2548 if (0 < \strlen($sDelimiter) && false !== \strpos($sNewFolderFullNameRaw, $sDelimiter)) 2549 { 2550 // TODO 2551 throw new \MailSo\Mail\Exceptions\RuntimeException('New folder name contains delimiter'); 2552 } 2553 2554 $sFolderParentFullNameRaw = false === $iLast ? '' : \substr($sPrevFolderFullNameRaw, 0, $iLast + 1); 2555 $sNewFolderFullNameRaw = $sFolderParentFullNameRaw.$sNewFolderFullNameRaw; 2556 } 2557 2558 $this->oImapClient->FolderRename($sPrevFolderFullNameRaw, $sNewFolderFullNameRaw); 2559 2560 if (\is_array($mSubscribeFolders) && 0 < count($mSubscribeFolders)) 2561 { 2562 foreach ($mSubscribeFolders as /* @var $oFolder \MailSo\Imap\Folder */ $oFolder) 2563 { 2564 $sFolderFullNameRawForResubscrine = $oFolder->FullNameRaw(); 2565 if (0 === \strpos($sFolderFullNameRawForResubscrine, $sPrevFolderFullNameRaw)) 2566 { 2567 $sNewFolderFullNameRawForResubscrine = $sNewFolderFullNameRaw. 2568 \substr($sFolderFullNameRawForResubscrine, \strlen($sPrevFolderFullNameRaw)); 2569 2570 $this->oImapClient->FolderSubscribe($sNewFolderFullNameRawForResubscrine); 2571 } 2572 } 2573 } 2574 2575 return $this; 2576 } 2577 2578 /** 2579 * @param string $sFolderFullNameRaw 2580 * @param bool $bUnsubscribeOnDeletion = true 2581 * 2582 * @return \MailSo\Mail\MailClient 2583 * 2584 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2585 * @throws \MailSo\Mail\Exceptions\RuntimeException 2586 */ 2587 public function FolderDelete($sFolderFullNameRaw, $bUnsubscribeOnDeletion = true) 2588 { 2589 if (0 === \strlen($sFolderFullNameRaw) || 'INBOX' === $sFolderFullNameRaw) 2590 { 2591 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 2592 } 2593 2594 $this->oImapClient->FolderExamine($sFolderFullNameRaw); 2595 2596 $aIndexOrUids = $this->oImapClient->MessageSimpleSearch('ALL'); 2597 if (0 < \count($aIndexOrUids)) 2598 { 2599 throw new \MailSo\Mail\Exceptions\NonEmptyFolder(); 2600 } 2601 2602 $this->oImapClient->FolderExamine('INBOX'); 2603 2604 if ($bUnsubscribeOnDeletion) 2605 { 2606 $this->oImapClient->FolderUnSubscribe($sFolderFullNameRaw); 2607 } 2608 2609 $this->oImapClient->FolderDelete($sFolderFullNameRaw); 2610 2611 return $this; 2612 } 2613 2614 /** 2615 * @param string $sFolderFullNameRaw 2616 * 2617 * @return \MailSo\Mail\MailClient 2618 * 2619 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2620 */ 2621 public function FolderClear($sFolderFullNameRaw) 2622 { 2623 $this->oImapClient->FolderSelect($sFolderFullNameRaw); 2624 2625 $oFolderInformation = $this->oImapClient->FolderCurrentInformation(); 2626 if ($oFolderInformation && $oFolderInformation->Exists && 0 < $oFolderInformation->Exists) // STATUS? 2627 { 2628 $this->oImapClient->MessageStoreFlag('1:*', false, 2629 array(\MailSo\Imap\Enumerations\MessageFlag::DELETED), 2630 \MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT 2631 ); 2632 2633 $this->oImapClient->MessageExpunge(); 2634 } 2635 2636 return $this; 2637 } 2638 2639 /** 2640 * @param string $sFolderFullNameRaw 2641 * @param bool $bSubscribe 2642 * 2643 * @return \MailSo\Mail\MailClient 2644 * 2645 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2646 */ 2647 public function FolderSubscribe($sFolderFullNameRaw, $bSubscribe) 2648 { 2649 if (0 === \strlen($sFolderFullNameRaw)) 2650 { 2651 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 2652 } 2653 2654 $this->oImapClient->{($bSubscribe) ? 'FolderSubscribe' : 'FolderUnSubscribe'}($sFolderFullNameRaw); 2655 2656 return $this; 2657 } 2658 2659 /** 2660 * @param \MailSo\Log\Logger $oLogger 2661 * 2662 * @return \MailSo\Mail\MailClient 2663 * 2664 * @throws \MailSo\Base\Exceptions\InvalidArgumentException 2665 */ 2666 public function SetLogger($oLogger) 2667 { 2668 if (!($oLogger instanceof \MailSo\Log\Logger)) 2669 { 2670 throw new \MailSo\Base\Exceptions\InvalidArgumentException(); 2671 } 2672 2673 $this->oLogger = $oLogger; 2674 $this->oImapClient->SetLogger($this->oLogger); 2675 2676 return $this; 2677 } 2678} 2679