1<?php 2/** 3 * Class for IMAP/POP3 functions 4 * 5 * Moved all imap_* PHP calls into one, which should make it easier to write 6 * our own IMAP/POP3 classes in the future. 7 * 8 * Copyright 2001 Nicolas Chalanset <nicocha@free.fr> 9 * Copyright 2001 Olivier Cahagne <cahagn_o@epita.fr> 10 * Copyright 2002 Mike Rylander <mrylander@mail.com> 11 * Copyright 2008-2011 Tim Gerundt <tim@gerundt.de> 12 * 13 * This file is part of NOCC. NOCC is free software under the terms of the 14 * GNU General Public License. You should have received a copy of the license 15 * along with NOCC. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * @package NOCC 18 * @license http://www.gnu.org/licenses/ GNU General Public License 19 * @version SVN: $Id: class_local.php 2881 2020-04-28 10:35:00Z oheil $ 20 */ 21 22 23require_once 'nocc_mailstructure.php'; 24require_once 'nocc_headerinfo.php'; 25require_once 'nocc_header.php'; 26require_once 'exception.php'; 27require_once './utils/detect_cyr_charset.php'; 28require_once './utils/crypt.php'; 29 30require_once 'horde_autoloader.php'; 31 32class result 33{ 34 public $text = ''; 35 public $charset = ''; 36} 37 38//TODO: Use mail or message as name? 39class nocc_imap 40{ 41 private $server; 42 private $login; 43 private $passwd; 44 private $conn; 45 private $folder; 46 private $namespace; 47 private $_isImap; 48 49 /** 50 * ... 51 * @global object $conf 52 * @global string $lang_could_not_connect 53 * @return nocc_imap Me! 54 */ 55 public function __construct() { 56 global $conf; 57 global $lang_could_not_connect; 58 global $err_user_empty; 59 global $err_passwd_empty; 60 61 if (!isset($_SESSION['nocc_servr']) || !isset($_SESSION['nocc_folder']) || !isset($_SESSION['nocc_login']) || !isset($_SESSION['nocc_passwd'])) { 62 throw new Exception($lang_could_not_connect."(0)"); 63 } 64 65 $this->server = $_SESSION['nocc_servr']; 66 if( isset($_SESSION['ajxfolder']) ) { 67 $this->folder = $_SESSION['ajxfolder']; 68 } 69 else { 70 $this->folder = $_SESSION['nocc_folder']; 71 } 72 $this->login = $_SESSION['nocc_login']; 73 /* decrypt password */ 74 $this->passwd = decpass($_SESSION['nocc_passwd'], $conf->master_key); 75 76 $this->namespace = $_SESSION['imap_namespace']; 77 78 // $ev is set if there is a problem with the connection 79 if( ! $this->is_horde() ) { 80 $conn = @imap_open('{'.$this->server.'}'.mb_convert_encoding($this->folder, 'UTF7-IMAP', 'UTF-8'), $this->login, $this->passwd, 0); 81 } 82 else { 83 $spec=explode("/",$this->server); 84 $host_port=explode(":",$spec[0]); 85 $host=$host_port[0]; 86 $port=$host_port[1]; 87 $imap=false; 88 $pop3=false; 89 $secure="false"; 90 foreach($spec as $index => $param) { 91 if( $param=="service=imap" || preg_match("/^imap/",$param) ) { 92 $imap=true; 93 } 94 if( $param=="service=pop3" || $param=="pop3" ) { 95 $pop3=true; 96 } 97 if( preg_match("/^ssl/",$param) ) { 98 $secure=$param; 99 } 100 if( preg_match("/^tls/",$param) ) { 101 $secure=$param; 102 } 103 if( $param=="true" ) { 104 $secure="true"; 105 } 106 } 107 108 if( $pop3 ) { 109 try { 110 $conn = new Horde_Imap_Client_Socket_Pop3(array( 111 'username' => $this->login, 112 'password' => $this->passwd, 113 'hostspec' => $host, 114 'port' => $port, 115 'secure' => $secure 116 )); 117 if( $conn != null ) { 118 $conn->openMailbox($this->folder); 119 $this->_isImap = false; 120 $_SESSION['is_imap'] = $this->_isImap; 121 } 122 } catch(Horde_Imap_Client_Exception $e) { 123 throw new Exception($lang_could_not_connect."(1)".":".$e->$raw_msg); 124 } 125 } 126 else if( $imap ) { 127 try { 128 $conn = new Horde_Imap_Client_Socket(array( 129 'username' => $this->login, 130 'password' => $this->passwd, 131 'hostspec' => $host, 132 'port' => $port, 133 'secure' => $secure 134 )); 135 if( $conn != null ) { 136 $conn->openMailbox($this->folder); 137 $this->_isImap = true; 138 $_SESSION['is_imap'] = $this->_isImap; 139 } 140 } catch(Horde_Imap_Client_Exception $e) { 141 throw new Exception($lang_could_not_connect."(2)".":".$e->$raw_msg); 142 } 143 } 144 else { 145 $success=false; 146 try { 147 $conn = new Horde_Imap_Client_Socket(array( 148 'username' => $this->login, 149 'password' => $this->passwd, 150 'hostspec' => $host, 151 'port' => $port, 152 'secure' => $secure 153 )); 154 if( $conn != null ) { 155 $conn->openMailbox($this->folder); 156 $success=true; 157 $this->_isImap = true; 158 $_SESSION['is_imap'] = $this->_isImap; 159 } 160 } catch(Horde_Imap_Client_Exception $e) { 161 $log_string='NOCC: open imap connection to '.$host.' failed, trying pop3'; 162 error_log($log_string); 163 syslog(LOG_INFO,$log_string); 164 } 165 if( ! $success ) { 166 try { 167 $conn = new Horde_Imap_Client_Socket_Pop3(array( 168 'username' => $this->login, 169 'password' => $this->passwd, 170 'hostspec' => $host, 171 'port' => $port, 172 'secure' => $secure 173 )); 174 if( $conn != null ) { 175 $conn->openMailbox($this->folder); 176 $success=true; 177 $this->_isImap = false; 178 $_SESSION['is_imap'] = $this->_isImap; 179 } 180 } catch(Horde_Imap_Client_Exception $e) { 181 $error=""; 182 if( strlen($this->login)==0 ) { 183 $error=$error.$err_user_empty.".\n"; 184 } 185 if( strlen($this->passwd)==0 ) { 186 $error=$error.$err_passwd_empty.".\n"; 187 } 188 throw new Exception($error.$lang_could_not_connect."(3)"); 189 } 190 } 191 } 192 } 193 194 if (!$conn) { 195 //php.log,syslog message to be used against brute force attempts e.g. with fail2ban 196 //don't change text or rules may fail 197 if( isset($_REQUEST['enter']) ) { 198 $log_string='NOCC: failed login from rhost='.$_SERVER['REMOTE_ADDR'].' to server='.$this->server.' as user='.$_SESSION['nocc_login'].''; 199 error_log($log_string); 200 syslog(LOG_INFO,$log_string); 201 } 202 $error=""; 203 if( strlen($this->login)==0 ) { 204 $error=$error.$err_user_empty.".\n"; 205 } 206 if( strlen($this->passwd)==0 ) { 207 $error=$error.$err_passwd_empty.".\n"; 208 } 209 if( ! $this->is_horde() ) { 210 throw new Exception($error.$lang_could_not_connect.":\n".$this->last_error()); 211 } 212 else { 213 throw new Exception($error.$lang_could_not_connect); 214 } 215 } 216 217 if( isset($_REQUEST['enter']) ) { 218 $log_string='NOCC: successful login from rhost='.$_SERVER['REMOTE_ADDR'].' to server='.$_SESSION['nocc_servr'].' as user='.$_SESSION['nocc_login'].''; 219 error_log($log_string); 220 syslog(LOG_INFO,$log_string); 221 } 222 223 $this->conn = $conn; 224 $_SESSION['conn']=$conn; 225 226 if( ! $this->is_horde() ) { 227 $this->_isImap = $this->isImapCheck(); 228 $_SESSION['is_imap'] = $this->_isImap; 229 } 230 231 $_SESSION['is_horde']=$this->is_horde(); 232 233 return $this; 234 } 235 236 /** 237 * Wrap imap_rfc822_write_address 238 * @param string $mailbox The mailbox name 239 * @param string $host The email host part 240 * @param string $personal The name of the account owner 241 * @return Returns a string properly formatted email address as defined in RFC2822 242 * @todo 243 */ 244 public function write_address($mailbox, $host, $personal) { 245 if( ! $this->is_horde() ) { 246 return imap_rfc822_write_address($mailbox, $host, $personal); 247 } 248 else { 249 $r=new Horde_Mail_Rfc822_Address(); 250 $r->mailbox = $mailbox; 251 $r->host = $host; 252 $r->personal = $personal; 253 return $r->__toString(); 254 } 255 } 256 257 /** 258 * Wrap imap_rfc822_parse_headers 259 * @param string $headers The parsed headers data 260 * @param string $defaulthost The default host name 261 * @return object 262 * @todo 263 */ 264 public function parse_headers($headers, $defaulthost = "UNKNOWN" ) { 265 if( ! $this->is_horde() ) { 266 $result=imap_rfc822_parse_headers( $headers, $defaulthost); 267 } 268 else { 269 //this is called only for rfc822 header 270 $result=new stdClass(); 271 $result->subject=""; 272 $result->date=""; 273 $result->from=array(); 274 $result->from[0] = new stdClass(); 275 $result->from[0]->mailbox=""; 276 $result->from[0]->host=""; 277 $result->from[0]->personal=""; 278 $result->to=array(); 279 $result->to[0] = new stdClass(); 280 $result->to[0]->mailbox=""; 281 $result->to[0]->host=""; 282 $result->to[0]->personal=""; 283 $matches=array(); 284 if( preg_match("/^\s*Subject:\s+(.*)$/im",$headers,$matches) ) { 285 $result->subject=$matches[1]; 286 } 287 $matches=array(); 288 if( preg_match("/^\s*From:(.*)<(.*)@(.*)>\s+/im",$headers,$matches) ) { 289 $result->from[0]->personal=trim($matches[1]); 290 $result->from[0]->mailbox=trim($matches[2]); 291 $result->from[0]->host=trim($matches[3]); 292 } 293 else if( preg_match("/^\s*From:\s*?<*(.*)@(.*)>*\s+/im",$headers,$matches) ) { 294 $result->from[0]->personal=""; 295 $result->from[0]->mailbox=trim($matches[1]); 296 $result->from[0]->host=trim($matches[2]); 297 } 298 $matches=array(); 299 if( preg_match("/^\s*To:(.*)<(.*)@(.*)>\s+/im",$headers,$matches) ) { 300 $result->to[0]->personal=trim($matches[1]); 301 $result->to[0]->mailbox=trim($matches[2]); 302 $result->to[0]->host=trim($matches[3]); 303 } 304 else if( preg_match("/^\s*To:\s*?<*(.*)@(.*)>*\s+/im",$headers,$matches) ) { 305 $result->to[0]->personal=""; 306 $result->to[0]->mailbox=trim($matches[1]); 307 $result->to[0]->host=trim($matches[2]); 308 } 309 $matches=array(); 310 if( preg_match("/^\s*Date:\s+(.*)$/im",$headers,$matches) ) { 311 $result->date=$matches[1]; 312 } 313 } 314 return $result; 315 } 316 317 /** 318 * Get the last IMAP error that occurred during this page request 319 * @return string Last IMAP error 320 * @todo Rename to getLastError()? 321 */ 322 public function last_error() { 323 if( ! $this->is_horde() ) { 324 return imap_last_error(); 325 } 326 else { 327 return ""; 328 } 329 } 330 331 /** 332 * Search messages matching the given search criteria 333 * @param string $criteria Search criteria 334 * @return array Messages 335 */ 336 public function search($criteria) { 337 $messages = array(); 338 if( ! $this->is_horde() ) { 339 $search_result = @imap_search($this->conn, $criteria); 340 if (is_array($search_result)) { 341 return $search_result; 342 } 343 } 344 else { 345 $elements = explode(" ",$criteria); 346 $query = new Horde_Imap_Client_Search_Query(); 347 for( $i=0; $i<count($elements); $i++ ) { 348 switch(strtolower($elements[$i])) { 349 case "unseen": 350 //$query->newMsgs(true); 351 $query->flag("\\Seen",false); 352 break; 353 case "subject": 354 case "to": 355 case "from": 356 case "cc": 357 if( $i+1 < count($elements) ) { 358 $par=$elements[$i+1]; 359 $par=preg_replace("/^\"/","",$par); 360 $par=preg_replace("/\"$/","",$par); 361 $query->headerText($elements[$i],$par); 362 $i++; 363 } 364 break; 365 case "body": 366 if( $i+1 < count($elements) ) { 367 $par=$elements[$i+1]; 368 $par=preg_replace("/^\"/","",$par); 369 $par=preg_replace("/\"$/","",$par); 370 $query->text($par); 371 $i++; 372 } 373 break; 374 } 375 } 376 $options=array( 377 "sequence" => true, 378 ); 379 try { 380 $horde_search=$this->conn->search($this->folder,$query,$options); 381 if( $horde_search['count'] > 0 ) { 382 $messages = $horde_search['match']->ids; 383 } 384 } 385 catch(Horde_Imap_Client_Exception $e) { 386 $log_string='NOCC: search failed'; 387 error_log($log_string); 388 syslog(LOG_INFO,$log_string); 389 } 390 } 391 return $messages; 392 } 393 394 /** 395 * Fetch mail structure 396 * @param integer $msgnum Message number 397 * @return NOCC_MailStructure Mail structure 398 */ 399 public function fetchstructure($msgnum) { 400 $parts_info=array(); 401 if( ! $this->is_horde() ) { 402 $structure = @imap_fetchstructure($this->conn, $msgnum); 403 } 404 else { 405 try { 406 $query=new Horde_Imap_Client_Fetch_Query(); 407 $query->structure(); 408 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 409 $options=array( 410 "ids" => $ids 411 ); 412 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 413 if( $fetch_result->count() == 1 ) { 414 $structure=$fetch_result->first()->getStructure(); 415 } 416 } 417 catch(Horde_Imap_Client_Exception $e) { 418 $log_string='NOCC: fetching structure failed'; 419 error_log($log_string); 420 syslog(LOG_INFO,$log_string); 421 } 422 423 $rec = function($part) use (&$rec, &$parts_info, $msgnum) { 424 $mimeID=$part->getMimeId(); 425 $query=new Horde_Imap_Client_Fetch_Query(); 426 $opts=array( 427 "peek" => true, 428 ); 429 $query->mimeHeader($mimeID,$opts); 430 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 431 $options=array( 432 "ids" => $ids, 433 ); 434 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 435 if( $fetch_result != null && $fetch_result->count()>0 && $fetch_result->first() != null ) { 436 $mimeHeader=$fetch_result->first()->getMimeHeader($mimeID,Horde_Imap_Client_Data_Fetch::HEADER_PARSE); 437 $parts_info[$mimeID]=array( 438 'encoding' => strtolower($mimeHeader->getHeader("Content-Transfer-Encoding")->value), 439 'contentId' => $mimeHeader->getHeader("Content-ID")->value 440 ); 441 } 442 $subparts=$part->getParts(); 443 if( count($subparts)>0 ) { 444 foreach( $subparts as $part ) { 445 $rec($part); 446 } 447 } 448 }; 449 $rec($structure); 450 } 451 if (!is_object($structure)) { 452 throw new Exception('imap_fetchstructure() did not return an object.'); 453 } 454 return new NOCC_MailStructure($structure,$this->is_horde(), $parts_info); 455 } 456 457 /** 458 * Fetch header 459 * @param integer $msgnum Message number 460 * @return NOCC_Header Header 461 * @todo Throw exceptions? 462 */ 463 public function fetchheader($msgnum) { 464 if( ! $this->is_horde() ) { 465 $header = imap_fetchheader($this->conn, $msgnum); 466 } 467 else { 468 try { 469 $query=new Horde_Imap_Client_Fetch_Query(); 470 $query_options=array( 471 "peek" => true, 472 ); 473 $query->headerText($query_options); 474 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 475 $options=array( 476 "ids" => $ids, 477 ); 478 $header_fetch=$this->conn->fetch($this->folder,$query,$options); 479 if( $header_fetch->count() >= 1 && $header_fetch->first() != null ) { 480 $header=$header_fetch->first()->getHeaderText(); 481 } 482 } 483 catch(Horde_Imap_Client_Exception $e) { 484 $log_string='NOCC: fetching header failed'; 485 error_log($log_string); 486 syslog(LOG_INFO,$log_string); 487 } 488 } 489 return new NOCC_Header($header, $this->is_horde()); 490 } 491 492 /** 493 * Fetch body 494 * @param integer $msgnum Message number 495 * @param string $partnum Part number 496 * @return string Body 497 * @todo Throw exceptions? 498 */ 499 public function fetchbody($msgnum, $partnum, $mimeid="", $decode=true, $rfc822=false) { 500 $bodyText=""; 501 if( ! $this->is_horde() ) { 502 $bodyText=@imap_fetchbody($this->conn, $msgnum, $partnum); 503 } 504 else { 505 try { 506 if( $rfc822 ) { 507 $headerText=""; 508 $body_only=false; 509 $header_only=false; 510 $matches=array(); 511 if( preg_match("/(\d*).*?\.(\d*)/",$partnum, $matches) ) { 512 if( $matches[2] == "0" ) { 513 $header_only=true; 514 } 515 if( $matches[2] == "1" ) { 516 $body_only=true; 517 } 518 //$partnum=$matches[1]; 519 //$mimeid=$partnum; 520 } 521 if( ! $body_only ) { 522 $query=new Horde_Imap_Client_Fetch_Query(); 523 $opts=array( 524 "id" => $mimeid, 525 "peek" => true, 526 ); 527 $query->headerText($opts); 528 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 529 $options=array( 530 "ids" => $ids, 531 ); 532 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 533 if( $fetch_result->count() >= 1 ) { 534 $headerText=$fetch_result->first()->getHeaderText($mimeid); 535 } 536 } 537 if( $header_only ) { 538 $bodyText=$headerText; 539 } 540 else { 541 $query=new Horde_Imap_Client_Fetch_Query(); 542 $opts=array( 543 "id" => $mimeid, 544 ); 545 $query->bodyText($opts); 546 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 547 $options=array( 548 "ids" => $ids, 549 ); 550 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 551 if( $fetch_result->count() >= 1 ) { 552 $bodyText=$fetch_result->first()->getBodyText($mimeid); 553 } 554 if( strlen($bodyText) == 0 ) { 555 $query=new Horde_Imap_Client_Fetch_Query(); 556 $opts=array( 557 "decode" => $decode, 558 ); 559 $query->bodyPart($mimeid,$opts); 560 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 561 $options=array( 562 "ids" => $ids, 563 ); 564 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 565 if( $fetch_result->count() >= 1 ) { 566 $bodyText=$fetch_result->first()->getBodyPart($mimeid); 567 } 568 } 569 $bodyText=$headerText.$bodyText; 570 } 571 } 572 else { 573 $query=new Horde_Imap_Client_Fetch_Query(); 574 $opts=array( 575 "decode" => $decode, 576 ); 577 $query->bodyPart($mimeid,$opts); 578 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 579 $options=array( 580 "ids" => $ids, 581 ); 582 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 583 if( $fetch_result->count() >= 1 ) { 584 $bodyText=$fetch_result->first()->getBodyPart($mimeid); 585 } 586 } 587 } 588 catch(Horde_Imap_Client_Exception $e) { 589 $log_string='NOCC: fetching body text failed'; 590 error_log($log_string); 591 syslog(LOG_INFO,$log_string); 592 } 593 594 } 595 return $bodyText; 596 } 597 598 /** 599 * Fetch the size of message 600 * @param integer $msgnum Message number 601 * @return int size in bytes 602 */ 603 public function get_size($msgnum) { 604 $size=0; 605 if( ! $this->is_horde() ) { 606 $overview=imap_fetch_overview($this->conn,$msgnum); 607 if( isset($overview[0]->size) ) { 608 $size=$overview[0]->size; 609 } 610 } 611 else { 612 try { 613 $query=new Horde_Imap_Client_Fetch_Query(); 614 $query->size(); 615 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 616 $options=array( 617 "ids" => $ids, 618 ); 619 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 620 if( $fetch_result->count() >= 1 ) { 621 $size=$fetch_result->first()->getSize(); 622 } 623 } 624 catch(Horde_Imap_Client_Exception $e) { 625 $log_string='NOCC: fetching message size failed'; 626 error_log($log_string); 627 syslog(LOG_INFO,$log_string); 628 } 629 } 630 return $size; 631 } 632 633 /** 634 * Fetch the entire message 635 * @param integer $msgnum Message number 636 * @return string Message 637 * @todo Throw exceptions? 638 */ 639 public function fetchmessage($msgnum) { 640 $fullText=""; 641 if( ! $this->is_horde() ) { 642 $fullText=@imap_fetchbody($this->conn, $msgnum, ''); 643 } 644 else { 645 try { 646 $query=new Horde_Imap_Client_Fetch_Query(); 647 $query->fullText(); 648 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 649 $options=array( 650 "ids" => $ids, 651 ); 652 $fetch_result=$this->conn->fetch($this->folder,$query,$options); 653 if( $fetch_result->count() >= 1 ) { 654 $fullText=$fetch_result->first()->getFullMsg(); 655 } 656 } 657 catch(Horde_Imap_Client_Exception $e) { 658 $log_string='NOCC: fetching message size failed'; 659 error_log($log_string); 660 syslog(LOG_INFO,$log_string); 661 } 662 } 663 return $fullText; 664 } 665 666 /** 667 * Get the number of messages in the current mailbox 668 * @return integer Number of messages 669 * @todo Rename to GetMessageCount()? 670 */ 671 public function num_msg() { 672 if( ! $this->is_horde() ) { 673 return imap_num_msg($this->conn); 674 } 675 else { 676 $count=0; 677 $status=array(); 678 try { 679 $status=$this->conn->status($this->folder,Horde_Imap_Client::STATUS_MESSAGES); 680 } catch(Horde_Imap_Client_Exception $e) { 681 $log_string='NOCC: getting number of messages from folder '.$this->folder.' failed'; 682 error_log($log_string); 683 syslog(LOG_INFO,$log_string); 684 } 685 $count=$status["messages"]; 686 return $count; 687 } 688 } 689 690 /** 691 * ... 692 * @param string $sort Sort criteria 693 * @param integer $sortdir Sort direction 694 * @return array Sorted message list 695 */ 696 public function sort($sort, $sortdir) { 697 if( ! $this->is_horde() ) { 698 switch($sort) { 699 case '1': $imapsort = SORTFROM; break; 700 case '2': $imapsort = SORTTO; break; 701 case '3': $imapsort = SORTSUBJECT; break; 702 case '4': $imapsort = SORTDATE; break; 703 case '5': $imapsort = SORTSIZE; break; 704 } 705 $sorted = imap_sort($this->conn, $imapsort, $sortdir, SE_NOPREFETCH); 706 if (!is_array($sorted)) { 707 throw new Exception('imap_sort() did not return an array.'); 708 } 709 } 710 else { 711 switch($sort) { 712 case '1': $imapsort = Horde_Imap_Client::SORT_FROM; break; 713 case '2': $imapsort = Horde_Imap_Client::SORT_TO; break; 714 case '3': $imapsort = Horde_Imap_Client::SORT_SUBJECT; break; 715 case '4': $imapsort = Horde_Imap_Client::SORT_DATE; break; 716 case '5': $imapsort = Horde_Imap_Client::SORT_SIZE; break; 717 } 718 $sort_array=array(); 719 if( $sortdir ) { 720 $sort_array[]=Horde_Imap_Client::SORT_REVERSE; 721 } 722 $sort_array[]=$imapsort; 723 try { 724 $options=array( 725 "sort" => $sort_array, 726 "sequence" => true, 727 ); 728 $result = $this->conn->search($this->folder, null, $options); 729 $sorted = $result["match"]->ids; 730 $options=array( 731 "sort" => $sort_array, 732 "sequence" => false, 733 ); 734 $result = $this->conn->search($this->folder, null, $options); 735 $sorted_uids = $result["match"]->ids; 736 $_SESSION['horde_sequence2uid']=array(); 737 for($i=0;$i<count($sorted);$i++) { 738 $_SESSION['horde_sequence2uid'][$sorted[$i]]=-1; 739 if( isset($sorted_uids[$i]) ) { 740 $_SESSION['horde_sequence2uid'][$sorted[$i]]=$sorted_uids[$i]; 741 } 742 } 743 } catch(Horde_Imap_Client_Exception $e) { 744 $log_string='NOCC: getting sequence numbers of messages from folder '.$this->folder.' failed'; 745 error_log($log_string); 746 syslog(LOG_INFO,$log_string); 747 } 748 } 749 return $sorted; 750 } 751 752 /** 753 * Get header info 754 * @param integer $msgnum Message number 755 * @param string $defaultcharset Default charset 756 * @return NOCC_HeaderInfo Header info 757 */ 758 public function headerinfo($msgnum, $defaultcharset = 'ISO-8859-1') { 759 $horde_flags=null; 760 if( ! $this->is_horde() ) { 761 $headerinfo = @imap_headerinfo($this->conn, $msgnum); 762 } 763 else { 764 try { 765 $query=new Horde_Imap_Client_Fetch_Query(); 766 $query->envelope(); 767 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 768 $options=array( 769 "ids" => $ids 770 ); 771 $headerinfo=$this->conn->fetch($this->folder,$query,$options); 772 $queryflags=new Horde_Imap_Client_Fetch_Query(); 773 $queryflags->flags(); 774 $horde_flags=$this->conn->fetch($this->folder,$queryflags,$options); 775 } 776 catch(Horde_Imap_Client_Exception $e) { 777 $log_string='NOCC: fetching headerinfo failed'; 778 error_log($log_string); 779 syslog(LOG_INFO,$log_string); 780 } 781 } 782 783 if (!is_object($headerinfo)) { 784 throw new Exception('imap_headerinfo() did not return an object.'); 785 } 786 return new NOCC_HeaderInfo($headerinfo, $defaultcharset, $horde_flags, $this->is_horde()); 787 } 788 789 /** 790 * Delete a mailbox 791 * @param string $mailbox Mailbox 792 * @return boolean Successful? 793 * @todo Rename to deleteMailbox()? 794 */ 795 public function deletemailbox($mailbox) { 796 if( ! $this->is_horde() ) { 797 return imap_deletemailbox($this->conn, '{' . $this->server . '}' . mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8')); 798 } 799 else { 800 try { 801 $this->conn->deleteMailbox($mailbox); 802 return true; 803 } 804 catch(Horde_Imap_Client_Exception $e) { 805 $log_string='NOCC: deleting mailbox '.$mailbox.' failed'; 806 error_log($log_string); 807 syslog(LOG_INFO,$log_string); 808 } 809 } 810 return false; 811 } 812 813 /** 814 * find specific email header from complete header 815 * @param string $head_search header to find 816 * @param string $temp_header find in headers 817 * @return string header content 818 * 819 * credits go to rklrkl, https://sourceforge.net/p/nocc/patches/149/, 2009-08-16 820 */ 821 function find_email_header($head_search,&$temp_header) { 822 // Look for "\n<header>: " in the header 823 $hpos = strpos($temp_header,"\n".$head_search.": "); 824 if ($hpos!=FALSE) { 825 // Now extract out the rest of the header line 826 $hpos += strlen("\n".$head_search.": "); 827 $hlen = strpos(substr($temp_header,$hpos),"\n"); 828 if ($hlen>0) { 829 $hstr = substr($temp_header,$hpos,$hlen); 830 if ($head_search=="Delivery-date" || $head_search=="Date") { 831 // Looking for e-mail date... 832 // Convert "normal" date format into bizarro mbox date format 833 // (which is expressed in local time, not GMT) 834 //return(date("D M d H:i:s Y",strtotime($hstr))); 835 return(strtotime($hstr)); 836 } 837 else { 838 // Looking for "from" e-mail address... 839 // Find out the first word which has an @ in it and return it 840 $harr = explode(" ",$hstr); 841 reset($harr); 842 foreach($harr as $eachword) { 843 // If we got an e-mail address, return it, but stripped of 844 // double quotes, <, >, ( and ) 845 if (strpos($eachword,"@")) { 846 return(trim($eachword,'"()<>')); 847 } 848 } 849 } 850 } 851 } 852 return ""; 853 } 854 855 /** 856 * Create a tmp file for downloading a complete mailbox folder 857 * @param string $download_box name of the folder to download 858 * 859 * 860 * credits go to rklrkl, https://sourceforge.net/p/nocc/patches/149/, 2009-08-16 861 */ 862 function downloadmailbox(&$download_box,&$ev) { 863 864 $_SESSION['fd_message']=array(); 865 866 global $conf; 867 868 // Create a sanitised mbox filename based on the folder name 869 $filename = preg_replace('/[\\/:\*\?"<>\|;]/','_',str_replace(' ',' ',$download_box)).".mbox"; 870 $_SESSION['fd_message'][]=$filename; 871 872 $remember_folder=$_SESSION['nocc_folder']; 873 $_SESSION['nocc_folder'] = $download_box; 874 875 $ev = ''; 876 $pop = new nocc_imap($ev); 877 if (NoccException::isException($ev)) { 878 $_SESSION['nocc_folder']=$remember_folder; 879 unset($_SESSION['fd_message']); 880 require ('./html/header.php'); 881 require ('./html/error.php'); 882 require ('./html/footer.php'); 883 return; 884 } 885 886 $memory_limit=ini_get('memory_limit'); 887 if( preg_match("/M$/i",$memory_limit) ) { 888 $memory_limit=intval($memory_limit)*1024*1024; 889 } 890 else if( preg_match("/K$/i",$memory_limit) ) { 891 $memory_limit=intval($memory_limit)*1024; 892 } 893 else if( preg_match("/G$/i",$memory_limit) ) { 894 $memory_limit=intval($memory_limit)*1024*1024*1024; 895 } 896 897 if( strlen($conf->tmpdir)==0 ) { 898 $_SESSION['nocc_folder']=$remember_folder; 899 unset($_SESSION['fd_message']); 900 $ev = new NoccException("tmp folder tmpdir is not set in config/php.conf."); 901 return; 902 } 903 else if( ! is_writable($conf->tmpdir) ) { 904 $_SESSION['nocc_folder']=$remember_folder; 905 unset($_SESSION['fd_message']); 906 $ev = new NoccException("tmp folder ".$conf->tmpdir." is not writeable."); 907 return; 908 } 909 910 $tmpFile=$_SESSION['sname']."_".md5(uniqid(rand(),true)).'.tmp'; 911 $_SESSION['fd_message'][]=$tmpFile; 912 $tmpFile=$conf->tmpdir.'/'.$tmpFile; 913 $_SESSION[$tmpFile]=1; 914 915 $mail_skipped=0; 916 if( $mbox=fopen($tmpFile,'w') ) { 917 // Find out how many messages are in the folder and loop for each one 918 $tot_msgs = $pop->num_msg(); 919 $_SESSION['fd_message'][]=$tot_msgs; 920 for ($mail = 1; $mail <= $tot_msgs; $mail++) { 921 // Prefix a line feed to the header so that later searches will 922 // find strings if they're right at the start of the header (first 923 // char in first line). Also strip any carriage returns that the 924 // IMAP server spits out. 925 926 $header_obj = $pop->fetchheader($mail); 927 $header=$header_obj->getHeader(); 928 $header = "\n" . str_replace("\r","",$header); 929 930 $headerinfo_obj=$pop->headerinfo($mail); 931 $subject=$headerinfo_obj->getSubject(); 932 933 // Find a "from" e-mail address in the headers 934 $from=$pop->find_email_header("From",$header); 935 if ($from=="") $from=$pop->find_email_header("Reply-To",$header); 936 if ($from=="") $from=$pop->find_email_header("X-From-Line",$header); 937 if ($from=="") $from="MAILER-DAEMON"; // Fallback if no From addr 938 939 // Find the date header and convert the date into mbox format 940 // Yes, Delivery-date: takes priority over Date:, which many 941 // mbox creation programs forget to take into account! 942 $date=$pop->find_email_header("Delivery-date",$header); 943 if ($date=="") $date=$pop->find_email_header("Date",$header); 944 if ($date=="") $date=0; // Time zero fallback 945 $showdate = format_date($date, $lang); 946 $date=date("D M d H:i:s Y",$date); 947 948 // Add the new "From " line, the rest of the header 949 // ...and the "raw" [but CR-stripped] body, but replace 950 // "\nFrom " with "\n>From " as well.(yes, 2 of the 4 crazily 951 // different mbox formats need this). Also append a blank line between 952 // message to separate them. 953 954 $mail_size=$pop->get_size($mail); 955 $memory_usage=memory_get_usage(); 956 957 if( 2*$mail_size+$memory_usage>$memory_limit ) { 958 $mail_skipped++; 959 $_SESSION['fd_message'][]='<tr><td style="text-align:left;">'.$showdate.'</td><td style="text-align:left;">'.$subject.'</td><td style="text-align:left;">'.$mail_size.'</td></tr>'; 960 } 961 else { 962 $body="\n".substr(str_replace("\n\nFrom ","\n\n>From ","\n\n".str_replace("\r","",$pop->fetchmessage($mail))."\n"),2); 963 fwrite($mbox,"From ".$from." ".$date.$body); 964 } 965 966 } 967 fwrite($mbox,"\n"); 968 fclose($mbox); 969 } 970 $pop->close(); 971 $_SESSION['nocc_folder']=$remember_folder; 972 973 if( is_file($tmpFile) ) { 974 $file_size=filesize($tmpFile); 975 $_SESSION['fd_message'][]=$file_size; 976 $_SESSION['fd_message'][]=$mail_skipped; 977 } 978 else { 979 unset($_SESSION['fd_message']); 980 $ev = new NoccException("folder download failed."); 981 return; 982 } 983 } 984 985 /** 986 * Download the tmp file for a complete mailbox folder 987 * 988 * credits go to rklrkl, https://sourceforge.net/p/nocc/patches/149/, 2009-08-16 989 */ 990 function downloadtmpfile(&$ev) { 991 global $conf; 992 if( isset($_SESSION['fd_tmpfile']) && is_array($_SESSION['fd_tmpfile']) && 993 isset($_SESSION['fd_tmpfile'][0]) && strlen($_SESSION['fd_tmpfile'][0])>0 && 994 isset($_SESSION['fd_tmpfile'][1]) && strlen($_SESSION['fd_tmpfile'][1])>0 ) 995 { 996 $tmpFile=$conf->tmpdir.'/'.basename($_SESSION['fd_tmpfile'][0]); 997 $filename=$_SESSION['fd_tmpfile'][1]; 998 if( is_file($tmpFile) ) { 999 $file_size=filesize($tmpFile); 1000 1001 // If no messages were found in the folder, don't offer the download 1002 // and simply fall into displaying the Folder page again. Maybe a warning 1003 // message should go here (JavaScripted "<foldername> folder contains 1004 // no messages")? 1005 //if ($file != "") { 1006 // This is a repeat of a large chunk of code fromm down_mail.php - 1007 // perhaps that should be put in a function somewhere and shared here 1008 // too? Would need to take $filename and $file as parameters. 1009 $isIE = $isIE6 = 0; 1010 1011 if (!isset($HTTP_USER_AGENT)) { 1012 $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT']; 1013 } 1014 1015 // Set correct http headers. 1016 // Thanks to Squirrelmail folks :-) 1017 if (strstr($HTTP_USER_AGENT, 'compatible; MSIE ') !== false && strstr($HTTP_USER_AGENT, 'Opera') === false) { 1018 $isIE = 1; 1019 } 1020 1021 if (strstr($HTTP_USER_AGENT, 'compatible; MSIE 6') !== false && strstr($HTTP_USER_AGENT, 'Opera') === false) { 1022 $isIE6 = 1; 1023 } 1024 1025 if ($isIE) { 1026 $filename=rawurlencode($filename); 1027 header ("Pragma: public"); 1028 header ("Cache-Control: no-store, max-age=0, no-cache, must-revalidate"); // HTTP/1.1 1029 header ("Cache-Control: post-check=0, pre-check=0", false); 1030 header ("Cache-Control: private"); 1031 1032 //set the inline header for IE, we'll add the attachment header later if we need it 1033 header ("Content-Disposition: inline; filename=$filename"); 1034 } 1035 1036 header ("Content-Type: application/octet-stream; name=\"$filename\""); 1037 header ("Content-Disposition: attachment; filename=\"$filename\""); 1038 1039 if ($isIE && !$isIE6) { 1040 header ("Content-Type: application/download; name=\"$filename\""); 1041 } 1042 else { 1043 header ("Content-Type: application/octet-stream; name=\"$filename\""); 1044 } 1045 header('Content-Length: '.$file_size); 1046 1047 $_SESSION[$tmpFile]=$_SESSION[$tmpFile]+1; 1048 1049 $chunksize = 1 * (1024 * 1024); // how many bytes per chunk 1050 if( $file_size > $chunksize ) { 1051 $handle = fopen($tmpFile, 'rb'); 1052 $buffer = ''; 1053 while (!feof($handle)) { 1054 $buffer = fread($handle, $chunksize); 1055 echo $buffer; 1056 ob_flush(); 1057 flush(); 1058 } 1059 fclose($handle); 1060 } else { 1061 readfile($tmpFile); 1062 } 1063 1064 exit; // Don't fall into HTML page - we're downloading and need to exit 1065 } 1066 else { 1067 unset($_SESSION['fd_tmpfile']); 1068 $ev = new NoccException("download file does not exits."); 1069 return; 1070 } 1071 } 1072 } 1073 1074 1075 /** 1076 * Rename a mailbox 1077 * @param string $oldMailbox Old mailbox 1078 * @param string $newMailbox New mailbox 1079 * @return boolean Successful? 1080 * @todo Rename to renameMailbox()? 1081 */ 1082 public function renamemailbox($oldMailbox, $newMailbox) { 1083 if( ! $this->is_horde() ) { 1084 return imap_renamemailbox($this->conn, '{' . $this->server . '}' . $oldMailbox, '{' . $this->server . '}' . $this->namespace . mb_convert_encoding($newMailbox, 'UTF7-IMAP', 'UTF-8')); 1085 } 1086 else { 1087 try { 1088 //$this->conn->renameMailbox(imap_mutf7_to_utf8($oldMailbox),$newMailbox); 1089 $this->conn->renameMailbox($oldMailbox,$newMailbox); 1090 return true; 1091 } 1092 catch(Horde_Imap_Client_Exception $e) { 1093 $log_string='NOCC: renaming mailbox '.$oldMailbox.' to '.$newMailbox. 'failed'; 1094 error_log($log_string); 1095 syslog(LOG_INFO,$log_string); 1096 } 1097 } 1098 } 1099 1100 /** 1101 * Create a mailbox 1102 * @param srtring $mailbox Mailbox 1103 * @return boolean Successful? 1104 * @todo Rename to createMailbox()? 1105 */ 1106 public function createmailbox($mailbox) { 1107 if( ! $this->is_horde() ) { 1108 return imap_createmailbox($this->conn, '{' . $this->server . '}' . $this->namespace . mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8')); 1109 } 1110 else { 1111 try { 1112 $this->conn->createMailbox($mailbox); 1113 return true; 1114 } 1115 catch(Horde_Imap_Client_Exception $e) { 1116 $log_string='NOCC: creating mailbox failed'; 1117 error_log($log_string); 1118 syslog(LOG_INFO,$log_string); 1119 return false; 1120 } 1121 } 1122 } 1123 1124 /** 1125 * Copy a mail to a mailbox 1126 * @param integer $msgnum Message number 1127 * @param string $mailbox Destination mailbox 1128 * @return boolean Successful? 1129 * @todo Rename to copyMail()? 1130 */ 1131 public function mail_copy($msgnum, $mailbox) { 1132 if( ! $this->is_horde() ) { 1133 return imap_mail_copy($this->conn, $msgnum, mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8'), 0); 1134 } 1135 else { 1136 try { 1137 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 1138 $options=array( 1139 "ids" => $ids 1140 ); 1141 $this->conn->copy($this->folder,$mailbox,$options); 1142 return true; 1143 } 1144 catch(Horde_Imap_Client_Exception $e) { 1145 $log_string='NOCC: copying mail failed'; 1146 error_log($log_string); 1147 syslog(LOG_INFO,$log_string); 1148 return false; 1149 } 1150 } 1151 } 1152 1153 /** 1154 * Subscribe to a mailbox 1155 * @param string $mailbox Mailbox 1156 * @param bool $isNewMailbox Is new mailbox? 1157 * @return bool Successful? 1158 * @todo Is $isNewMailbox really nedded? 1159 */ 1160 public function subscribe($mailbox, $isNewMailbox) { 1161 if( ! $this->is_horde() ) { 1162 if ($isNewMailbox) { 1163 return @imap_subscribe($this->conn, '{' . $this->server . '}' . $this->namespace . mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8')); 1164 } else { 1165 return @imap_subscribe($this->conn, '{' . $this->server . '}' . mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8')); 1166 } 1167 } 1168 else { 1169 try { 1170 $this->conn->subscribeMailbox($mailbox,true); 1171 return true; 1172 } 1173 catch(Horde_Imap_Client_Exception $e) { 1174 $log_string='NOCC: subscribing to mailbox failed'; 1175 error_log($log_string); 1176 syslog(LOG_INFO,$log_string); 1177 return false; 1178 } 1179 } 1180 } 1181 1182 /** 1183 * Unsubscribe from a mailbox 1184 * @param string $mailbox Mailbox 1185 * @return bool Successful? 1186 */ 1187 public function unsubscribe($mailbox) { 1188 if( ! $this->is_horde() ) { 1189 return @imap_unsubscribe($this->conn, '{' . $this->server . '}' . mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8')); 1190 } 1191 else { 1192 try { 1193 $this->conn->subscribeMailbox($mailbox,false); 1194 return true; 1195 } 1196 catch(Horde_Imap_Client_Exception $e) { 1197 $log_string='NOCC: unsubscribing from mailbox failed'; 1198 error_log($log_string); 1199 syslog(LOG_INFO,$log_string); 1200 return false; 1201 } 1202 } 1203 } 1204 1205 /** 1206 * Move a mail to a mailbox 1207 * @param integer $msgnum Message number 1208 * @param string $mailbox Destination mailbox 1209 * @return boolean Successful? 1210 * @todo Rename to moveMail()? 1211 */ 1212 public function mail_move($msgnum, $mailbox) { 1213 if( ! $this->is_horde() ) { 1214 return imap_mail_move($this->conn, $msgnum, mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8'), 0); 1215 } 1216 else { 1217 try { 1218 $ids=new Horde_Imap_Client_Ids(array($msgnum),true); 1219 $options=array( 1220 "ids" => $ids, 1221 "move" => true, 1222 ); 1223 $this->conn->copy($this->folder,$mailbox,$options); 1224 } catch(Horde_Imap_Client_Exception $e) { 1225 $log_string='NOCC: move mail to folder '.$mailbox.' failed'; 1226 error_log($log_string); 1227 syslog(LOG_INFO,$log_string); 1228 return false; 1229 } 1230 return true; 1231 } 1232 } 1233 1234 /** 1235 * Delete all messages marked for deletion 1236 * @return boolean Successful? 1237 */ 1238 public function expunge() { 1239 if( ! $this->is_horde() ) { 1240 return imap_expunge($this->conn); 1241 } 1242 else { 1243 try { 1244 $this->conn->expunge($this->folder); 1245 return true; 1246 } catch(Horde_Imap_Client_Exception $e) { 1247 $log_string='NOCC: expunge of folder '.$this->folder.' failed'; 1248 error_log($log_string); 1249 syslog(LOG_INFO,$log_string); 1250 } 1251 } 1252 } 1253 1254 /** 1255 * Delete a mail 1256 * @param integer $msgnum Message number 1257 * @return boolean Successful? 1258 * @todo Rename to deleteMail()? 1259 */ 1260 public function delete($msgnum) { 1261 if( ! $this->is_horde() ) { 1262 return imap_delete($this->conn, $msgnum, 0); 1263 } 1264 else { 1265 //only works with uids 1266 if( isset($_SESSION['horde_sequence2uid'][$msgnum]) && $_SESSION['horde_sequence2uid'][$msgnum]>=0 ) { 1267 try { 1268 $uid=$_SESSION['horde_sequence2uid'][$msgnum]; 1269 $ids=new Horde_Imap_Client_Ids(array($uid),false); 1270 $options=array( 1271 'ids' => $ids, 1272 'add' => array(Horde_Imap_Client::FLAG_DELETED), 1273 ); 1274 $this->conn->store($this->folder,$options); 1275 } catch(Horde_Imap_Client_Exception $e) { 1276 $log_string='NOCC: deleting message failed'; 1277 error_log($log_string); 1278 syslog(LOG_INFO,$log_string); 1279 } 1280 } 1281 return false; 1282 } 1283 return true; 1284 } 1285 1286 public function close() { 1287 if( ! $this->is_horde() ) { 1288 return imap_close($this->conn, CL_EXPUNGE); 1289 } 1290 else { 1291 try { 1292 $options = array( 1293 "expunge" => true, 1294 ); 1295 $this->conn->close($options); 1296 return; 1297 } catch(Horde_Imap_Client_Exception $e) { 1298 $log_string='NOCC: close failed'; 1299 error_log($log_string); 1300 syslog(LOG_INFO,$log_string); 1301 } 1302 } 1303 } 1304 1305 /** 1306 * ... 1307 * @return bool Is IMAP? 1308 * @todo Rename to isImap()? 1309 */ 1310 public function is_imap() { 1311 return $this->_isImap; 1312 } 1313 1314 /** 1315 * ... 1316 * @return bool Is IMAP? 1317 */ 1318 private function isImapCheck() { 1319 if( ! $this->is_horde() ) { 1320 //-------------------------------------------------------------------------------- 1321 // Check IMAP keywords... 1322 //-------------------------------------------------------------------------------- 1323 $keywords = array('/imap', '/service=imap', ':143'); 1324 foreach ($keywords as $keyword) { //for each IMAP keyword... 1325 if (stripos($this->server, $keyword) !== false) { 1326 return true; 1327 } 1328 } 1329 1330 //-------------------------------------------------------------------------------- 1331 // Check POP3 keywords... 1332 //-------------------------------------------------------------------------------- 1333 $keywords = array('/pop3', '/service=pop3', ':110'); 1334 foreach ($keywords as $keyword) { //for each POP3 keyword... 1335 if (stripos($this->server, $keyword) !== false) { 1336 return false; 1337 } 1338 } 1339 //-------------------------------------------------------------------------------- 1340 1341 //-------------------------------------------------------------------------------- 1342 // Check driver... 1343 //-------------------------------------------------------------------------------- 1344 $check = imap_check($this->conn); 1345 if ($check) { 1346 return ($check->{'Driver'} == 'imap'); 1347 } 1348 //-------------------------------------------------------------------------------- 1349 } 1350 else { 1351 return $this->_isImap; 1352 } 1353 1354 return false; 1355 } 1356 1357// public static function utf8($mime_encoded_text) { 1358// //TODO: Fixed in PHP 5.3.2! 1359// //Since PHP 5.2.5 returns imap_utf8() only capital letters! 1360// //See bug #44098 for details: http://bugs.php.net/44098 1361// if (version_compare(PHP_VERSION, '5.2.5', '>=')) { //if PHP 5.2.5 or newer... 1362// return nocc_imap::decode_mime_string($mime_encoded_text); 1363// } 1364// else { //if PHP 5.2.4 or older... 1365// return imap_utf8($mime_encoded_text); 1366// } 1367// } 1368// 1369// /** 1370// * Decode MIME string 1371// * @param string $string MIME encoded string 1372// * @param string $charset Charset 1373// * @return string Decoded string 1374// * @static 1375// */ 1376// public static function decode_mime_string($string, $charset = 'UTF-8') { 1377// $decodedString = ''; 1378// $elements = imap_mime_header_decode($string); 1379// foreach ($elements as $element) { //for all elements... 1380// if ($element->charset == 'default') { //if 'default' charset... 1381// $element->charset = mb_detect_encoding($element->text); 1382// } 1383// $decodedString .= mb_convert_encoding($element->text, $charset, $element->charset); 1384// } 1385// return $decodedString; 1386// } 1387 1388 /** 1389 * ... 1390 * @return array Mailboxes 1391 */ 1392 public function getmailboxes() { 1393 if( ! $this->is_horde() ) { 1394 $mailboxes = @imap_getmailboxes($this->conn, '{' . $this->server . '}', '*'); 1395 if (!is_array($mailboxes)) { 1396 throw new Exception('imap_getmailboxes() did not return an array.'); 1397 } else { 1398 sort($mailboxes); 1399 } 1400 return $mailboxes; 1401 } 1402 else { 1403 try { 1404 $mode = Horde_Imap_Client::MBOX_ALL; 1405 $options = array( 1406 "flat" => true, 1407 "sort" => true, 1408 ); 1409 $horde_mboxes=$this->conn->listMailboxes("*",$mode,$options); 1410 $allmailboxes=array(); 1411 foreach( $horde_mboxes as $mbox ) { 1412 $obj=new stdClass(); 1413 $obj->name="{".$this->server."}".$mbox->utf8; 1414 $allmailboxes[]=$obj; 1415 } 1416 return $allmailboxes; 1417 } catch(Horde_Imap_Client_Exception $e) { 1418 $log_string='NOCC: failed to get mailbox names'; 1419 error_log($log_string); 1420 syslog(LOG_INFO,$log_string); 1421 throw new Exception('imap_getmailboxes() did not return an array.'); 1422 } 1423 } 1424 } 1425 1426 /** 1427 * ... 1428 * @return array Mailboxes names 1429 * @todo Return UTF-8 names? 1430 */ 1431 public function getmailboxesnames() { 1432 try { 1433 $mailboxes = $this->getmailboxes(); 1434 $names = array(); 1435 foreach ($mailboxes as $mailbox) { //for all mailboxes... 1436 if( ! $this->is_horde() ) { 1437 $name = str_replace('{' . $this->server . '}', '', mb_convert_encoding($mailbox->name, 'UTF-8', 'UTF7-IMAP')); 1438 } 1439 else { 1440 $name = str_replace('{' . $this->server . '}', '', $mailbox->name); 1441 } 1442 //TODO: Why not add names with more the 32 chars? 1443 //if (strlen($name) <= 32) { 1444 array_push($names, $name); 1445 //} 1446 } 1447 return $names; 1448 } 1449 catch (Exception $ex) { 1450 return array(); 1451 } 1452 } 1453 1454 /** 1455 * ... 1456 * @return array Subscribed mailboxes 1457 * @todo Really throw an exception? 1458 */ 1459 public function getsubscribed() { 1460 if( ! $this->is_horde() ) { 1461 $subscribed = @imap_getsubscribed($this->conn, '{' . $this->server . '}', '*'); 1462 if (!is_array($subscribed)) { 1463 throw new Exception('imap_getsubscribed() did not return an array.'); 1464 } else { 1465 sort($subscribed); 1466 } 1467 } 1468 else { 1469 try { 1470 $mode = Horde_Imap_Client::MBOX_SUBSCRIBED; 1471 $options = array( 1472 "flat" => true, 1473 "sort" => true, 1474 ); 1475 $horde_mboxes=$this->conn->listMailboxes("*",$mode,$options); 1476 $subscribed=array(); 1477 foreach( $horde_mboxes as $mbox ) { 1478 $obj=new stdClass(); 1479 $obj->name="{".$this->server."}".$mbox->utf8; 1480 $subscribed[]=$obj; 1481 } 1482 } catch(Horde_Imap_Client_Exception $e) { 1483 $log_string='NOCC: list subscribed mailboxes failed'; 1484 error_log($log_string); 1485 syslog(LOG_INFO,$log_string); 1486 } 1487 } 1488 return $subscribed; 1489 } 1490 1491 /** 1492 * ... 1493 * @return array Subscribed mailboxes names 1494 * @todo Return UTF-8 names? 1495 */ 1496 public function getsubscribednames() { 1497 try { 1498 $subscribed = $this->getsubscribed(); 1499 1500 $names = array(); 1501 foreach ($subscribed as $mailbox) { //for all mailboxes... 1502 if( ! $this->is_horde() ) { 1503 $name = str_replace('{' . $this->server . '}', '', mb_convert_encoding($mailbox->name, 'UTF-8', 'UTF7-IMAP')); 1504 } 1505 else { 1506 $name = str_replace('{' . $this->server . '}', '', $mailbox->name); 1507 } 1508 if (!in_array($name, $names)) { 1509 array_push($names, $name); 1510 } 1511 } 1512 return $names; 1513 } 1514 catch (Exception $ex) { 1515 return array(); 1516 } 1517 } 1518 1519 /** 1520 * Mark mail as read 1521 * @param integer $msgnum Message number 1522 * @return boolean Successful? 1523 * @todo Rename to markMailRead()? 1524 */ 1525 public function mail_mark_read($msgnum) { 1526 if( ! $this->is_horde() ) { 1527 return imap_setflag_full($this->conn, $msgnum, '\\Seen'); 1528 } 1529 else { 1530 //only works with uids 1531 if( isset($_SESSION['horde_sequence2uid'][$msgnum]) && $_SESSION['horde_sequence2uid'][$msgnum]>=0 ) { 1532 try { 1533 $uid=$_SESSION['horde_sequence2uid'][$msgnum]; 1534 $ids=new Horde_Imap_Client_Ids(array($uid),false); 1535 $options=array( 1536 "ids" => $ids, 1537 "add" => array(Horde_Imap_Client::FLAG_SEEN), 1538 ); 1539 $this->conn->store($this->folder,$options); 1540 } catch(Horde_Imap_Client_Exception $e) { 1541 $log_string='NOCC: setting mail as read failed'; 1542 error_log($log_string); 1543 syslog(LOG_INFO,$log_string); 1544 } 1545 } 1546 } 1547 } 1548 1549 /** 1550 * Mark mail as unread 1551 * @param integer $msgnum Message number 1552 * @return boolean Successful? 1553 * @todo Rename to markMailUnread()? 1554 */ 1555 public function mail_mark_unread($msgnum) { 1556 if( ! $this->is_horde() ) { 1557 return imap_clearflag_full($this->conn, $msgnum, '\\Seen'); 1558 } 1559 else { 1560 //only works with uids 1561 if( isset($_SESSION['horde_sequence2uid'][$msgnum]) && $_SESSION['horde_sequence2uid'][$msgnum]>=0 ) { 1562 try { 1563 $uid=$_SESSION['horde_sequence2uid'][$msgnum]; 1564 $ids=new Horde_Imap_Client_Ids(array($uid),false); 1565 $options=array( 1566 "ids" => $ids, 1567 "remove" => array(Horde_Imap_Client::FLAG_SEEN), 1568 ); 1569 $this->conn->store($this->folder,$options); 1570 } catch(Horde_Imap_Client_Exception $e) { 1571 $log_string='NOCC: setting mail as read failed'; 1572 error_log($log_string); 1573 syslog(LOG_INFO,$log_string); 1574 } 1575 } 1576 } 1577 } 1578 1579 public function copytosentfolder($maildata, &$ev, $sent_folder_name) { 1580 if( ! $this->is_horde() ) { 1581 if (!(imap_append($this->conn, '{'.$this->server.'}'.$this->namespace.mb_convert_encoding($sent_folder_name, 'UTF7-IMAP', 'UTF-8'), $maildata, "\\Seen"))) { 1582 $ev = new NoccException("could not copy mail into $sent_folder_name folder: ".$this->last_error()); 1583 return false; 1584 } 1585 } 1586 else { 1587 try { 1588 $data=array( 1589 array( 1590 "data" => $maildata, 1591 "flags" => array(Horde_Imap_Client::FLAG_SEEN), 1592 ), 1593 ); 1594 $ids=$this->conn->append($sent_folder_name,$data); 1595 if( $ids->isEmpty() ) { 1596 $ev = new NoccException("could not copy mail into $sent_folder_name folder, ids empty"); 1597 return false; 1598 } 1599 } catch(Horde_Imap_Client_Exception $e) { 1600 $ev = new NoccException("could not copy mail into $sent_folder_name folder"); 1601 return false; 1602 } 1603 } 1604 return true; 1605 } 1606 1607// /* 1608// * These functions are static, but if we could re-implement them without 1609// * requiring PHP IMAP support, more people can use NOCC. 1610// */ 1611// public static function base64($file) { 1612// return imap_base64($file); 1613// } 1614// 1615// public static function i8bit($file) { 1616// return imap_8bit($file); 1617// } 1618// 1619// public static function qprint($file) { 1620// return imap_qprint($file); 1621// } 1622 1623 /** 1624 * Decode BASE64 or QUOTED-PRINTABLE data 1625 * @param string $data Encoded data 1626 * @param string $transfer BASE64 or QUOTED-PRINTABLE? 1627 * @return string Decoded data 1628 * TODO: Better name? 1629 */ 1630 public static function decode($data, $transfer) { 1631 if ($transfer == 'BASE64') { //if BASE64... 1632 $data=mb_convert_encoding($data,"UTF-8","BASE64"); 1633 return $data; 1634 } 1635 elseif ($transfer == 'QUOTED-PRINTABLE') { //if QUOTED-PRINTABLE... 1636 return quoted_printable_decode($data); 1637 } 1638 1639 return $data; 1640 } 1641 1642 1643 public static function mime_header_decode($header,$decode=true,$ishorde=false) { 1644 if( ! $ishorde ) { 1645 $source=imap_mime_header_decode($header); 1646 for ($j = 0; $j < count($source); $j++ ) { 1647 $element_charset=($source[$j]->charset == 'default') ? detect_charset($source[$j]->text) : $source[$j]->charset; 1648 if ($element_charset == '' || $element_charset == null) { 1649 if (isset($conf->default_charset) && $conf->default_charset != '') { 1650 $element_charset = $conf->default_charset; 1651 } 1652 else { 1653 $element_charset = 'ISO-8859-1'; 1654 } 1655 } 1656 if( $decode ) { 1657 //$element_converted = os_iconv($element_charset, 'UTF-8', $source[$j]->text); 1658 $element_converted = mb_convert_encoding($source[$j]->text, 'UTF-8', $element_charset); 1659 } 1660 else { 1661 $element_converted = $source[$j]->text; 1662 } 1663 $decodedheader=$decodedheader.$element_converted; 1664 } 1665 } 1666 else { 1667 $charset_all=""; 1668 $tail=$header; 1669 $matches=array(); 1670 while( preg_match("/^(.*)(=\?.*\?.*\?=)(.*)$/U",$tail,$matches) ) { 1671 $decodedheader=$decodedheader.trim($matches[1]); 1672 $mime_encoded=$matches[2]; 1673 $tail=trim($matches[3]); 1674 $encoding=""; 1675 $charset=""; 1676 $matches=array(); 1677 if( preg_match("/^=\?(.*)\?.*\?=/U",$mime_encoded,$matches) ) { 1678 $charset=mb_strtoupper($matches[1]); 1679 } 1680 $matches=array(); 1681 if( preg_match("/^=\?.*\?([pq])\?.*\?=/iU",$mime_encoded,$matches) ) { 1682 $encoding=mb_strtoupper($matches[1]); 1683 } 1684 if( $charset == '' || $charset == null) { 1685 $charset='ISO-8859-1'; 1686 } 1687 $charset_all=$charset; 1688 $mime_decoded=mb_convert_encoding(mb_decode_mimeheader($mime_encoded),$charset); 1689 if( $encoding == "Q" ) { 1690 $mime_decoded=preg_replace("/_/"," ",$mime_decoded); 1691 } 1692 $decodedheader=$decodedheader.$mime_decoded; 1693 $matches=array(); 1694 } 1695 $decodedheader=$decodedheader.$tail; 1696 1697 if( $decode ) { 1698 if( $charset_all == '' || $charset_all == null) { 1699 $charset_all='ISO-8859-1'; 1700 } 1701 $decodedheader=mb_convert_encoding($decodedheader,"UTF-8",$charset_all); 1702 } 1703 } 1704 return $decodedheader; 1705 } 1706 1707 1708// public static function mime_header_decode($header, $decode=true) { 1709// $source = imap_mime_header_decode($header); 1710// $result[] = new result; 1711// $result[0]->text=''; 1712// $result[0]->charset='ISO-8859-1'; 1713// for ($j = 0; $j < count($source); $j++ ) { 1714// $element_charset = ($source[$j]->charset == 'default') ? detect_charset($source[$j]->text) : $source[$j]->charset; 1715// if ($element_charset == '' || $element_charset == null) { 1716// if (isset($conf->default_charset) && $conf->default_charset != '') { 1717// $element_charset = $conf->default_charset; 1718// } 1719// else { 1720// $element_charset = 'ISO-8859-1'; 1721// } 1722// } 1723// if( $decode ) { 1724// $element_converted = os_iconv($element_charset, 'UTF-8', $source[$j]->text); 1725// } 1726// else { 1727// $element_converted = $source[$j]->text; 1728// } 1729// $result[$j] = new stdClass(); 1730// $result[$j]->text = $element_converted; 1731// $result[$j]->charset = 'UTF-8'; 1732// } 1733// return $result; 1734// } 1735 1736 /* 1737 * These are general utility functions that extend the imap interface. 1738 */ 1739 public function html_folder_select($value, $selected = '') { 1740 $folders = $this->getsubscribednames(); 1741 if (!is_array($folders) || count($folders) < 1) { 1742 return "<p class=\"error\">Not currently subscribed to any mailboxes</p>"; 1743 } 1744 reset($folders); 1745 1746 $html_select = "<select class=\"button\" id=\"$value\" name=\"$value\">\n"; 1747 foreach ($folders as $folder) { 1748 $html_select .= "\t<option ".($folder == $selected ? "selected=\"selected\"" : "")." value=\"$folder\">".$folder."</option>\n"; 1749 } 1750 $html_select .= "</select>\n"; 1751 return $html_select; 1752 } 1753 1754 public function get_folder_count() { 1755 try { 1756 return count($this->getsubscribed()); 1757 } 1758 catch (Exception $ex) { 1759 return 0; 1760 } 1761 } 1762 1763 /** 1764 * ... 1765 * @param int $num_messages Number of messages 1766 * @return int Page count 1767 */ 1768 public function get_page_count($num_messages) { 1769 if (!is_int($num_messages)) { //if NO integer... 1770 return 0; 1771 } 1772 if ($num_messages == 0) { //if 0 messages... 1773 return 0; 1774 } 1775 return ceil($num_messages / get_per_page()); 1776 } 1777 1778 /** 1779 * Retrieve the quota settings 1780 * @param string $quotaRoot Quota root (mailbox) 1781 * @return array Quota settings 1782 */ 1783 public function get_quota_usage($quotaRoot) { 1784 if( ! $this->is_horde() ) { 1785 return @imap_get_quotaroot($this->conn, mb_convert_encoding($quotaRoot, 'UTF7-IMAP', 'UTF-8')); 1786 } 1787 else { 1788 try { 1789 $quota=$this->conn->getQuotaRoot($quotaRoot); 1790 return $quota; 1791 } catch(Horde_Imap_Client_Exception $e) { 1792 $log_string='NOCC: getting quotaroot failed'; 1793 error_log($log_string); 1794 syslog(LOG_INFO,$log_string); 1795 } 1796 } 1797 return false; 1798 } 1799 1800 /** 1801 * Return status information from a mailbox 1802 * @param string $mailbox Mailbox 1803 * @return object Status information 1804 */ 1805 public function status($mailbox) { 1806 if( ! $this->is_horde() ) { 1807 $status_obj=imap_status($this->conn, mb_convert_encoding($mailbox, 'UTF7-IMAP', 'UTF-8'), SA_ALL); 1808 $status=array(); 1809 $status['unseen']=$status_obj->unseen; 1810 return $status; 1811 } 1812 else { 1813 $status=array(); 1814 try { 1815 $mailbox=str_replace('{' . $this->server . '}', '',$mailbox); 1816 $status=$this->conn->status($mailbox,Horde_Imap_Client::STATUS_ALL); 1817 } catch(Horde_Imap_Client_Exception $e) { 1818 $log_string='NOCC: getting status failed'; 1819 error_log($log_string); 1820 syslog(LOG_INFO,$log_string); 1821 } 1822 return $status; 1823 } 1824 } 1825 1826 /** 1827 * Check if we should use Horde/Imap_Client 1828 * @param 1829 * @return bool true if Horde/Imap_Client should be used, default is false 1830 */ 1831 public function is_horde() { 1832 global $conf; 1833 $r=false; 1834 if( isset($conf->horde_imap_client) && $conf->horde_imap_client ) { 1835 $r=true; 1836 } 1837 if( isset($conf->domains[$_SESSION['nocc_domainnum']]->horde_imap_client) ) { 1838 if( $conf->domains[$_SESSION['nocc_domainnum']]->horde_imap_client ) { 1839 $r=true; 1840 } 1841 else { 1842 $r=false; 1843 } 1844 } 1845 if( isset($_SESSION['is_horde']) ) { 1846 return $_SESSION['is_horde']; 1847 } 1848 return $r; 1849 } 1850 1851} 1852 1853 1854 1855