X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=banana%2Fmbox.inc.php;h=01c329ebd7d34d93f81f2639c67ee5a996ec0c86;hb=dc5f77ad837f38a80e02699b97a4d7947f2f7f70;hp=f14fbeebb4b02cc33d9107e2f749bc61f5958e80;hpb=dfdf27fc75c66e2e1a7e9feb4bd41a1644c3865f;p=banana.git diff --git a/banana/mbox.inc.php b/banana/mbox.inc.php index f14fbee..01c329e 100644 --- a/banana/mbox.inc.php +++ b/banana/mbox.inc.php @@ -13,52 +13,21 @@ 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; - } - - /** Close the file - */ - public function __destruct() - { - if ($this->file) { - fclose($this->file); - } + $this->debug = Banana::$debug_mbox; } - - /** 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° @@ -93,53 +62,58 @@ 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) { + if (!Banana::$spool) { return $message; } $id = Banana::$spool->ids[$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); } - 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(Banana::$spool->ids); + } + $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 +121,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 +130,40 @@ 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']); + $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(); + while ($lines) { + $id = array_shift($lines); + if ($id === '') { + continue; + } + $offset = array_shift($lines); + if ($offset === '') { + continue; + } + $id = intval($id); + $headers[$id] = array('beginning' => intval($offset)); + while (true) { + $hname = array_shift($lines); + if ($hname === '') { + break; } - if ($header == 'date') { - $headers[$id][$header] = @strtotime($message['message'][$header]); + $hval = array_shift($lines); + if ($hname == 'date') { + $headers[$id][$hname] = @strtotime($hval); } else { - $headers[$id][$header] = @$message['message'][$header]; + $headers[$id][$hname] = $hval; } } } - unset($this->messages); - unset($messages); + array_walk_recursive($headers, array('BananaMimePart', 'decodeHeader')); return $headers; } @@ -186,9 +174,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,6 +184,10 @@ 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(); } @@ -274,188 +266,71 @@ 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; } ####### # 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']; - } else { - $pos = $this->messages[$id]['beginning']; - } - if (fseek($this->file, $pos) == 0) { - $this->current_id = $id; - $this->at_beginning = true; - return true; + if (Banana::$spool->overview) { + if (!is_null($id) && isset(Banana::$spool->overview[$id])) { + $key = $id; } 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(Banana::$spool->ids); + 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 (isset(Banana::$spool->overview[$key]->storage['offset'])) { + $options[] = '-p ' . $key . ':' . Banana::$spool->overview[$key]->storage['offset']; } - if (!$stripBody || !$inBody) { - $msg[] = $line; // add the line to the message source - } - $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)) { - break; - } - } - $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); + } + if ($return != 0) { + $this->_lasterrorno = 1; + $this->_lasterrorcode = "Helper failed"; + $out = null; } - return $messages; + return $out; } }