<?
if(VALID_DOCUMENT != 1) die('what?');
define('DELETED_FLAG','\Deleted');
define('ANSWERED_FLAG','\Answered');
define('SEEN_FLAG','\Seen');
define(SORT_BY_DATE,'date');
define(SORT_BY_SIZE,'size');
define(SORT_BY_FROM,'from');
define(SORT_BY_TO,'to');
define(SORT_BY_SUBJECT,'subject');
define(SORT_BY_NUMBER,'number');
function _header_sort_by_date(&$h1,&$h2){
return $h2['date'] - $h1['date'];
}
function _header_sort_by_size(&$h1,&$h2){
return $h2['size'] - $h1['size'];
}
function _header_sort_by_subject(&$h1,&$h2){
return strcmp($h1['subject'],$h2['subject']);
}
function _header_sort_by_from(&$h1,&$h2){
return strcmp($h1['from'][0]['personal'].$h1['from'][0]['mailbox'].$h1['from'][0]['host'],
$h2['from'][0]['personal'].$h2['from'][0]['mailbox'].$h2['from'][0]['host']);
}
function _header_sort_by_to(&$h1,&$h2){
return strcmp($h1['to'][0]['personal'].$h1['to'][0]['mailbox'].$h1['to'][0]['host'],
$h2['to'][0]['personal'].$h2['to'][0]['mailbox'].$h2['to'][0]['host']);
}
function mime_header_decode($str){
$d = imap_string_decode(trim($str));
return $d['decoded'];
}
function imap_string_decode($str){
$res = $str;
$chr='default';
// hack against segmentation fault
if(eregi("=[ ]?\?.+\?[ ]?=",$str) && eregi("[ ]",$str))
$str = str_replace(' ','',$str);
if(eregi("=\?[^ ]+\?=",$str)){
$arr = imap_mime_header_decode($str);
if(is_array($arr)){
$res = '';
foreach($arr as $a){
$res .= $a->text;
if($a->charset != 'default') $chr = $a->charset;
}
}
}
if($chr != 'default' && $chr != $GLOBALS['DEFAULT_CHARSET']){
$res = to_default_charset($res,$chr);
$res = str_replace("\x1a",'',$res);
}
return array('decoded' => chop($res),'charset' => $chr);
}
$__one_imap = false;
$__one_nntp = false;
function getIMAP(){
global $__one_imap;
if(!$__one_imap)
$__one_imap = new IMAP($GLOBALS['MAIL_USER_NAME'],$GLOBALS['MAIL_USER_PASSWORD']);
return $__one_imap;
}
function getNNTP(){
global $__one_nntp;
if(!$__one_nntp)
$__one_nntp = new NNTP;
return $__one_nntp;
}
function closeMP(){
global $__one_imap,$__one_nntp;
if($__one_imap)
$__one_imap->close();
$__one_imap=false;
if($__one_nntp)
$__one_nntp->close();
$__one_nntp=false;
}
//////////////////////////////////////
// Knows to parse RFC-822 mails
/////////////////////////////////////
class MailParser{
function MailParser(){ }
// separate header and body
function separate_header_body($whole_mail){
$NL = "\r\n";
$nl_pos = strpos($whole_mail,$NL.$NL);
if($nl_pos === false){
$NL = "\n";
$nl_pos = strpos($whole_mail,$NL.$NL);
}
if($nl_pos > 0){
$_header = substr($whole_mail,0,$nl_pos);
$_body = substr($whole_mail,$nl_pos+strlen($NL));
}
else{
$_header = $whole_mail;
$_body = '';
}
return array($_header,$_body);
}
// parse whole mail
// if not in mail format, returns false
function parse_mail($whole_mail){
list($_header,$_body) = $this->separate_header_body($whole_mail);
$header = $this->parse_header($_header);
if(array_not_empty($header)){
return $this->parse_mail_body($header,$_body);
}
else
return false;
}
// getting header value
function get_header_value($header,&$line,&$res){
$p = stripos($line,$header.':');
if($p === 0){
$res = substr($line,strlen($header)+1);
return true;
}
return false;
}
// parse header
function parse_header($_header){
$lines = explode("\n",$_header);
$header = array();
$last = false;
$res = null;
foreach($lines as $line){
if($this->get_header_value('Date',$line,$res)){
$res = trim($res);
$header['date'] = strtotime($res);
// workaound for baggy dates , like 'Mon, 26 Jun 2006 22:57:49 0800' (without +/- in timezone)
if($header['date'] <= 0 && preg_match('/[^+-]\d{2,4}$/',$res)){
$header['date'] = strtotime(preg_replace('/(\d+)$/','+$1',$res));
}
else if($header['date'] <= 0 && preg_match('/UT$/',$res)){
$header['date'] = strtotime(preg_replace('/UT$/','',$res));
}
$last = 'date';
}
else if($this->get_header_value('Content-Type',$line,$res)){
$last = 'content-type';
$header[$last] = $line;
}
else if($this->get_header_value('Content-Id',$line,$res)){
$last = 'content-id';
$header[$last] = $res;
}
else if($this->get_header_value('Content-Transfer-Encoding',$line,$res)){
$last = 'encoding';
$header[$last] = strtolower($res);
}
else if($this->get_header_value('Content-Disposition',$line,$res)){
$last = 'content-disposition';
$header[$last] = trim($res);
}
else if($this->get_header_value('Subject',$line,$res)){
$last = 'subject';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('From',$line,$res)){
$last = 'fromaddress';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('To',$line,$res)){
$last = 'toaddress';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('Cc',$line,$res)){
$last = 'ccaddress';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('Bcc',$line,$res)){
$last = 'bccaddress';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('Newsgroups',$line,$res)){
$last = 'newsgroups';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('Disposition-Notification-To',$line,$res)){
$last = 'notification';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('Message-Id',$line,$res)){
$last = 'message-id';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('In-Reply-To',$line,$res)){
$last = 'in-reply-to';
$header[$last] = ltrim($res);
}
else if($this->get_header_value('Priority',$line,$res)){
$last = 'priority';
$header[$last] = ltrim($res);
}
else if(strpos($line," ") === 0 || strpos($line,"\t") === 0){
if($last)
$header[$last] .= ltrim($line);
}
else
$last = false;
}
$header['all'] = $_header;
if(isset($header['toaddress']))
$header['to'] = $this->parse_emails($header['toaddress']);
if(isset($header['fromaddress']))
$header['from'] = $this->parse_emails($header['fromaddress']);
if(isset($header['ccaddress']))
$header['cc'] = $this->parse_emails($header['ccaddress']);
if(isset($header['bccaddress']))
$header['bcc'] = $this->parse_emails($header['bccaddress']);
if(isset($header['newsgroups']))
$header['newsgroups'] = $header['newsgroups'];
if(isset($header['content-type']))
$header['content-type'] = $this->parse_content_type($header['content-type']);
if(isset($header['content-id']))
$header['content-id'] = trim(eregi_replace('[<>]','',$header['content-id']));
if(isset($header['content-disposition']))
$header['content-disposition'] = $this->parse_content_disposition($header['content-disposition']);
if(preg_match('/<([^>]+)>/',$header['message-id'],$m))
$header['message-id'] = trim($m[1]);
if(preg_match('/<([^>]+)>/',$header['in-reply-to'],$m))
$header['in-reply-to'] = trim($m[1]);
if(isset($header['priority']))
$header['priority'] = trim($header['priority']);
if(isset($header['subject'])){
$arr = imap_string_decode($header['subject']);
$header['subject-decoded'] = $arr['decoded'];
$header['subject-charset'] = $arr['charset'];
}
// if anly 'all' index defined
if(count($header) <= 1) $header = array();
return $header;
}
// parse mail body
function parse_mail_body($header,$_mail){
if(count($header) <= 0) return array();
$mail = array('header'=>$header);
// mutipart
if(isset($header['content-type']['boundary']) &&
$header['content-type']['boundary'] != ''){
$boundary = $header['content-type']['boundary'];
// will be prepared
$_prepared_mail = $_mail;
// solve non-newline startings
if($_prepared_mail[0] != "\r" && $_prepared_mail[0] != "\n")
$_prepared_mail = "\r\n".$_prepared_mail;
// array of parts strings
$_parts = array();
//devide to parts
if(strpos($_prepared_mail,"\r\n--".$boundary."\r\n") !== false){
// replace boundary end to nothing
$_prepared_mail = str_replace("--".$boundary."--\r\n",'',$_prepared_mail);
$_parts = explode("\r\n--".$boundary."\r\n",$_prepared_mail);
}
else if(strpos($_prepared_mail,"\n--".$boundary."\n") !== false){
// replace boundary end to nothing
$_prepared_mail = str_replace("--".$boundary."--\n",'',$_prepared_mail);
$_parts = explode("\n--".$boundary."\n",$_prepared_mail);
}
else{
debug($_prepared_mail);
debug("BAD BOUNDARY: ".$boundary);
}
// aggregate parts
$parts = array();
$part_index = 1;
$was_first = false;
$parts = array();
foreach($_parts as $_part){
// first is body
if(!$was_first){
$mail['body'] = $_part;
$was_first = true;
}
else{
// recursive parsing
list($_part_header,$_part_mail) = $this->separate_header_body($_part);
$part_header = $this->parse_header($_part_header);
// if rfc-822 header: encapsulated email
if($part_header['content-type']['mime-type'] === 'message/rfc822'){
list($_part_header2,$_part_mail2) = $this->separate_header_body($_part_mail);
$part_header2 = $this->parse_header($_part_header2);
// save headers as differetnt part
$part_header['content-disposition']['attachment'] = true;
$part_header['content-disposition']['filename'] = 'RFC-822 Message Headers';
$parts[$part_index++] = array('header'=>$part_header,'body'=>$_part_header2);
// parse recursive
$parts[$part_index++] = $this->parse_mail_body($part_header2,$_part_mail2);
}
else
$parts[$part_index++] = $this->parse_mail_body($part_header,$_part_mail);
}
}
if(array_not_empty($parts))
$mail['parts'] = $parts;
}
else
$mail['body'] = $_mail;
// take source
$mail['source'] = $header['all']."\r\n".$_mail;
return $mail;
}
// parse emails
function parse_emails($emails_str){
$emails = array();
$emails_str = preg_replace('/"([^"]+)"/e',
"'\"'.str_replace(',',' ','$1').'\"'",
$emails_str);
$ems = preg_split('/,/',$emails_str);
// parsing for weird case with , in personal
// like: "me,myself" <hide@address.com>
$new_ems = array();
$miss_one = false;
foreach($ems as $e){
if($miss_one){
$take .= $e;
$miss_one = false;
$new_ems[] = $take;
}
else if(!preg_match('/".*"/',$e) && eregi('"',$e)){
$take = $e;
$miss_one = true;
}
else
$new_ems[] = $e;
}
foreach($new_ems as $e){
$e = trim($e);
if($e && $e != ''){
$email = array();
if(preg_match('/"([^"]+)"\s*<(.*)@(.*)>/',$e,$m) ||
preg_match('/(.*)\s*<(.*)@(.*)>/',$e,$m)){
$email['personal'] = trim($m[1]);
$email['mailbox'] = trim($m[2]);
$email['host'] = trim($m[3]);
}
else if(preg_match('/"([^"]*)"\s*<(.*)>/',$e,$m) ||
preg_match('/(.*)\s*<(.*)>/',$e,$m)){
$email['personal'] = trim($m[1]);
$email['mailbox'] = trim($m[2]);
$email['host'] = 'UNKNOWN_HOST';
}
else if(preg_match("/<(.*)@(.*)>/",$e,$m)){
$email['mailbox'] = trim($m[1]);
$email['host'] = trim($m[2]);
}
else if(preg_match("/(.*)@(.*)/",$e,$m)){
$email['mailbox'] = trim($m[1]);
$email['host'] = trim($m[2]);
}
else{
$email['personal'] = $e;
$email['mailbox'] = '';//'UNKNOWN_MAILBOX';
$email['host'] = '';//'UNKNOWN_HOST';
}
if($email['personal']){
$arr = imap_string_decode($email['personal']);
$email['personal-decoded'] = $arr['decoded'];
$email['personal-charset'] = $arr['charset'];
}
$email['email'] = $email['mailbox'].'@'.$email['host'];
$emails[] = $email;
}
}
return $emails;
}
// parse content-type
function parse_content_type($ct){
$res = array();
if(preg_match('/^Content-Type:\s*([^;]+)\/([^;]+);?/i',$ct,$m)){
// mime type
$res['mime-type'] = strtolower(trim($m[1])).'/'.strtolower(trim($m[2]));
// charset
if(preg_match('/charset="?([^";]*)"?/i',$ct,$m))
$res['charset'] = strtolower(trim($m[1]));
// name
if(preg_match('/name="?([^";]*)"?/i',$ct,$m))
$res['name'] = trim($m[1]);
// boundary
if(preg_match('/boundary="?([^";]*)"?/i',$ct,$m))
$res['boundary'] = trim($m[1]);
}
else if(preg_match('/Content-Type:\s*(.*)\/(.*)/i',$ct,$m))
$res['mime-type'] = strtolower(trim($m[1])).'/'.strtolower(trim($m[2]));
return $res;
}
// parse content-disposition
function parse_content_disposition($cd){
$res = array();
// if attachment
if(preg_match('/attachment;?/i',$cd,$m))
$res['attachment'] = true;
// filename
if(preg_match('/filename="?([^"]*)"?;?/i',$cd,$m))
$res['filename'] = $m[1];
return $res;
}
}
//////////////////////////////////////
// Abstract Mail Protocol, knows to parse
// RFC-822 mails
/////////////////////////////////////
class MailProtocol{
var $sock;
var $parser;
function MailProtocol($host,$port){
$this->sock = @fsockopen($host,$port,$errno,$errstr);
if(!$this->sock){
throw new Exception("Connection to [$host:$port] failed.<br/>[$errno] $errstr");
}
$this->parser = new MailParser();
}
//
// must be implemented
//
function all_headers($name){
return array();
}
function range_headers($name,$from,$count){
return array();
}
function get_header($name,$key){
return array();
}
function get_mail($name,$key){
return array();
}
function listing($wildcard){
return array();
}
function status(){
return array();
}
// returns sorted headers
function sorted_headers($mbox,$sort_order=SORT_BY_NUMBER,$from=-1,$count=-1){
if($from > 0 && $count >= 0){
$headers = $this->range_headers($mbox,$from,$count);
}
else{
$headers = $this->all_headers($mbox);
}
$func = false;
if($sort_order == SORT_BY_DATE)
$func = '_header_sort_by_date';
else if($sort_order == SORT_BY_SIZE)
$func = '_header_sort_by_size';
else if($sort_order == SORT_BY_SUBJECT)
$func = '_header_sort_by_subject';
else if($sort_order == SORT_BY_FROM)
$func = '_header_sort_by_from';
else if($sort_order == SORT_BY_TO)
$func = '_header_sort_by_to';
if($func)
usort($headers,$func);
else
$headers = array_reverse($headers);
return $headers;
}
// parse header
function parse_header($_header){
return $this->parser->parse_header($_header);
}
// parse mail body
function parse_mail_body($header,$_mail){
return $this->parser->parse_mail_body($header,$_mail);
}
function close(){
fclose($this->sock);
}
}
/////////////////////////////////
// IMAP protocol implementation
////////////////////////////////
class IMAP extends MailProtocol{
var $num;
function IMAP($user,$password){
parent::MailProtocol(IMAPHOST,IMAPPORT);
$this->num = 0;
$this->login($user,$password);
}
function close(){
$this->logout();
parent::close();
}
function login($user,$password){
// $password = str_replace('\\','\\\\',str_replace('"','\"',$password));
$password = str_replace('"','\"',str_replace('\\','\\\\',$password));
$data = $this->exec('LOGIN '.$user.' "'.$password.'"');
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw_error('login error',
"$user's password don't match.",
$data['last']/*."\n".'LOGIN '.$user.' "'.$password.'"'*/);
}
}
function logout(){
$data = $this->exec('LOGOUT');
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception('Logout error<br/>'.$data['last']);
}
}
// list of mailboxes
function listing($wildcard){
$data = $this->exec("LIST \"\" \"$wildcard\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception('IMAP::listing '."can't open list for $wildcard.<br/>\n".$data['last']);
}
$lines = preg_split("/\n/",$data['data']);
$mboxes = array();
foreach($lines as $line){
if(eregi("^\* list \((.*)\) \".*\" (.*)",$line,$m))
if(!eregi('\\NoSelect',$m[1])){
$mb = chop($m[2]);
if(eregi("\"(.*)\"",$mb,$m))
$mb = $m[1];
// special file cases
if(!preg_match('/\.msf$/',$mb))
$mboxes[] = $mb;
}
}
return $mboxes;
}
// status of mailbox
function status($mbox){
$data = $this->exec("STATUS \"$mbox\" (MESSAGES RECENT UNSEEN)");
if(preg_match('/^'.$data['id'].' (no|bad)/i',$data['last'])){
throw new Exception("IMAP::status <b>$mbox</b> isn't a valid mbox file.");
return null;
}
$status = array();
if(preg_match('/\* .* \(messages ([0-9]*) recent ([0-9]*) unseen ([0-9]*)\)/i',$data['data'],$m)){
$status['messages'] = $m[1];
$status['recent'] = $m[2];
$status['unseen'] = $m[3];
}
return $status;
}
// select mbox and get information
function select($mbox){
$data = $this->exec("SELECT \"$mbox\"");
if(eregi('^'.$data['id'].' no',$data['last'])){
throw new Exception('IMAP::select '."can't open $mbox.<br/>".$data['last']);
}
$num = 0;
if(eregi("\* ([0-9]*) exists",$data['data'],$m)){
$num = $m[1];
}
return $num;
}
// create new mbox
function create($mbox){
$data = $this->exec("CREATE \"$mbox\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception("IMAP::create : can't create ".$mbox."<br/>\n".
$data['last']);
}
}
// delete mbox
function delete($mbox){
$data = $this->exec("DELETE \"$mbox\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
return false;
// throw new Exception('IMAP::delete '."can't delete $mbox.<br/>\n".$data['last']);
}
return true;
}
// rename mbox
function rename($mbox,$newname){
$data = $this->exec("RENAME \"$mbox\" \"$newname\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception('IMAP::rename '."can't rename $mbox to $name.<br/>\n".$data['last']);
}
}
// expunge
function expunge($mbox){
$data = $this->exec("EXPUNGE");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception('IMAP::expunge '."can't expunge $mbox.<br/>\n".$data['last']);
}
}
// set flag
function store_flag($mbox,$key,$flag,$set){
$data = $this->exec("STORE $key ".($set?'+':'-')."FLAGS ($flag)");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception('IMAP::store_flag '."can't set flags to $mbox:$key.<br/>".$data['data']);
}
}
function set_flag($mbox,$key,$flag){
$this->select($mbox);
$this->store_flag($mbox,$key,$flag,true);
}
// delete messages
function delete_messages($mbox,$keys){
if(!is_array($keys) && !is_numeric($keys))
return;
if(is_numeric($keys))
$keys = array($keys);
$this->select($mbox);
foreach($keys as $k){
$this->store_flag($mbox,$k,DELETED_FLAG,true);
}
$this->expunge($mbox);
}
// move messages
function move_messages($mbox,$newmbox,$keys){
if(!is_array($keys) && !is_numeric($keys))
return;
if(is_numeric($keys))
$keys = array($keys);
$this->select($mbox);
foreach($keys as $k){
$data = $this->exec("COPY $k \"$newmbox\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception("IMAP::move_messages can't move $mbox:$k to $newmbox.<br/>".$data['last']);
}
$this->store_flag($mbox,$k,DELETED_FLAG,true);
}
$this->expunge($mbox);
}
// copy messages
function copy_messages($mbox,$newmbox,$keys){
if(!is_array($keys) && !is_numeric($keys))
return;
if(is_numeric($keys))
$keys = array($keys);
$this->select($mbox);
foreach($keys as $k){
$this->store_flag($mbox,$k,DELETED_FLAG,false);
$data = $this->exec("COPY $k \"$newmbox\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last'])){
throw new Exception("IMAP::copy_messages can't copy $mbox:$k to $newmbox.<br/>".$data['last']);
}
}
}
// append
function append($mbox,$message){
// $message = preg_replace("/([^\\r])\\n/","\\1\r\n",$message);
$message = str_replace("\r",'',$message);
$len = strlen($message);
$id = $this->make_next_id();
// write command
fputs($this->sock,$id.' APPEND "'.$mbox.'" {'.$len.'}'."\n");
// read feedback
$s = fgets($this->sock,2048);
if(eregi("^$id (no|bad)",$s)){
throw new Exception('IMAP::append '."can't append to $mbox.<br/>".$s);
}
// write message
$bytes = fputs($this->sock,$message);
fputs($this->sock,"\n");
// read feedback
while($s = fgets($this->sock,2048)){
if(eregi("^$id ",$s))
break;
}
}
// fetch headers
// optimized !!!
function fetch_headers($from,$to=-1){
if($to < 0) $to = $from;
// next id
$id = $this->make_next_id();
// write comamnd
fputs($this->sock,"$id FETCH $from:$to (FLAGS INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER])\n");
$s = fgets($this->sock,2048);
if(eregi("^$id (no|bad)",$s)){
throw new Exception("IMAP::fetch_headers can't fetch $from:$to.<br/>".$s);
}
$headers = array();
$_header = ''; $header = false; $size = 0; $readen = 0;
do{
$chop = chop($s);
if(strpos($chop,$id.' ')===0)
break;
else if(preg_match('/^\* ([0-9]*) fetch \(flags \((.*)\) internaldate "(.*)" rfc822\.size ([0-9]*) body\[header\] \{([0-9]*)\}/i',$chop,$m)){
$_header = '';
$header = array();
$header['key'] = $m[1];
$flags = $m[2];
if(strpos($flags,"\\Seen")!==false)
$header['seen'] = true;
else
$header['unseen'] = true;
if(strpos($flags,"\\Recent")!==false)
$header['recent'] = true;
if(strpos($flags,"\\Deleted")!==false)
$header['deleted'] = true;
if(strpos($flags,"\\Answered")!==false)
$header['answered'] = true;
$header['flags'] = split_quoted_string($flags);
$header['date'] = $m[3];
$header['size'] = $m[4];
$size = $m[5];
$readen = 0;
}
else if($readen < $size){
$_header .= $s;
$readen += strlen($s);
}
if($readen >= $size){
// trim to size
$_header = substr($_header,0,$size);
$headers[] = array_merge($header,$this->parse_header($_header));
$_header = '';
$readen = 0;
}
}while($s = fgets($this->sock,2048));
return $headers;
}
function get_mail($mbox,$key){
$header = $this->get_header($mbox,$key);
if(count($header) == 0)
return array();
// next id
$id = $this->make_next_id();
// write comamnd
fputs($this->sock,"$id FETCH $key BODY[TEXT]\n");
$s = fgets($this->sock,2048);
if(eregi("^$id (no|bad)",$s)){
throw new Exception("IMAP::get_mail can't fetch $mbox:$key body.<br/>".$s);
}
$size = false;
if(eregi("^\* [0-9]* FETCH .* {([0-9]*)}",$s,$m)){
$size = $m[1];
}
else if(!$size){
print_obj(htmlspecialchars($s));
}
$_mail = '';
$readen = 0;
while($s = fgets($this->sock,2048)){
$chop = chop($s);
if(eregi("^$id ",$chop))
break;
else if($size === false || $readen < $size){
$_mail .= $s;
$readen += strlen($s);
}
}
// delete unnecessary chairs , mostly ')' char at the last line
if($size){
$_mail = substr($_mail,0,$size);
}
return $this->parse_mail_body($header,$_mail);
}
function all_headers($mbox){
$num = $this->select($mbox);
if($num <= 0)
return array();
return $this->fetch_headers(1,$num);
}
function range_headers($mbox,$from,$count){
$num = $this->select($mbox);
if($num < 0) return array();
print $from.','.($from+$count);
return $this->fetch_headers($from,$from+$count);
}
function sorted_headers($mbox,$sort_order=SORT_BY_NUMBER,$from=-1,$count=-1){
$num = $this->select($mbox);
if($num <= 0) return array();
if($from < 0 && $count < 0){
$from = 1;
$count = $num-1;
}
$func = false;
if($sort_order == SORT_BY_DATE)
$func = 'DATE';
else if($sort_order == SORT_BY_SIZE)
$func = 'SIZE';
else if($sort_order == SORT_BY_SUBJECT)
$func = 'REVERSE SUBJECT';
else if($sort_order == SORT_BY_FROM)
$func = 'REVERSE FROM';
else if($sort_order == SORT_BY_TO)
$func = 'TO';
if($func){
// print "SORT ($func) UTF-8 ALL\n";
$data = $this->exec("SORT ($func) UTF-8 ALL");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::sort sort failed<br/>".$data['last']);
$keys = preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SORT')+4)));
//print "$from $count <br/>";
//print implode(" ",$keys)."<br/>";
if($from > 0 && $count >= 0)
$keys = array_slice($keys,$from-1,$count+1);
$keys = array_reverse($keys);
// print implode(" ",$keys)."<br/>";
$headers = array();
foreach($keys as $k){
$tmp = $this->fetch_headers($k);
$headers[] = array_pop($tmp);
}
}
else{
//print "$from $count <br/>";
return array_reverse($this->fetch_headers($from,$from+$count));
}
return $headers;
}
function threads_build_tree(&$thread_syms){
$res = array();
while($val = current($thread_syms)){
if($val == '('){
next($thread_syms);
$ret = $this->threads_build_tree($thread_syms);
if(is_array($ret) && count($ret)==1)
$res[] = $ret[0];
else
$res[] = $ret;
}
else if($val == ')'){
next($thread_syms);
break;
}
else{
next($thread_syms);
$res[] = $val;
}
}
return $res;
}
function threads_tree($thread_list){
$thread_temp = preg_split("//", $thread_list, -1, PREG_SPLIT_NO_EMPTY);
$thread_temp_count = count($thread_temp);
$thread_syms = array();
for($i=0;$i<$thread_temp_count;$i++){
if($thread_temp[$i] == '(' || $thread_temp[$i] == ')'){
$thread_syms[] = $thread_temp[$i];
}
else if(preg_match('/[0-9]/',$thread_temp[$i])){
$digit = '';
do{
$digit .= $thread_temp[$i];
$i++;
}while($i<$thread_temp_count && preg_match('/[0-9]/',$thread_temp[$i]));
$thread_syms[] = $digit;
$i--;
}
}
reset($thread_syms);
return $this->threads_build_tree($thread_syms);
}
function parse_threads($thread_list){
$threads_tree = $this->threads_tree($thread_list);
$thread_temp = preg_split("//", $thread_list, -1, PREG_SPLIT_NO_EMPTY);
$char_count = count($thread_temp);
$counter = 0;
$thread_new = array();
$k = 0;
$thread_new[0] = "";
for($i=0;$i<$char_count;$i++) {
if ($thread_temp[$i] != ')' && $thread_temp[$i] != '(') {
$thread_new[$k] .= $thread_temp[$i];
}
else if($thread_temp[$i] == '(') {
// $thread_new[$k] .= $thread_temp[$i];
$counter++;
}
else if($thread_temp[$i] == ')') {
if ($counter > 1) {
// $thread_new[$k] .= $thread_temp[$i];
$thread_new[$k] .= ' ';
$counter--;
}
else{
// $thread_new[$k] .= $thread_temp[$i];
$k++;
$thread_new[$k] = "";
$counter--;
}
}
}
$thread_new = array_reverse($thread_new);
$thread_list = implode(" ", $thread_new);
$thread_list = str_replace("(", " ", $thread_list);
$thread_list = str_replace(")", " ", $thread_list);
$thread_list = preg_split("/\s/", $thread_list, -1, PREG_SPLIT_NO_EMPTY);
$threads = array();
$threads_count = array();
foreach($thread_new as $s){
$s = trim($s);
if($s != ''){
$thread = array_reverse(preg_split('/\s/',$s,-1,PREG_SPLIT_NO_EMPTY));
$threads[] = $thread;
$threads_count[] = count($thread);
}
}
// if(is_test_user())
// print_obj($threads);
return array('threads'=>$threads,'count'=>$threads_count,'tree'=>$threads_tree);
}
function get_threads($mbox){
$num = $this->select($mbox);
if($num > 0){
// REFERENCES or ORDEREDSUBJECT
$data = $this->exec("THREAD REFERENCES UTF-8 ALL");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::thread thread failed<br/>".$data['last']);
$thread_list = trim(preg_replace('/\* THREAD /','',$data['data']));
return $this->parse_threads($thread_list);
}
}
function get_header($mbox,$key){
$num = $this->select($mbox);
if($key > $num || $key < 1)
return null;
$headers = $this->fetch_headers($key);
foreach($headers as $h)
return $h;
}
function search_label($label,$boxes){
$all_keys = array();
if(is_array($boxes)){
foreach($boxes as $mbox){
$num = $this->select($mbox);
if($num > 0){
$data = $this->exec("SEARCH KEYWORD \"$label\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::search search failed<br/>".$data['last']);
$keys = preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SEARCH')+7)));
$keys = array_diff(array_unique($keys),array(''));
if(count($keys)>0){
foreach($keys as $k){
$k_headers = $this->fetch_headers($k);
$all_keys[$mbox][] = array_pop($k_headers);
}
}
}
}
}
return $all_keys;
}
function search($query,$type,$subject,$email,$body,$boxes){
if(!eregi('and',$type) && !eregi('or',$type)) $type = 'AND';
$query = trim($query);
if($query == '')
return false;
$words = array();
if(eregi('"',$query,$m)){
$chars = preg_split('//',$query);$chars_count=count($chars);
$words = array();
$word = false;$in_quote=false;
for($i=0;$i<$chars_count;$i++){
if($chars[$i] == '"'){
if($in_quote){
$in_quote = false; $words[] = $word;$word = false;
}
else{
if($word) $words[] = $word;
$in_quote = true;
}
continue;
}
else if($in_quote) $word .= $chars[$i];
else if($chars[$i] != ' ') $word .= $chars[$i];
else if($word){
$words[] = $word; $word = false;
}
}
if($word) $words[] = $word;
}
else{
$words = array_diff(array_unique(preg_split("/ /",$query)),array(''));
}
$all_keys = array();
if(is_array($boxes)){
foreach($boxes as $mbox){
$num = $this->select($mbox);
if($num > 0){
$res_keys = false;
foreach($words as $w){
$keys = array();
$w = str_replace('"','\"',trim($w));
$data = $this->exec("SEARCH FROM \"$w\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::search search failed<br/>".$data['last']);
$keys = array_merge($keys,preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SEARCH')+7))));
$data = $this->exec("SEARCH TO \"$w\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::search search failed<br/>".$data['last']);
$keys = array_merge($keys,preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SEARCH')+7))));
$data = $this->exec("SEARCH CC \"$w\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::search search failed<br/>".$data['last']);
$keys = array_merge($keys,preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SEARCH')+7))));
if($subject){
$data = $this->exec("SEARCH SUBJECT \"$w\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::search search failed<br/>".$data['last']);
$keys = array_merge($keys,preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SEARCH')+7))));
}
if($body){
$data = $this->exec("SEARCH BODY \"$w\"");
if(eregi('^'.$data['id'].' (no|bad)',$data['last']))
throw new Exception("IMAP::search search failed<br/>".$data['last']);
$keys = array_merge($keys,preg_split("/ /",trim(substr($data['data'],
strpos($data['data'],'SEARCH')+7))));
}
if(!$res_keys){
$res_keys = $keys;
}
else if(eregi('and',$type))
$res_keys = array_intersect($res_keys,$keys);
else
$res_keys = array_merge($res_keys,$keys);
}
$res_keys = array_diff(array_unique($res_keys),array(''));
if(count($res_keys)>0){
foreach($res_keys as $k){
$tmp_hdrs = $this->fetch_headers($k);
$all_keys[$mbox][] = array_pop($tmp_hdrs);
}
}
}
}
}
return $all_keys;
}
// execute imap command
function exec($command){
// make id
$id = $this->make_next_id();
// write comamnd
fputs($this->sock,"$id $command\n");
// take output
$data['id'] = $id;
$data['data'] = '';
while($s = fgets($this->sock,4096)){
if(eregi("^$id ",$s)){
$data['last'] = $s;
break;
}
else
$data['data'] .= $s;
}
return $data;
}
function make_next_id(){
return sprintf('A%04d',($this->num++));
}
}
///////////////////////////////////
// NNTP protocol implementation
//////////////////////////////////
class NNTP extends MailProtocol{
function NNTP(){
parent::MailProtocol(NNTPHOST,NNTPPORT);
// read firts welcome line
$s = fgets($this->sock,2048);
}
function listing($wildcard){
fputs($this->sock,"LIST ACTIVE *".$wildcard."*\n");
$s = fgets($this->sock,2048);
if(eregi('^[45][0-9]{2} ',$s)){
throw new Exception("NNTP::listing can't open list<br/>".$s);
}
$news = array();
while($s = fgets($this->sock,2048)){
$s = trim($s);
if(eregi("^\.$",$s))
break;
else if(eregi('^([a-z0-9\.\-]*)',$s,$m))
$news[] = trim($m[1]);
}
return $news;
}
function status($newsgroup){
fputs($this->sock,"GROUP $newsgroup\n");
$s = fgets($this->sock,2048);
if(eregi('^[45][0-9]{2} ',$s)){
throw new Exception("NNTP::status can't fetch #$newsgroup status<br/>".$s);
}
$status = array();
if(eregi('[0-9]* ([0-9]*) ([0-9]*) ([0-9]*)',$s,$m)){
$status['messages'] = $m[1];
$status['unseen'] = 0;
}
return $status;
}
function all_headers($newsgroup){
fputs($this->sock,"LISTGROUP $newsgroup\n");
$s = fgets($this->sock,2048);
if(eregi('^[45][0-9]{2} ',$s)){
throw new Exception("NNTP::all_headers can't fetch #$newsgroup headers<br/>".$s);
}
$mnums = array();
while($s = chop(fgets($this->sock,2048))){
if(eregi("^\.$",$s))
break;
else if(eregi('([0-9]*)',$s,$m)){
$mnums[] = $m[1];
}
}
$headers = array();
foreach($mnums as $n)
$headers[] = $this->fetch_header($newsgroup,$n);
return $headers;
}
function get_header($newsgroup,$key){
$status = $this->status($newsgroup);
if($status['messages'] > 0)
return $this->fetch_header($newsgroup,$key);
return array();
}
function fetch_header($newsgroup,$key){
fputs($this->sock,"HEAD $key\n");
$s = fgets($this->sock,2048);
if(eregi('^[45][0-9]{2} ',$s)){
throw new Exception("NNTP::listing can't fetch #$newsgroup:$key header<br/>".$s);
}
$_header = '';
while($s = chop(fgets($this->sock,2048))){
if(eregi("^\.$",$s))
break;
$_header .= "$s\n";
}
$header = $this->parse_header($_header);
$header['key'] = $key;
return $header;
}
function get_mail($newsgroup,$key){
$header = $this->get_header($newsgroup,$key);
if(count($header) == 0)
return array();
fputs($this->sock,"BODY $key\n");
$s = fgets($this->sock,2048);
if(eregi('^[45][0-9]{2} ',$s)){
throw new Exception("NNTP::listing can't fetch #$newsgroup:$key body<br/>".$s);
}
$_mail = '';
while($s = fgets($this->sock,2048)){
if(eregi("^\.$",chop($s)))
break;
$_mail .= $s;
}
return $this->parse_mail_body($header,$_mail);
}
}
////////////////////////////
// Abstract email sender
////////////////////////////
class Sender{
var $if_successful;
function Sender(){
$this->if_successful = false;
}
function send($from,$to,$msg){}
}
// SMTP sender implementation
class SMTPSender extends Sender{
function send($from,$tos,$msg){
$this->if_successful = false;
// replacing line ".\n" by " .\n"
// $checked_msg = ereg_replace("([\r\n]+)\.([\r\n]+)","\\1 .\\2",$msg);
$checked_msg = str_replace("\n\r.\n\r","\n\r.\n\r",$msg);
$checked_msg = str_replace("\n\.\n","\n.\n",$msg);
// open connection
$smtpConnection = fsockopen(SMTPHOST,SMTPPORT,$errno,$errstr);
if(!$smtpConnection){
throw new Exception("SMTPSender::send : connection to [$SMTPHOST:$SMTPPORT] failed<br/>[$errno] $errstr");
}
$out = fgets($smtpConnection,1024);$res .= $out;
// say hello
fputs($smtpConnection,"HELO ".$GLOBALS['MAIL_USER_NAME']."\n");
$out = fgets($smtpConnection,1024);$res .= $out;
if(!eregi("^250",$out))
return $res;
// check server spam , how many lines your server sends ?
/*
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
$out = fgets($smtpConnection,1024);$res .= $out;
*/
// from
fputs($smtpConnection,"MAIL FROM: <$from>\n");
$out = fgets($smtpConnection,1024);$res .= $out;
if(!eregi("^250",$out))
return $res;
$one_ok = false;
// tos
foreach($tos as $to){
fputs($smtpConnection,"RCPT TO: <$to>\n");
$out = fgets($smtpConnection,1024);$res .= $out;
if(eregi("^250",$out))
$one_ok = true;
}
if(!$one_ok){
return $res."No recipients.\n";
}
// data
fputs($smtpConnection,"DATA\n");
$out = fgets($smtpConnection,1024);$res .= $out;
fputs($smtpConnection,$checked_msg."\n");
// end of message
fputs($smtpConnection,".\n");
$out = fgets($smtpConnection,1024);$res .= $out;
fputs($smtpConnection,"QUIT\n");
fclose($smtpConnection);
$this->if_successful = true;
return $res;
}
}
///////////////////////////////////////////////
// helper functions for build email message
///////////////////////////////////////////////
function create_envelope($subject,$to,$cc='',$bcc=''){
$to = str_replace("\n",'',str_replace("\r",'',$to));
$cc = str_replace("\n",'',str_replace("\r",'',$cc));
$bcc = str_replace("\n",'',str_replace("\r",'',$bcc));
// headers
$envelope = array();
// from e-mail
$envelope['from'] =
$_SESSION['preferences']->getName().' <'.get_current_user_email().'>';
// HACK for keasar
if($GLOBALS['MAIL_USER_NAME'] == 'keasar' && MAILSUFFIX == 'cs.bgu.ac.il')
$envelope['from'] = $_SESSION['preferences']->getName().' <hide@address.com>';
// recipients
$pre_recipients = array();
$recipients = array();
// multi-mail delimeters
$tmp = preg_split('/,/',$to);
$to_num = count($tmp);
$pre_recipients = array_merge($pre_recipients,$tmp);
$tmp = preg_split('/,/',$cc);
$cc_num = $to_num+count($tmp);
$pre_recipients = array_merge($pre_recipients,$tmp);
$tmp = preg_split('/,/',$bcc);
$bcc_num = $cc_num+count($tmp);
$pre_recipients = array_merge($pre_recipients,$tmp);
reset($pre_recipients);
$recipients = array();
$recipients_full = array();
$i = 0;
foreach($pre_recipients as $pre_recipient){
$pre_recipient = trim($pre_recipient);
if($pre_recipient != ''){
if(eregi('(.*)<(.+@.+)>',$pre_recipient,$match)){
$recipients[] = chop($match[2]);
}
else if(!eregi('(.+)@(.+)',$pre_recipient)){
/*
$entry = $_SESSION['addressbook']->find($pre_recipient);
if($entry){
$recipients = array_merge($recipients,$entry->emails);
$pre_recipient = $entry->get_emails();
}
else{
$pre_recipient .= "@".MAILSUFFIX;
$recipients[] = $pre_recipient;
}
*/
$pre_recipient .= "@".MAILSUFFIX;
$recipients[] = $pre_recipient;
}
else
$recipients[] = $pre_recipient;
//print '<b>'.$pre_recipient.'</b><br/>';
// header
if($i < $to_num-1)
$envelope['to'] .= $pre_recipient.",";
else if($i < $to_num)
$envelope['to'] .= $pre_recipient;
else if($i < $cc_num-1)
$envelope['cc'] .= $pre_recipient.",";
else if($i < $cc_num)
$envelope['cc'] .= $pre_recipient;
else if($i < $bcc_num-1)
$envelope['bcc'] .= $pre_recipient.",";
else if($i < $bcc_num)
$envelope['bcc'] .= $pre_recipient;
// full with name
$recipients_full[] = $pre_recipient;
}
$i++;
}
$envelope['all-recipients'] = $recipients;
$envelope['all-recipients-full'] = $recipients_full;
if($subject != "")
$envelope['subject'] = $subject;
else
$envelope['subject'] = 'none';
// Custom headers definition
$envelope['custom_headers'][] = 'Organization: '.ORGANIZATION;
#$envelope['custom_headers'][] = 'Errors-To: '.ADMIN_EMAIL;
$envelope['custom_headers'][] = 'X-Mailer: '.VERSION;
return $envelope;
}
/**
* Build mail from envelope(headers) and body.
* Returns mail ready for delivery with \r\n newlines
*/
function build_mail($envelope,$body,$with_bcc=false){
// body pre check
$ctype = 'text/plain';
$boundary = false;
if(is_array($body)){
$body_count = count($body);
if($body_count == 1){
$p = $body[0];
$ctype = $p['type'];
if($p['charset']) $ctype .= '; charset='.$p['charset'];
$single = true;
}
else{
if($body_count == 2 &&
eregi('text/plain',$body[0]['type']) &&
eregi('text/html',$body[1]['type']) &&
$body[0]['disposition'] == '' &&
$body[1]['disposition'] == '' &&
$body[1]['multipart']){
$ctype = "multipart/alternative;\n";
}
else if($body_count == 2 &&
eregi('text/(plain|html)',$body[0]['type']) &&
eregi('message/disposition-notification',$body[1]['type'])){
$ctype = "multipart/report;report-type=disposition-notification;\n";
}
else if($envelope['encrypted']){
$ctype = "multipart/encrypted;\n";
$ctype .= " protocol=\"application/pgp-encrypted\";\n";
}
else if($envelope['signed']){
$ctype = "multipart/signed; micalg=pgp-sha1;\n";
$ctype .= " protocol=\"application/pgp-signature\";\n";
}
else{
$ctype = "multipart/mixed;\n";
}
$boundary = sha1(print_r($body,true));
$ctype .= ' boundary="'.$boundary.'"';
}
}
// encode unicode subjects
if(preg_match('/[\x7f-\xff]/',$envelope['subject'])){
$envelope['subject'] = '=?'.$GLOBALS['DEFAULT_CHARSET'].'?B?'.base64_encode($envelope['subject']).'?=';
}
$res = '';
// headers
if($envelope['from'] != '')
$res .= 'From: '.$envelope['from']."\n";
if($envelope['to'] != '')
$res .= 'To: '.$envelope['to']."\n";
if($envelope['cc'] != '')
$res .= 'Cc: '.$envelope['cc']."\n";
if($with_bcc && $envelope['bcc'] != '')
$res .= 'Bcc: '.$envelope['bcc']."\n";
if($envelope['subject'] != '')
$res .= 'Subject: '.$envelope['subject']."\n";
if(array_not_empty($envelope))
$res .= "MIME-Version: 1.0\n";
$res .= 'Content-Type: '.$ctype."\n";
if(is_array($envelope['custom_headers'])){
foreach($envelope['custom_headers'] as $h){
$res .= $h."\n";
}
}
// header/body separator
$res .= "\n";
// body
if(is_array($body)){
if($single){
$res .= $body[0]['contents.data'];
}
else{
if($envelope['encrypted'])
$res .= "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\n";
else if($envelope['signed'])
$res .= "This is an OpenPGP/MIME signed message (RFC 2440 and 3156)\n";
else
$res .= "This is a multi-part message in MIME format.\n";
foreach($body as $p){
$res .= '--'.$boundary."\n";
// composite part
if($p['composite'] && array_not_empty($p['body'])){
$res .= build_mail($p['envelope'],$p['body'],$with_bcc);
$res .= "\n";
continue;
}
if(isset($p['charset']))
$res .= 'Content-Type: '.$p['type'].'; charset='.$p['charset']."\n";
else
$res .= 'Content-Type: '.$p['type']."\n";
if(isset($p['disposition']))
$res .= 'Content-Disposition: '.$p['disposition']."\n";
if(isset($p['description']))
$res .= 'Content-Description: '.$p['description']."\n";
if($p['encoding'] == ENCBINARY)
$res .= 'Content-Transfer-Encoding: base64'."\n";
else if(isset($p['encoding']))
$res .= 'Content-Transfer-Encoding: '.$p['encoding']."\n";
// separator
$res .= "\n";
if($p['encoding'] == ENCBINARY)
$res .= chunk_split(base64_encode($p['contents.data']));
else{
$res .= $p['contents.data'];
// append \n at end if no
if($len = strlen($p['contents.data']) && $p['contents.data'][$len-1] != "\n")
$res .= "\n";
}
}
$res .= '--'.$boundary."--\n";
}
}
return fix_newlines($res);
}
?>