2016-10-30 13:40:29 +00:00
|
|
|
<?php
|
2016-11-06 16:20:15 +00:00
|
|
|
/**
|
|
|
|
* @author cytopia
|
|
|
|
* @date 2016-11-06
|
|
|
|
*
|
|
|
|
* Fixed constructor name to __construct in order to be
|
|
|
|
* compatible with PHP >5.5
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2016-10-30 13:40:29 +00:00
|
|
|
/**
|
|
|
|
* Class to read mbox mail files.
|
|
|
|
*
|
|
|
|
* PHP versions 4 and 5
|
|
|
|
*
|
|
|
|
* @category Mail
|
|
|
|
* @package Mail_Mbox
|
|
|
|
* @author Roberto Berto <darkelder@php.net>
|
|
|
|
* @author Christian Weiske <cweiske@php.net>
|
|
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
|
|
* @version CVS: $Id: Mbox.php 290486 2009-11-10 20:42:51Z cweiske $
|
|
|
|
* @link http://pear.php.net/package/Mail_Mbox
|
|
|
|
*/
|
|
|
|
require_once 'PEAR.php';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file has been modified since it has been opened.
|
|
|
|
* You should close and re-open it.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_MODIFIED', 2101);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The mail mbox file doesn't exist.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_FILE_NOT_EXISTING', 2102);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* There is no message with the given number.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_MESSAGE_NOT_EXISTING', 2103);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* No permission to access the file.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_NO_PERMISSION', 2104);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file cannot be opened.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_CANNOT_OPEN', 2105);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file cannot be closed due to some strange things.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_CANNOT_CLOSE', 2106);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file cannot be read.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_CANNOT_READ', 2107);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Failed to create a temporary file.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_CANNOT_CREATE_TMP', 2108);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file cannot be written.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_CANNOT_WRITE', 2109);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file is not open.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_NOT_OPEN', 2110);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The resource isn't valid anymore.
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_NO_RESOURCE', 2111);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Message is invalid and would trash the file
|
|
|
|
*/
|
|
|
|
define('MAIL_MBOX_ERROR_MSG_INVALID', 2112);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class to read mbox mail files.
|
|
|
|
*
|
|
|
|
* An mbox mail file is contains plain emails concatenated in one
|
|
|
|
* big file. Since each mail starts with "From ", and ends with a newline,
|
|
|
|
* they can be separated from each other.
|
|
|
|
*
|
|
|
|
* This class takes a mbox filename in the constructor, generates an
|
|
|
|
* index where the mails start and end when calling open() and returns
|
|
|
|
* single mails with get(), using the positions in the index.
|
|
|
|
*
|
|
|
|
* With the help of this class, you also can add(), remove() and update()
|
|
|
|
* messages in the mbox file. When calling one of this methods, the class
|
|
|
|
* checks if the file has been modified since the index was created -
|
|
|
|
* changing the file with the wrong positions in the index would very likely
|
|
|
|
* corrupt it.
|
|
|
|
* This check is not done when retrieving single messages via get(), as this
|
|
|
|
* would slow down the process if you retrieve thousands of mails. You can,
|
|
|
|
* however, call hasBeenModified() before using get() to check for modification
|
|
|
|
* yourself. If the method returns true, you should close() and re-open() the
|
|
|
|
* file.
|
|
|
|
*
|
|
|
|
* If something strange happens and you don't know why, activate debugging with
|
|
|
|
* setDebug(true). You also can modify the temporary directory in which changed
|
|
|
|
* mboxes are stored when adding/removing/modifying by using setTmpDir('/path/');
|
|
|
|
*
|
|
|
|
* See @link tags for specifications.
|
|
|
|
*
|
|
|
|
* @category Mail
|
|
|
|
* @package Mail_Mbox
|
|
|
|
* @author Roberto Berto <darkelder@php.net>
|
|
|
|
* @author Christian Weiske <cweiske@php.net>
|
|
|
|
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
|
|
|
* @link http://pear.php.net/package/Mail_Mbox
|
|
|
|
* @link http://en.wikipedia.org/wiki/Mbox
|
|
|
|
* @link http://www.qmail.org/man/man5/mbox.html
|
|
|
|
*/
|
|
|
|
class Mail_Mbox extends PEAR
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* File resource / handle
|
|
|
|
*
|
|
|
|
* @var resource
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
var $_resource = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Message index. Each mail has its own subarray,
|
|
|
|
* which contains the start position and end position
|
|
|
|
* as first and second subindex.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
var $_index = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp at which the file has been modified last.
|
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
var $_lastModified = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Debug mode
|
|
|
|
*
|
|
|
|
* Set to true to turn on debug mode.
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
* @access public
|
|
|
|
* @see setDebug()
|
|
|
|
* @see getDebug()
|
|
|
|
*/
|
|
|
|
var $debug = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Directory in which the temporary mbox files are created.
|
|
|
|
* Even if it's a unix directory, it does work on windows as
|
|
|
|
* the only function it's used in is tempnam which automatically
|
|
|
|
* chooses the right temp directory if this here doesn't exist.
|
|
|
|
* So this variable is for special needs only.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @access public
|
|
|
|
* @see getTmpDir()
|
|
|
|
* @see setTmpDir()
|
|
|
|
*/
|
|
|
|
var $tmpdir = '/tmp';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if the file is automatically re-opened and its
|
|
|
|
* structure is parsed after modifying it. Setting this to false
|
|
|
|
* makes you responsible for calling open() by hand, but is
|
|
|
|
* *a lot* faster when appending many messages.
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
var $autoReopen = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new Mbox class instance.
|
|
|
|
* After creating it, you should use open().
|
|
|
|
*
|
|
|
|
* @param string $file Filename to open.
|
|
|
|
*
|
|
|
|
* @access public
|
|
|
|
*/
|
2016-11-06 16:20:15 +00:00
|
|
|
function __construct($file)
|
2016-10-30 13:40:29 +00:00
|
|
|
{
|
|
|
|
$this->_file = $file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open the mbox file
|
|
|
|
*
|
|
|
|
* Also, this function will process the Mbox and create a cache
|
|
|
|
* that tells each message start and end bytes.
|
|
|
|
*
|
|
|
|
* @return boolean|PEAR_Error True if all went ok, PEAR_Error on failure
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function open($create = false)
|
|
|
|
{
|
|
|
|
// check if file exists else return pear error
|
|
|
|
if (!is_file($this->_file)) {
|
|
|
|
if ($create) {
|
|
|
|
$ret = $this->_create();
|
|
|
|
if (PEAR::isError($ret)) {
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot open the mbox file "'
|
|
|
|
. $this->_file . '": file does not exist.',
|
|
|
|
MAIL_MBOX_ERROR_FILE_NOT_EXISTING
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// opening the file
|
|
|
|
$this->_lastModified = filemtime($this->_file);
|
|
|
|
$this->_resource = fopen($this->_file, 'r');
|
|
|
|
if (!is_resource($this->_resource)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot open the mbox file: maybe without permission.',
|
|
|
|
MAIL_MBOX_ERROR_NO_PERMISSION
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// process the file and get the messages bytes offsets
|
|
|
|
$this->_process();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the file
|
|
|
|
*
|
|
|
|
* @return boolean True if it was created, false if it already
|
|
|
|
* existed. PEAR_Error in case it could not
|
|
|
|
* be created.
|
|
|
|
*
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
function _create()
|
|
|
|
{
|
|
|
|
if (is_file($this->_file)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//We should maybe try to check if the directory
|
|
|
|
// is writable here. But that's too much fuss for now.
|
|
|
|
touch($this->_file);
|
|
|
|
|
|
|
|
if (is_file($this->_file)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//error
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'File could not be created',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_WRITE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Re-opens the file and parses the messages again.
|
|
|
|
* Used by other methods to be able to be able to prevent
|
|
|
|
* re-opening the file.
|
|
|
|
*
|
|
|
|
* @return mixed See open() for return values. Returns true if
|
|
|
|
* $this->autoReopen is false.
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
function _reopen()
|
|
|
|
{
|
|
|
|
if ($this->autoReopen) {
|
|
|
|
return $this->open();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close a Mbox
|
|
|
|
*
|
|
|
|
* Close the Mbox file opened by open()
|
|
|
|
*
|
|
|
|
* @return mixed true on success, else PEAR_Error
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function close()
|
|
|
|
{
|
|
|
|
if (!is_resource($this->_resource)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot close the mbox file because it was not open.',
|
|
|
|
MAIL_MBOX_ERROR_NOT_OPEN
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fclose($this->_resource)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot close the mbox, maybe file is being used (?)',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_CLOSE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get number of messages in this mbox
|
|
|
|
*
|
|
|
|
* @return int Number of messages on Mbox (starting on 1,
|
|
|
|
* 0 if no message exists)
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function size()
|
|
|
|
{
|
|
|
|
if ($this->_index !== null) {
|
|
|
|
return sizeof($this->_index);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a message from the mbox
|
|
|
|
*
|
|
|
|
* Note: Message numbers start from 0.
|
|
|
|
*
|
|
|
|
* @param int $message The number of the message to retrieve
|
|
|
|
*
|
|
|
|
* @return string Return the message, PEAR_Error on error
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function get($message)
|
|
|
|
{
|
|
|
|
// checking if we have bytes locations for this message
|
|
|
|
if (!is_array($this->_index[$message])) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message does not exist.',
|
|
|
|
MAIL_MBOX_ERROR_MESSAGE_NOT_EXISTING
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// getting bytes locations
|
|
|
|
$bytesStart = $this->_index[$message][0];
|
|
|
|
$bytesEnd = $this->_index[$message][1];
|
|
|
|
|
|
|
|
// a debug feature to show the bytes locations
|
|
|
|
if ($this->debug) {
|
|
|
|
printf("%08d=%08d<br />", $bytesStart, $bytesEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_resource($this->_resource)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Mbox resource is not valid. Maybe you need to re-open it?',
|
|
|
|
MAIL_MBOX_ERROR_NO_RESOURCE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// seek to start of message
|
|
|
|
if (fseek($this->_resource, $bytesStart) == -1) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot read message bytes',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_READ
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($bytesEnd - $bytesStart <= 0) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message byte length is negative',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_READ
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// reading and returning message
|
|
|
|
// (bytes to read = difference of bytes locations)
|
|
|
|
$msg = fread($this->_resource, $bytesEnd - $bytesStart);
|
|
|
|
return $this->_unescapeMessage($msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a message from Mbox and save it.
|
|
|
|
*
|
|
|
|
* Note: messages start with 0.
|
|
|
|
*
|
|
|
|
* @param int $message The number of the message to remove, or
|
|
|
|
* array of message ids to remove
|
|
|
|
*
|
|
|
|
* @return mixed Return true else PEAR_Error
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function remove($message)
|
|
|
|
{
|
|
|
|
if ($this->hasBeenModified()) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'File has been modified since loading. Re-open the file.',
|
|
|
|
MAIL_MBOX_ERROR_MODIFIED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert single message to array
|
|
|
|
if (!is_array($message)) {
|
|
|
|
$message = array($message);
|
|
|
|
}
|
|
|
|
|
|
|
|
// checking if we have bytes locations for this message
|
|
|
|
foreach ($message as $msg) {
|
|
|
|
if (!isset($this->_index[$msg])
|
|
|
|
|| !is_array($this->_index[$msg])
|
|
|
|
) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message ' . $msg . 'does not exist.',
|
|
|
|
MAIL_MBOX_ERROR_MESSAGE_NOT_EXISTING
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// changing umask for security reasons
|
|
|
|
$umaskOld = umask(077);
|
|
|
|
// creating temp file
|
|
|
|
$ftempname = tempnam($this->tmpdir, 'Mail_Mbox');
|
|
|
|
// returning to old umask
|
|
|
|
umask($umaskOld);
|
|
|
|
|
|
|
|
$ftemp = fopen($ftempname, 'w');
|
|
|
|
if ($ftemp === false) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot create a temp file "' . $ftempname . '".',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_CREATE_TMP
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// writing only undeleted messages
|
|
|
|
$messages = $this->size();
|
|
|
|
|
|
|
|
for ($x = 0; $x < $messages; $x++) {
|
|
|
|
if (in_array($x, $message)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$messageThis = $this->_escapeMessage($this->get($x));
|
|
|
|
if (is_string($messageThis)) {
|
|
|
|
fwrite($ftemp, $messageThis, strlen($messageThis));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// closing file
|
|
|
|
$this->close();
|
|
|
|
fclose($ftemp);
|
|
|
|
|
|
|
|
return $this->_move($ftempname, $this->_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update a message
|
|
|
|
*
|
|
|
|
* Note: messages start with 0.
|
|
|
|
*
|
|
|
|
* @param int $message The number of Message to update
|
|
|
|
* @param string $content The new content of the Message
|
|
|
|
*
|
|
|
|
* @return mixed Return true if all is ok, else PEAR_Error
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function update($message, $content)
|
|
|
|
{
|
|
|
|
if (!$this->_isValid($content)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message is invalid', MAIL_MBOX_ERROR_MSG_INVALID
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->hasBeenModified()) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'File has been modified since loading. Re-open the file.',
|
|
|
|
MAIL_MBOX_ERROR_MODIFIED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// checking if we have bytes locations for this message
|
|
|
|
if (!is_array($this->_index[$message])) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message does not exist.',
|
|
|
|
MAIL_MBOX_ERROR_MESSAGE_NOT_EXISTING
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// creating temp file
|
|
|
|
$ftempname = tempnam($this->tmpdir, 'Mail_Mbox');
|
|
|
|
$ftemp = fopen($ftempname, 'w');
|
|
|
|
if ($ftemp === false) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot create temp file "' . $ftempname . '" .',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_CREATE_TMP
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$messages = $this->size();
|
|
|
|
|
|
|
|
for ($x = 0; $x < $messages; $x++) {
|
|
|
|
if ($x == $message) {
|
|
|
|
$messageThis = $content;
|
|
|
|
} else {
|
|
|
|
$messageThis = $this->get($x);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_string($messageThis)) {
|
|
|
|
$messageThis = $this->_escapeMessage($messageThis);
|
|
|
|
fwrite($ftemp, $messageThis, strlen($messageThis));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// closing file
|
|
|
|
$this->close();
|
|
|
|
fclose($ftemp);
|
|
|
|
|
|
|
|
return $this->_move($ftempname, $this->_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert a message
|
|
|
|
*
|
|
|
|
* PEAR::Mail_Mbox will insert the message according its offset.
|
|
|
|
* 0 means before the actual message 0. 3 means before the message 3
|
|
|
|
* (Remember: message 3 is the fourth message). The default is put
|
|
|
|
* AFTER the last message (offset = null).
|
|
|
|
*
|
|
|
|
* @param string $content The content of the new message
|
|
|
|
* @param int $offset Before the offset. Default: last message (null)
|
|
|
|
*
|
|
|
|
* @return mixed Return true else PEAR_Error object
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function insert($content, $offset = null)
|
|
|
|
{
|
|
|
|
if (!$this->_isValid($content)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message is invalid', MAIL_MBOX_ERROR_MSG_INVALID
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->hasBeenModified()) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'File has been modified since loading. Re-open the file.',
|
|
|
|
MAIL_MBOX_ERROR_MODIFIED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// optimize insert() to use append whenever possible
|
|
|
|
if ($offset < 0 || $offset == $this->size() || $this->size() == 0) {
|
|
|
|
return $this->append($content);
|
|
|
|
}
|
|
|
|
|
|
|
|
// creating temp file
|
|
|
|
$ftempname = tempnam($this->tmpdir, 'Mail_Mbox');
|
|
|
|
$ftemp = fopen($ftempname, 'w');
|
|
|
|
if ($ftemp === false) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot create temp file "' . $ftempname . '".',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_CREATE_TMP
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// writing only undeleted messages
|
|
|
|
$messages = $this->size();
|
|
|
|
$content = $this->_escapeMessage($content);
|
|
|
|
|
|
|
|
if ($messages == 0 && $offset !== null) {
|
|
|
|
fwrite($ftemp, $content, strlen($content));
|
|
|
|
} else {
|
|
|
|
for ($x = 0; $x < $messages; $x++) {
|
|
|
|
if ($offset !== null && $x == $offset) {
|
|
|
|
fwrite($ftemp, $content, strlen($content));
|
|
|
|
}
|
|
|
|
$messageThis = $this->_escapeMessage($this->get($x));
|
|
|
|
|
|
|
|
if (is_string($messageThis)) {
|
|
|
|
fwrite($ftemp, $messageThis, strlen($messageThis));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($offset === null) {
|
|
|
|
fwrite($ftemp, $content, strlen($content));
|
|
|
|
}
|
|
|
|
|
|
|
|
// closing file
|
|
|
|
$this->close();
|
|
|
|
fclose($ftemp);
|
|
|
|
|
|
|
|
return $this->_move($ftempname, $this->_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Appends a message at the end of the file.
|
|
|
|
*
|
|
|
|
* This method is also used by insert() since it's faster.
|
|
|
|
*
|
|
|
|
* @param string $content The content of the new message
|
|
|
|
*
|
|
|
|
* @return mixed Return true else PEAR_Error object
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function append($content)
|
|
|
|
{
|
|
|
|
if (!$this->_isValid($content)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Message is invalid', MAIL_MBOX_ERROR_MSG_INVALID
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->close();
|
|
|
|
$content = $this->_escapeMessage($content);
|
|
|
|
|
|
|
|
$fp = fopen($this->_file, 'a');
|
|
|
|
if ($fp === false) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot open file "' . $this->_file . '" for appending.',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_OPEN
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fwrite($fp, $content, strlen($content)) === false) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot write to file "' . $this->_file. '".',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_WRITE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->_reopen();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the given message is valid.
|
|
|
|
* If it was invalid and we'd add it to the file,
|
|
|
|
* it would get unreadable
|
|
|
|
*
|
|
|
|
* @param string $content Message to be added or updated
|
|
|
|
*
|
|
|
|
* @return boolean True if it is valid, false if not
|
|
|
|
*/
|
|
|
|
function _isValid($content)
|
|
|
|
{
|
|
|
|
if (substr($content, 0, 5) != 'From ') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move a file to another.
|
|
|
|
*
|
|
|
|
* Used internally to move the content of the temp file to the mbox file.
|
|
|
|
* Note that we can't use rename() internally, as it behaves very, very
|
|
|
|
* strange on windows.
|
|
|
|
*
|
|
|
|
* @param string $ftempname Source file - will be removed
|
|
|
|
* @param string $filename Output file
|
|
|
|
*
|
|
|
|
* @return boolean|PEAR_Error True if everything went fine, PEAR_Error when
|
|
|
|
* an error happened.
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
function _move($ftempname, $filename)
|
|
|
|
{
|
|
|
|
if (!copy($ftempname, $filename)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot copy "' . $ftempname . '" to "' . $filename . '".',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_WRITE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
unlink($ftempname);
|
|
|
|
|
|
|
|
// open another resource and substitute it to the old one
|
|
|
|
$this->_file = $filename;
|
|
|
|
return $this->_reopen();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process the Mbox
|
|
|
|
*
|
|
|
|
* Put start bytes and end bytes of each message into _index array
|
|
|
|
*
|
|
|
|
* @return boolean|PEAR_Error True if all went ok, PEAR_Error on failure
|
|
|
|
* @access protected
|
|
|
|
*/
|
|
|
|
function _process()
|
|
|
|
{
|
|
|
|
$this->_index = array();
|
|
|
|
|
|
|
|
// sanity check
|
|
|
|
if (!is_resource($this->_resource)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Resource is not valid. Maybe the file has not be opened?',
|
|
|
|
MAIL_MBOX_ERROR_NOT_OPEN
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// going to start
|
|
|
|
if (fseek($this->_resource, 0) == -1) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'Cannot read mbox',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_READ
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// current start byte position
|
|
|
|
$start = 0;
|
|
|
|
// last start byte position
|
|
|
|
$laststart = 0;
|
|
|
|
// there aren't any message
|
|
|
|
$hasmessage = false;
|
|
|
|
|
|
|
|
while ($line = fgets($this->_resource, 4096)) {
|
|
|
|
// if line start with "From ", it is a new message
|
|
|
|
if (0 === strncmp($line, 'From ', 5)) {
|
|
|
|
// save last start byte position
|
|
|
|
$laststart = $start;
|
|
|
|
|
|
|
|
// new start byte position is the start of the line
|
|
|
|
$start = ftell($this->_resource) - strlen($line);
|
|
|
|
|
|
|
|
// if it is not the first message add message positions
|
|
|
|
if ($start > 0) {
|
|
|
|
$this->_index[] = array($laststart, $start - 1);
|
|
|
|
} else {
|
|
|
|
// tell that there is really a message on the file
|
|
|
|
$hasmessage = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there are just one message, or if it's the last one,
|
|
|
|
// add it to messages positions
|
|
|
|
if (($start == 0 && $hasmessage === true) || ($start > 0)) {
|
|
|
|
$this->_index[] = array($start, ftell($this->_resource));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Quotes "From " lines in the midst of the message.
|
|
|
|
* And quoted "From " lines, too :)
|
|
|
|
* Also appends the trailing newline.
|
|
|
|
* After escaping, the message can be written to file.
|
|
|
|
*
|
|
|
|
* @param string $message Message content
|
|
|
|
*
|
|
|
|
* @return string Escaped message
|
|
|
|
*
|
|
|
|
* @access protected
|
|
|
|
* @see _unescapeMessage()
|
|
|
|
*/
|
|
|
|
function _escapeMessage($message)
|
|
|
|
{
|
|
|
|
if (substr($message, -1) == "\n") {
|
|
|
|
$message .= "\n";
|
|
|
|
} else {
|
|
|
|
$message .= "\n\n";
|
|
|
|
}
|
|
|
|
return preg_replace(
|
|
|
|
"/\n([>]*From )/",
|
|
|
|
"\n>$1",
|
|
|
|
$message
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes quoted "From " lines from the message
|
|
|
|
*
|
|
|
|
* @param string $message Message content
|
|
|
|
*
|
|
|
|
* @return string Unescaped message
|
|
|
|
*
|
|
|
|
* @access protected
|
|
|
|
* @see _escapeMessage()
|
|
|
|
*/
|
|
|
|
function _unescapeMessage($message)
|
|
|
|
{
|
|
|
|
return preg_replace(
|
|
|
|
"/\n>([>]*From )/",
|
|
|
|
"\n$1",
|
|
|
|
//the -1 drops the last newline
|
|
|
|
substr($message, 0, -1)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the file was modified since it has been loaded.
|
|
|
|
* If this is true, the file needs to be re-opened.
|
|
|
|
*
|
|
|
|
* @return bool True if it has been modified.
|
|
|
|
* @access public
|
|
|
|
*/
|
|
|
|
function hasBeenModified()
|
|
|
|
{
|
|
|
|
return filemtime($this->_file) > $this->_lastModified;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dumb getter and setter
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the directory for temporary files.
|
|
|
|
*
|
|
|
|
* @param string $tmpdir The new temporary directory
|
|
|
|
*
|
|
|
|
* @return mixed True if all is ok, PEAR_Error if $tmpdir
|
|
|
|
* is a dir but not writable
|
|
|
|
*
|
|
|
|
* @see Mail_Mbox::$tmpdir
|
|
|
|
*/
|
|
|
|
function setTmpDir($tmpdir)
|
|
|
|
{
|
|
|
|
if (is_dir($tmpdir) && !is_writable($tmpdir)) {
|
|
|
|
return PEAR::raiseError(
|
|
|
|
'"' . $tmpdir . '" is not writable.',
|
|
|
|
MAIL_MBOX_ERROR_CANNOT_WRITE
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$this->tmpdir = $tmpdir;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the temporary directory
|
|
|
|
*
|
|
|
|
* @return string The temporary directory
|
|
|
|
*/
|
|
|
|
function getTmpDir()
|
|
|
|
{
|
|
|
|
return $this->tmpdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the debug flag
|
|
|
|
*
|
|
|
|
* @param bool $debug If debug is on or off
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
* @see Mail_Mbox::$debug
|
|
|
|
*/
|
|
|
|
function setDebug($debug)
|
|
|
|
{
|
|
|
|
$this->debug = (bool)$debug;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the debug flag setting
|
|
|
|
*
|
|
|
|
* @see Mail_Mbox::$debug
|
|
|
|
*
|
|
|
|
* @return bool If debug is enabled.
|
|
|
|
*/
|
|
|
|
function getDebug()
|
|
|
|
{
|
|
|
|
return $this->debug;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets if the mbox is reloaded after modification
|
|
|
|
* automatically.
|
|
|
|
*
|
|
|
|
* @param bool $autoReopen If the mbox is reloaded automatically
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
* @see Mail_Mbox::$autoReopen
|
|
|
|
*/
|
|
|
|
function setAutoReopen($autoReopen)
|
|
|
|
{
|
|
|
|
$this->autoReopen = (bool)$autoReopen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the automatically reopening setting
|
|
|
|
*
|
|
|
|
* @return bool If the mbox is reloaded automatically.
|
|
|
|
*
|
|
|
|
* @see Mail_Mbox::$autoReopen
|
|
|
|
*/
|
|
|
|
function getAutoReopen()
|
|
|
|
{
|
|
|
|
return $this->autoReopen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
?>
|