X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=banana%2Fmbox.inc.php;h=1f158c904c5ae90e3290474345fcdf51965b029b;hb=6ec8703c9a26dc36334478488789433b00c471d7;hp=a5bfdc7ed0157d668ddb880a3a3a0f92a53b004f;hpb=e1debf92f73fab82a01e0e90a4f537b28f22841f;p=banana.git diff --git a/banana/mbox.inc.php b/banana/mbox.inc.php index a5bfdc7..1f158c9 100644 --- a/banana/mbox.inc.php +++ b/banana/mbox.inc.php @@ -13,61 +13,30 @@ require_once dirname(__FILE__) . '/message.inc.php'; class BananaMBox implements BananaProtocoleInterface { - private $file = null; - private $filesize = null; - private $current_id = null; - private $at_beginning = false; - private $file_cache = null; + private $debug = false; + private $bt = array(); private $_lasterrno = 0; private $_lasterror = null; - private $count = null; - private $new_messages = null; - private $messages = null; - - /** Build a protocole handler plugged on the given box - */ public function __construct() { - $filename = $this->getFileName(Banana::$group); - if (is_null($filename)) { - return; - } - $this->filesize = filesize($filename); - $this->file = @fopen($filename, 'r'); - if (!$this->file) { - $this->_lasterrno = 1; - $this->_lasterror = _b_('Can\'t open file'); - $this->file = null; - } - $this->current_id = 0; - $this->at_beginning = true; + $this->debug = Banana::$debug_mbox; } - /** Close the file - */ - public function __destruct() - { - if ($this->file) { - fclose($this->file); - } - } - - /** Indicate if the Protocole handler has been succesfully built - */ public function isValid() { - return !Banana::$group || $this->file; + return true; + //!Banana::$group || $this->file; } - + /** Indicate last error n° */ public function lastErrNo() { return $this->_lasterrno;; } - + /** Indicate last error text */ public function lastError() @@ -93,53 +62,60 @@ class BananaMBox implements BananaProtocoleInterface return array(Banana::$group => array('desc' => '', 'msgnum' => 0, 'unread' => 0)); } - /** Return a message - * @param id Id of the emssage (can be either an Message-id or a message index) - * @return A BananaMessage or null if the given id can't be retreived - */ - public function &getMessage($id) + private function &getRawMessage($id) { $message = null; if (!is_numeric($id)) { if (!Banana::$spool) { return $message; } - $id = Banana::$spool->ids[$id]; + $id = Banana::$spool->ids[$id]->id; } - $messages = $this->readMessages(array($id)); - if (!empty($messages)) { - $message = new BananaMessage($messages[$id]['message']); + $options = array ('-m ' . $id); + $this->getMBoxPosition($options, $id); + return $this->callHelper('-b', $options); + } + + /** Return a message + * @param id Id of the emssage (can be either an Message-id or a message index) + * @return A BananaMessage or null if the given id can't be retreived + */ + public function &getMessage($id) + { + $messages =& $this->getRawMessage($id); + if ($messages) { + $messages = new BananaMessage($messages); + } else { + $messages = null; } - return $message; + return $messages; } /** Return the sources of the given message */ public function getMessageSource($id) { - $message = null; - if (!is_numeric($id)) { - if (!Banana::$spool) { - return $message; - } - $id = Banana::$spool->ids[$id]; - } - $message = $this->readMessages(array($id)); - return implode("\n", $message[$id]['message']); - } + $message =& $this->getRawMessage($id); + if ($message) { + $message = implode("\n", $message); + } + return $message; + } /** Compute the number of messages of the box */ private function getCount() { - $this->count = count(Banana::$spool->overview); - $max = @max(array_keys(Banana::$spool->overview)); - if ($max && Banana::$spool->overview[$max]->storage['next'] == $this->filesize) { - $this->new_messages = 0; - } else { - $this->new_messages = $this->countMessages($this->count); - $this->count += $this->new_messages; - } + $options = array(); + if (@filesize($this->getFileName()) == @Banana::$spool->storage['size']) { + return max(array_keys(Banana::$spool->overview)) + 1; + } + $this->getMBoxPosition($options); + $val =& $this->callHelper('-c', $options); + if (!$val) { + return 0; + } + return intval(trim($val[0])); } /** Return the indexes of the messages presents in the Box @@ -147,10 +123,8 @@ class BananaMBox implements BananaProtocoleInterface */ public function getIndexes() { - if (is_null($this->count)) { - $this->getCount(); - } - return array($this->count, 0, $this->count - 1); + $count = $this->getCount(); + return array($count, 0, $count - 1); } /** Return the message headers (in BananaMessage) for messages from firstid to lastid @@ -158,24 +132,42 @@ class BananaMBox implements BananaProtocoleInterface */ public function &getMessageHeaders($firstid, $lastid, array $msg_headers = array()) { - $msg_headers = array_map('strtolower', $msg_headers); - $messages =& $this->readMessages(range($firstid, $lastid), true); - $msg_headers = array_map('strtolower', $msg_headers); - $headers = array(); - foreach ($msg_headers as $header) { - foreach ($messages as $id=>&$message) { - if (!isset($headers[$id])) { - $headers[$id] = array('beginning' => $message['beginning'], 'end' => $message['end']); - } - if ($header == 'date') { - $headers[$id][$header] = @strtotime($message['message'][$header]); - } else { - $headers[$id][$header] = @$message['message'][$header]; + $headers = null; + $options = array(); + $options[] = "-m $firstid:$lastid"; + $this->getMboxPosition($options, $firstid); + $lines =& $this->callHelper('-d', $options, $msg_headers); + if (!$lines) { + return $headers; + } + $headers = array(); + $in_message = false; + $get_pos = true; + $hname = null; + foreach ($lines as $key=>&$line) { + if (!$in_message) { + if (!empty($line)) { + $id = intval($line); + $in_message = true; + $get_pos = true; } + } elseif ($get_pos) { + $headers[$id] = array('beginning' => intval($line)); + $get_pos = false; + } elseif (empty($line) && empty($hname)) { + $in_message = false; + } elseif (empty($hname)) { + $hname = $line; + } elseif ($hname == 'date') { + $headers[$id][$hname] = @strtotime($line); + $hname = null; + } else { + BananaMimePart::decodeHeader($line, $hname); + $headers[$id][$hname] = $line; + $hname = null; } + unset($lines[$key]); } - unset($this->messages); - unset($messages); return $headers; } @@ -186,9 +178,9 @@ class BananaMBox implements BananaProtocoleInterface foreach ($messages as $id=>&$data) { if (isset(Banana::$spool->overview[$id])) { Banana::$spool->overview[$id]->storage['offset'] = $data['beginning']; - Banana::$spool->overview[$id]->storage['next'] = $data['end']; } } + Banana::$spool->storage['size'] = @filesize($this->getFileName()); } /** Return the indexes of the new messages since the give date @@ -196,8 +188,12 @@ class BananaMBox implements BananaProtocoleInterface */ public function getNewIndexes($since) { + $this->open(); + if (is_null($this->file)) { + return array(); + } if (is_null($this->new_messages)) { - $this->getCount(); + $this->getCount(); } return range($this->count - $this->new_messages, $this->count - 1); } @@ -241,7 +237,7 @@ class BananaMBox implements BananaProtocoleInterface foreach ($headers as $key=>$value) { if (!empty($value)) { $hdrs .= "$key: $value\r\n"; - } + } } $body = $message->get(false); return mail($to, $subject, $body, $hdrs); @@ -274,16 +270,27 @@ class BananaMBox implements BananaProtocoleInterface return $file . $mail; } + /** Return the execution backtrace + */ + public function backtrace() + { + if ($this->debug) { + return $this->bt; + } else { + return null; + } + } + ####### # Filesystem functions ####### - protected function getFileName($box) + protected function getFileName() { - if (is_null($box)) { + if (is_null(Banana::$group)) { return null; } - @list($mail, $domain) = explode('@', $box); + @list($mail, $domain) = explode('@', Banana::$group); return Banana::$mbox_path . '/' . $mail; } @@ -291,171 +298,43 @@ class BananaMBox implements BananaProtocoleInterface # MBox parser ####### - /** Go to the given message + /** Add the '-p' optioin for callHelper */ - private function goTo($id) + private function getMBoxPosition(array &$options, $id = null) { - if ($this->current_id == $id && $this->at_beginning) { - return true; - } - if ($id == 0) { - fseek($this->file, 0); - $this->current_id = 0; - $this->at_beginning = true; - return true; - } elseif (isset(Banana::$spool->overview[$id]) || isset($this->messages[$id])) { - if (isset(Banana::$spool->overview[$id])) { - $pos = Banana::$spool->overview[$id]->storage['offset']; + if (Banana::$spool && Banana::$spool->overview) { + if (!is_null($id) && isset(Banana::$spool->overview[$id])) { + $key = $id; } else { - $pos = $this->messages[$id]['beginning']; - } - if (fseek($this->file, $pos) == 0) { - $this->current_id = $id; - $this->at_beginning = true; - return true; - } else { - $this->current_id = null; - $this->_lasterrno = 2; - $this->_lasterror = _b_('Can\'t find message ') . $id; - return false; - } - } else { - $max = @max(array_keys(Banana::$spool->overview)); - if (is_null($max)) { - $max = 0; - } - if ($id <= $max && $max != 0) { - $this->current_id = null; - $this->_lasterrno = 3; - $this->_lasterror = _b_('Invalid message index ') . $id; - return false; - } - if (!$this->goTo($max)) { - return false; - } - if (feof($this->file)) { - $this->current_id = null; - $this->_lasterrno = 4; - $this->_lasterror = _b_('Requested index does not exists or file has been truncated'); - return false; - } - while ($this->readCurrentMessage(true) && $this->current_id < $id); - if ($this->current_id == $id) { - return true; - } - $this->current_id = null; - $this->_lasterrno = 5; - $this->_lasterror = _b_('Requested index does not exists or file has been truncated'); - return false; - } - } - - private function countMessages($from = 0) - { - $this->messages =& $this->readMessages(array($from), true, true); - return count($this->messages); - } - - /** Read the current message (identified by current_id) - * @param needFrom_ BOOLEAN is true if the first line *must* be a From_ line - * @param alignNext BOOLEAN is true if the buffer must be aligned at the beginning of the next From_ line - * @return message sources (without storage data) - */ - private function &readCurrentMessage($stripBody = false, $needFrom_ = true, $alignNext = true) - { - $file_cache =& $this->file_cache; - if ($file_cache && $file_cache != ftell($this->file)) { - $file_cache = null; - } - $msg = array(); - $canFrom_ = false; - $inBody = false; - while(!feof($this->file)) { - // Process file cache - if ($file_cache) { // this is a From_ line - $needFrom_ = false; - $this->at_beginning = false; - $file_cache = null; - continue; - } - - // Read a line - $line = rtrim(fgets($this->file), "\r\n"); - - // Process From_ line - if ($needFrom_ || !$msg || $canFrom_) { - if (substr($line, 0, 5) == 'From ') { // this is a From_ line - if ($needFrom_) { - $needFrom = false; - } elseif (!$msg) { - continue; - } else { - $this->current_id++; // we are finally in the next message - if ($alignNext) { // align the file pointer at the beginning of the new message - $this->at_beginning = true; - $file_cache = ftell($this->file); - } - break; - } - } elseif ($needFrom_) { - return $msg; + $key = max(array_keys(Banana::$spool->overview)); + if (!is_null($id) && $key >= $id) { + return; } } - - // Process non-From_ lines - if (substr($line, 0, 6) == '>From ') { // remove inline From_ quotation - $line = substr($line, 1); - } - if (!$stripBody || !$inBody) { - $msg[] = $line; // add the line to the message source + if (isset(Banana::$spool->overview[$key]->storage['offset'])) { + $options[] = '-p ' . $key . ':' . Banana::$spool->overview[$key]->storage['offset']; } - $canFrom_ = empty($line); // check if next line can be a From_ line - if ($canFrom_ && !$inBody && $stripBody) { - $inBody = true; - } - $this->at_beginning = false; - } - if (!feof($this->file) && !$canFrom_) { - $msg = array(); } - return $msg; } - /** Read message with the given ids - * @param ids ARRAY of ids to look for - * @param strip BOOLEAN if true, only headers are retrieved - * @param from BOOLEAN if true, process all messages from max(ids) to the end of the mbox - * @return Array(Array('message' => message sources (or parsed message headers if $strip is true), - * 'beginning' => offset of message beginning, - * 'end' => offset of message end)) - */ - private function &readMessages(array $ids, $strip = false, $from = false) + private function &callHelper($action, array $options = array(), array $headers = array()) { - if ($this->messages) { - return $this->messages; + $action .= ' -f ' . $this->getFileName(); + $cmd = Banana::$mbox_helper . " $action " . implode(' ', $options) . ' ' . implode(' ', $headers); + if ($this->debug) { + $start = microtime(true); } - sort($ids); - $messages = array(); - while ((count($ids) || $from) && !feof($this->file)) { - if (count($ids)) { - $id = array_shift($ids); - } else { - $id++; - } - if ($id != $this->current_id || !$this->at_beginning) { - if (!$this->goTo($id)) { - continue; - } - } - $beginning = ftell($this->file); - $message =& $this->readCurrentMessage($strip, false); - if ($strip) { - $message =& BananaMimePart::parseHeaders($message); - } - $end = ftell($this->file); - $messages[$id] = array('message' => $message, 'beginning' => $beginning, 'end' => $end); + exec($cmd, $out, $return); + if ($this->debug) { + $this->bt[] = array('action' => $cmd, 'time' => (microtime(true) - $start), + 'code' => $return, 'response' => count($out), 'error' => $return ? "Helper failed" : null); } - return $messages; + if ($return != 0) { + $this->_lasterrorno = 1; + $this->_lasterrorcode = "Helper failed"; + $out = null; + } + return $out; } }