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('&nbsp;',' ',$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