Mail_Mbox
[ class tree: Mail_Mbox ] [ index: Mail_Mbox ] [ all elements ]

Source for file Mbox.php

Documentation is available at Mbox.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at the following url:           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Roberto Berto <darkelder.php.net>                           |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Mbox.php,v 1.2 2006/06/25 09:27:36 cweiske Exp $
  20.  
  21. require_once 'PEAR.php';
  22.  
  23. /**
  24. *   Class to read mbox mail files.
  25. */
  26. class Mail_Mbox extends PEAR
  27. {
  28.     /**
  29.     * File resource / handle
  30.     *
  31.     * @var      resource 
  32.     * @access   protected
  33.     */
  34.     var $_resource = null;
  35.  
  36.     /**
  37.      * Message index
  38.      *
  39.      * @var array 
  40.      * @access protected
  41.      */
  42.     var $_index = null;
  43.  
  44.     /**
  45.      * Timestamp at which the file has been modified last
  46.      *
  47.      * @var int 
  48.      * @access protected
  49.      */
  50.     var $_lastModified = null;
  51.  
  52.     /**
  53.      * Debug mode
  54.      *
  55.      * Set to true to turn on debug mode
  56.      *
  57.      * @var      bool 
  58.      * @access   public
  59.      */
  60.     var $debug = false;
  61.  
  62.     /**
  63.      * Directory in which the temporary mbox files are created.
  64.      * Even if it's a unix directory, it does work on windows as
  65.      * the only function it's used in is tempnam which automatically
  66.      * chooses the right temp directory if this here doesn't exist.
  67.      * So this variable is for special needs only.
  68.      *
  69.      * @var      string 
  70.      * @access   public
  71.      */
  72.     var $tmpdir '/tmp';
  73.  
  74.  
  75.  
  76.     /**
  77.      * Create a new Mbox class instance.
  78.      * After creating it, you should use open().
  79.      *
  80.      * @param string $file  Filename to open.
  81.      * @access public
  82.      */
  83.     function Mail_Mbox($file)
  84.     {
  85.         $this->_file $file;
  86.     }
  87.  
  88.     /**
  89.      * Open the mbox file
  90.      *
  91.      * Also, this function will process the Mbox and create a cache
  92.      * that tells each message start and end bytes.
  93.      *
  94.      * @access public
  95.      */
  96.     function open()
  97.     {
  98.         // check if file exists else return pear error
  99.         if (!file_exists($this->_file)) {
  100.             return PEAR::raiseError('Cannot open the mbox file "' $this->_file '": file does not exist.');
  101.         }
  102.  
  103.         // opening the file
  104.         $this->_lastModified filemtime($this->_file);
  105.         $this->_resource fopen($this->_file'r');
  106.         if (!is_resource($this->_resource)) {
  107.             return PEAR::raiseError('Cannot open the mbox file: maybe without permission.');
  108.         }
  109.  
  110.         // process the file and get the messages bytes offsets
  111.         $this->_process();
  112.  
  113.         return true;
  114.     }
  115.  
  116.     /**
  117.      * Close a Mbox
  118.      *
  119.      * Close the Mbox file opened by open()
  120.      *
  121.      * @return   mixed       true on success, else PEAR_Error
  122.      * @access   public
  123.      */
  124.     function close()
  125.     {
  126.         if (!is_resource($this->_resource)) {
  127.             return PEAR::raiseError('Cannot close the mbox file because it was not open.');
  128.         }
  129.  
  130.         if (!fclose($this->_resource)) {
  131.             return PEAR::raiseError('Cannot close the mbox, maybe file is being used (?)');
  132.         }
  133.  
  134.         return true;
  135.     }
  136.  
  137.     /**
  138.      * Get number of messages in this mbox
  139.      *
  140.      * @return   int                 Number of messages on Mbox (starting on 1,
  141.      *                                0 if no message exists)
  142.      * @access   public
  143.      */
  144.     function size()
  145.     {
  146.         if ($this->_index !== null{
  147.             return sizeof($this->_index);
  148.         else {
  149.             return 0;
  150.         }
  151.     }
  152.  
  153.     /**
  154.      * Get a message from the mbox
  155.      *
  156.      * Note: Message number start from 0.
  157.      *
  158.      * @param    int $message        The number of Message
  159.      * @return   string              Return the message, PEAR_Error on error
  160.      * @access   public
  161.      */
  162.     function get($message)
  163.     {
  164.         // checking if we have bytes locations for this message
  165.         if (!is_array($this->_index[$message])) {
  166.             return PEAR::raiseError('Message does not exist.');
  167.         }
  168.  
  169.         // getting bytes locations
  170.         $bytesStart $this->_index[$message][0];
  171.         $bytesEnd $this->_index[$message][1];
  172.  
  173.         // a debug feature to show the bytes locations
  174.         if ($this->debug{
  175.             printf("%08d=%08d<br />"$bytesStart$bytesEnd);
  176.         }
  177.  
  178.         // seek to start of message
  179.         if (fseek($this->_resource$bytesStart== -1{
  180.             return PEAR::raiseError('Cannot read message bytes');
  181.         }
  182.  
  183.         if ($bytesEnd $bytesStart > 0{
  184.             // reading and returning message (bytes to read = difference of bytes locations)
  185.             $msg fread($this->_resource$bytesEnd $bytesStart"\n";
  186.             return $msg;
  187.         }
  188.     }
  189.  
  190.     /**
  191.      * Remove a message from Mbox and save it.
  192.      *
  193.      * Note: messages start with 0.
  194.      *
  195.      * @param    int $message        The number of the message to remove, or
  196.      *                                array of message ids to remove
  197.      * @return   mixed               Return true else PEAR_Error
  198.      * @access   public
  199.      */
  200.     function remove($message)
  201.     {
  202.         if ($this->hasBeenModified()) {
  203.             return PEAR::raiseError('File has been modified since loading. Re-open the file.');
  204.         }
  205.  
  206.         // convert single message to array
  207.         if (!is_array($message)) {
  208.             $message = array($message);
  209.         }
  210.  
  211.         // checking if we have bytes locations for this message
  212.         foreach ($message as $msg{
  213.             if (!isset($this->_index[$msg]|| !is_array($this->_index[$msg])) {
  214.                 return PEAR::raiseError('Message ' $msg 'does not exist.');
  215.             }
  216.         }
  217.  
  218.         // changing umask for security reasons
  219.         $umaskOld   umask(077);
  220.         // creating temp file
  221.         $ftempname  tempnam($this->tmpdir'Mail_Mbox');
  222.         // returning to old umask
  223.         umask($umaskOld);
  224.  
  225.         $ftemp      fopen($ftempname'w');
  226.         if ($ftemp === false{
  227.             return PEAR::raiseError('Cannot create a temp file "' $ftempname '". Cannot handle this error.');
  228.         }
  229.  
  230.         // writing only undeleted messages 
  231.         $messages $this->size();
  232.  
  233.         for ($x = 0; $x $messages$x++{
  234.             if (in_array($x$message)) {
  235.                 continue;
  236.             }
  237.  
  238.             $messageThis $this->get($x);
  239.             if (is_string($messageThis)) {
  240.                 fwrite($ftemp$messageThisstrlen($messageThis));
  241.             }
  242.         }
  243.  
  244.         // closing file
  245.         $this->close();
  246.         fclose($ftemp);
  247.  
  248.         return $this->_move($ftempname$this->_file);
  249.     }
  250.  
  251.     /**
  252.      * Update a message
  253.      *
  254.      * Note: Mail_Mbox auto adds \n\n at end of the message
  255.      *
  256.      * Note: messages start with 0.
  257.      *
  258.      * @param    int $message        The number of Message to updated
  259.      * @param    string $content     The new content of the Message
  260.      * @return   mixed               Return true if all is ok, else PEAR_Error
  261.      * @access   public
  262.      */
  263.     function update($message$content)
  264.     {
  265.         if ($this->hasBeenModified()) {
  266.             return PEAR::raiseError('File has been modified since loading. Re-open the file.');
  267.         }
  268.  
  269.         // checking if we have bytes locations for this message
  270.         if (!is_array($this->_index[$message])) {
  271.             return PEAR::raiseError('Message does not exists.');
  272.         }
  273.  
  274.         // creating temp file
  275.         $ftempname  tempnam($this->tmpdir'Mail_Mbox');
  276.         $ftemp fopen($ftempname'w');
  277.         if ($ftemp === false{
  278.             return PEAR::raiseError('Cannot create temp file "' $ftempname '" . Cannot handle this error.');
  279.         }
  280.  
  281.         $messages $this->size();
  282.  
  283.         for ($x = 0; $x $messages$x++{
  284.             if ($x == $message{
  285.                 $messageThis $content "\n\n";
  286.             else {
  287.                 $messageThis $this->get($x);
  288.             }
  289.  
  290.             if (is_string($messageThis)) {
  291.                 fwrite($ftemp$messageThisstrlen($messageThis));
  292.             }
  293.         }
  294.  
  295.         // closing file
  296.         $this->close();
  297.         fclose($ftemp);
  298.  
  299.         return $this->_move($ftempname$this->_file);
  300.     }
  301.  
  302.     /**
  303.      * Insert a message
  304.      *
  305.      * PEAR::Mail_Mbox will insert the message according its offset.
  306.      * 0 means before the actual message 0. 3 means before the message 3
  307.      * (Remember: message 3 is the forth message). The default is put
  308.      * AFTER the last message (offset = null).
  309.      *
  310.      * Note: PEAR::Mail_Mbox auto adds \n\n at end of the message
  311.      *
  312.      * @param    string $content     The content of the new message
  313.      * @param    int offset          Before the offset. Default: last message (null)
  314.      * @return   mixed               Return true else pear error class
  315.      * @access   public
  316.      */
  317.     function insert($content$offset = null)
  318.     {
  319.         if ($this->hasBeenModified()) {
  320.             return PEAR::raiseError('File has been modified since loading. Re-open the file.');
  321.         }
  322.  
  323.         if ($offset === -1{
  324.             $offset = null;
  325.         }
  326.  
  327.         // creating temp file
  328.         $ftempname  tempnam($this->tmpdir'Mail_Mbox');
  329.         $ftemp fopen($ftempname'w');
  330.         if ($ftemp === false{
  331.             return PEAR::raiseError('Cannot create temp file "' $ftempname '". Cannot handle this error.');
  332.         }
  333.  
  334.         // writing only undeleted messages
  335.         $messages $this->size();
  336.         $content .= "\n\n";
  337.  
  338.         if ($messages == 0 && $offset !== null{
  339.             fwrite($ftemp$contentstrlen($content));
  340.         else {
  341.             for ($x = 0; $x $messages$x++)  {
  342.                 if ($offset !== null && $x == $offset{
  343.                     fwrite($ftemp$contentstrlen($content));
  344.                 }
  345.                 $messageThis $this->get($x);
  346.  
  347.                 if (is_string($messageThis)) {
  348.                     fwrite($ftemp$messageThisstrlen($messageThis));
  349.                 }
  350.             }
  351.         }
  352.  
  353.         if ($offset === null{
  354.             fwrite($ftemp$contentstrlen($content));
  355.         }
  356.  
  357.         // closing file
  358.         $this->close();
  359.         fclose($ftemp);
  360.  
  361.         return $this->_move($ftempname$this->_file);
  362.     }
  363.  
  364.     /**
  365.      * Copy a file to another
  366.      *
  367.      * Used internally to copy the content of the temp file to the mbox file
  368.      *
  369.      * @parm     string $ftempname   Source file - will be removed
  370.      * @param    string $filename    Output file
  371.      * @access   protected
  372.      */
  373.     function _move($ftempname$filename)
  374.     {
  375.         // opening ftemp to read
  376.         $ftemp fopen($ftempname'r');
  377.  
  378.         if ($ftemp === false{
  379.             return PEAR::raiseError('Cannot open temp file "' $ftempname '".');
  380.         }
  381.  
  382.         // copy from ftemp to fp
  383.         $fp fopen($filename'w');
  384.         if ($fp === false{
  385.             return PEAR::raiseError('Cannot write on mbox file "' $filename '".');
  386.         }
  387.  
  388.         while (feof($ftemp!= true{
  389.             $strings fread($ftemp4096);
  390.             if (fwrite($fp$stringsstrlen($strings)) === false{
  391.                 return PEAR::raiseError('Cannot write to file "' $filename '".');
  392.             }
  393.         }
  394.  
  395.         fclose($fp);
  396.         fclose($ftemp);
  397.         unlink($ftempname);
  398.  
  399.         // open another resource and substitute it to the old one
  400.         $this->_file $filename;
  401.         return $this->open();
  402.     }
  403.  
  404.     /**
  405.      * Process the Mbox
  406.      *
  407.      * - Get start bytes and end bytes of each messages
  408.      *
  409.      * @access   protected
  410.      */
  411.     function _process()
  412.     {
  413.         $this->_index = array();
  414.  
  415.         // sanity check
  416.         if (!is_resource($this->_resource)) {
  417.             return PEAR::raiseError('Resource is not valid. Maybe the file has not be opened?');
  418.         }
  419.  
  420.         // going to start
  421.         if (fseek($this->_resource0== -1{
  422.             return PEAR::raiseError('Cannot read mbox');
  423.         }
  424.  
  425.         // current start byte position
  426.         $start      = 0;
  427.         // last start byte position
  428.         $laststart  = 0;
  429.         // there aren't any message
  430.         $hasmessage = false;
  431.  
  432.         while ($line fgets($this->_resource4096)) {
  433.             // if line start with "From ", it is a new message
  434.             if (0 === strncmp($line'From '5)) {
  435.                 // save last start byte position
  436.                 $laststart  $start;
  437.  
  438.                 // new start byte position is the start of the line 
  439.                 $start      ftell($this->_resourcestrlen($line);
  440.  
  441.                 // if it is not the first message add message positions
  442.                 if ($start > 0{
  443.                     $this->_index[= array($laststart$start - 1);
  444.                 else {
  445.                     // tell that there is really a message on the file
  446.                     $hasmessage = true;
  447.                 }
  448.             }
  449.         }
  450.  
  451.         // if there are just one message, or if it's the last one,
  452.         // add it to messages positions
  453.         if (($start == 0 && $hasmessage === true|| ($start > 0)) {
  454.             $this->_index[= array($startftell($this->_resource));
  455.         }
  456.     }
  457.  
  458.     /**
  459.      * Checks if the file was modified since it has been loaded.
  460.      * If this is true, the file needs to be re-opened.
  461.      *
  462.      * @return boolean  True if it has been modified.
  463.      * @access public
  464.      */
  465.     function hasBeenModified()
  466.     {
  467.         return filemtime($this->_file$this->_lastModified;
  468.     }
  469. }
  470.  
  471. ?>

Documentation generated on Mon, 11 Mar 2019 14:41:28 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.