Mail
[ class tree: Mail ] [ index: Mail ] [ 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.13 2004/10/19 16:41:04 darkelder Exp $
  20.  
  21. require_once "PEAR.php";
  22.  
  23.     /**
  24.      * Mbox PHP class to Unix MBOX parsing and using
  25.      * 
  26.      * 
  27.      * METHODS:
  28.      * int resource mbox->open(string file)
  29.      *   open a mbox and return a resource id
  30.      *
  31.      * bool mbox->close(resource)
  32.      *   close a mbox resource id
  33.      *
  34.      * int mbox->size(resource)
  35.      *   return mbox number of messages
  36.      *
  37.      * string mbox->get(int resource, messageNumber)
  38.      *   return the message number of the resource
  39.      *
  40.      * bool mbox->update(int resource, int messageNumber, string message)
  41.      *   update the message offset to message (need write permission)
  42.      *
  43.      * bool mbox->remove(int resource, int messageNumber)
  44.      *   remove the message messageNumber (need write permission)
  45.      *
  46.      * bool mbox->insert(int resource, string message[, $offset = null])
  47.      *   add message to the end of the mbox. Offset == 0 message will
  48.      *   be append at first message. If after == null will be the last
  49.      *   one message. (need write permission)
  50.      *
  51.      * RELATED LINKS:
  52.      * - CPAN Perl Mail::Folder::Mbox Module
  53.      *   Used as a start point to create this class.
  54.      *   http://search.cpan.org/author/KJOHNSON/MailFolder-0.07/Mail/Folder/Mbox.pm
  55.      *
  56.      * - PHP Mime Decode PEAR Module
  57.      *   Use it to parse headers and body.
  58.      *   http://pear.php.net/package-info.php?pacid=21
  59.      *
  60.      * EXAMPLE:
  61.      *    // some random content
  62.      *    $content = <<<EOF
  63.      *From Foo@example.com Fri Dec 27 14:31:10 2002
  64.      *Return-Path:
  65.      *Received: from [unix socket] by campos.example.com (LMTP); Fri, 27 Dec
  66.      *    2002 14:31:10 -0200 (BRST)
  67.      *Date: Fri, 27 Dec 2002 14:31:21 -0500
  68.      *Message-Id: <200212271931.gBRJVL012289@example.com>
  69.      *Received: from  pcp128525pcs.foo.example.com (
  70.      *    pcp128525pcs.example.com [99.99.99.99]) by/
  71.      *    serjolen6com.example.com (v64.19) with ESMTP id
  72.      *    MAILRELAYINZA98-3601058302; Fri, 08 Nov 2002 06:39:05 -0500
  73.      *From: "Foo@example.com"
  74.      *To: fool@example.com
  75.      *Subject: This is A SPAM!!
  76.      *Content-Type: text/plan
  77.      *
  78.      *testing foo spam
  79.      *EOF;
  80.      *
  81.      *    // starting mbox
  82.      *    require_once "mbox.php";
  83.      *    $mbox    =      new Mail_Mbox();
  84.      *
  85.      *    // uncomment to see lots of things
  86.      *    #$mbox->debug    = true;
  87.      *
  88.      *    // opennign file mbox
  89.      *    $mid     =     $mbox->open("mbox");
  90.      *
  91.      *    // uncomment to see internal vars
  92.      *    #print_r($mbox);
  93.      *
  94.      *
  95.      *
  96.      *    // deleting a message (uncomment to test)
  97.      *    #$res1 =  $mbox->remove($mid,0);
  98.      *    if (PEAR::isError($res1))
  99.      *    {
  100.      *        print $res1->getMessage();
  101.      *    }
  102.      *
  103.      *
  104.      *
  105.      *
  106.      *
  107.      *        // changing a message (uncomment to test)
  108.      *    #$res2 = $mbox->update($mid,0,$content);
  109.      *        if (PEAR::isError($res2))
  110.      *        {
  111.      *                print $res2->getMessage();
  112.      *        }
  113.      *
  114.      *
  115.      *        // adding a message (uncomment to test)
  116.      *        $res3 = $mbox->insert($mid,$content,0);
  117.      *        if (PEAR::isError($res3))
  118.      *        {
  119.      *                print $res3->getMessage();
  120.      *        }
  121.      *
  122.      *
  123.      *
  124.      *    require_once "Mail/mimeDecode.php";
  125.      *    // showing current messages with Mail Mime
  126.      *    for ($x = 0; $x < $mbox->size($mid); $x++)
  127.      *    {
  128.      *        printf("Message: %08d<pre>",$x);
  129.      *        $thisMessage     = $mbox->get($mid,$x);
  130.      *        print $thisMessage;
  131.      *        print "<hr />";
  132.      *        $decode = new Mail_mimeDecode($thisMessage, "\r\n");
  133.      *        $structure = $decode->decode();
  134.      *        print_r($structure);
  135.      *
  136.      *        print "</pre><hr /><hr /><hr />";
  137.      *    }
  138.      *
  139.      *
  140.      *
  141.      * @author   Roberto Berto <darkelder@php.net>
  142.      * @package  Mail
  143.      * @access   public
  144.      */
  145.  
  146. class Mail_Mbox extends PEAR
  147. {
  148.     /**
  149.     * Resources data like file name, file resource, mbox number, and other
  150.     * cacheds things are stored here.
  151.     *
  152.     * Note that it isnt really a valid resource type. It is of array type.
  153.     *
  154.     * @var      array 
  155.     * @access   private
  156.     */
  157.     var $_resources;
  158.  
  159.     /**
  160.      * Debug mode
  161.      *
  162.      * Set to true to turn on debug mode
  163.      *
  164.      * @var      bool 
  165.      * @access   public
  166.      */
  167.      var $debug = false;
  168.  
  169.     /**
  170.      * Open a Mbox
  171.      *
  172.      * Open the Mbox file and return an resource identificator.
  173.      *
  174.      * Also, this function will process the Mbox and create a cache
  175.      * that tells each message start and end bytes.
  176.      * 
  177.      * @param  int $file   Mbox file to open
  178.      * @return mixed       ResourceID on success else pear error class
  179.      * @access public
  180.      */
  181.     function open($file)
  182.     {
  183.         // check if file exists else return pear error
  184.         if (!file_exists($file)) {
  185.             return PEAR::raiseError("Cannot open the mbox file: file doesnt exists.");
  186.         }
  187.  
  188.         // getting next resource it to set
  189.         $resourceId sizeof($this->_resources+ 1;
  190.  
  191.         // setting filename to the resource id
  192.         $this->_resources[$resourceId]["filename"$file;
  193.  
  194.         // opening the file
  195.         $this->_resources[$resourceId]["fresource"fopen($file"r");
  196.         if (!is_resource($this->_resources[$resourceId]["fresource"])) {
  197.             return PEAR::raiseError("Cannot open the mbox file: maybe without permission.");
  198.         }
  199.  
  200.         // process the file and get the messages bytes offsets
  201.         $this->_process($resourceId);
  202.  
  203.         return $resourceId;
  204.     }
  205.  
  206.     /**
  207.      * Close a Mbox
  208.      *
  209.      * Close the Mbox file opened by open()
  210.      *
  211.      * @param    int $resourceId     Mbox resouce id created by open
  212.      * @return   mixed               true on success else pear error class
  213.      * @access   public
  214.      */
  215.     function close($resourceId)
  216.     {
  217.         if (!is_resource($this->_resources[$resourceId]["fresource"])) {
  218.             return PEAR::raiseError("Cannot close the mbox file because it wanst open.");
  219.         }
  220.  
  221.         if (!fclose($this->_resources[$resourceId]["fresource"])) {
  222.             return PEAR::raiseError("Cannot close the mbox, maybe file is being used (?)");
  223.         }
  224.  
  225.         return true;
  226.     }
  227.  
  228.     /**
  229.      * Mbox Size
  230.      * 
  231.      * Get Mbox Number of Messages
  232.      *
  233.      * @param    int $resourceId     Mbox resouce id created by open
  234.      * @return   int                 Number of messages on Mbox (starting on 1,
  235.      *                                0 if no message exists)
  236.      * @access   public
  237.      */
  238.     function size($resourceId)
  239.     {
  240.         if (array_key_exists('messages'$this->_resources[$resourceId])) {
  241.             return sizeof($this->_resources[$resourceId]["messages"]);
  242.         else {
  243.             return 0;
  244.         }
  245.     }    
  246.  
  247.     /**
  248.      * Mbox Get
  249.      *
  250.      * Get a Message from Mbox
  251.      *
  252.      * Note: Message number start from 0.
  253.      *
  254.      * @param    int $resourceId     Mbox resouce id created by open
  255.      * @param    int $message        The number of Message
  256.      * @return   string              Return the message else pear error class
  257.      * @access   public
  258.      */
  259.     function get($resourceId$message)
  260.     {
  261.         // checking if we have bytes locations for this message
  262.         if (!is_array($this->_resources[$resourceId]["messages"][$message])) {
  263.             return PEAR::raiseError("Message doesnt exists.");
  264.         }
  265.  
  266.         // getting bytes locations
  267.         $bytesStart $this->_resources[$resourceId]["messages"][$message][0];
  268.         $bytesEnd $this->_resources[$resourceId]["messages"][$message][1];
  269.  
  270.         // a debug feature to show the bytes locations
  271.         if ($this->debug{
  272.             printf("%08d=%08d<br />"$bytesStart$bytesEnd);
  273.         }
  274.  
  275.         // seek to start of message
  276.         if (@fseek($this->_resources[$resourceId]["fresource"]$bytesStart== -1{
  277.             return PEAR::raiseError("Cannot read message bytes");
  278.         }
  279.  
  280.         if ($bytesEnd $bytesStart > 0{
  281.             // reading and returning message (bytes to read = difference of bytes locations)
  282.             $msg fread($this->_resources[$resourceId]["fresource"],
  283.                          $bytesEnd $bytesStart"\n";
  284.             return $msg;
  285.         }
  286.     }
  287.  
  288.     /**
  289.      * Delete Message
  290.      *
  291.      * Remove a message from Mbox and save it.
  292.      *
  293.      * Note: messages start with 0.
  294.      *
  295.      * @param    int $resourceId     Mbox resouce id created by open
  296.      * @param    int $message        The number of Message to remove, or
  297.      *                                array of message ids to remove
  298.      * @return   mixed               Return true else pear error class
  299.      * @access   public
  300.      */
  301.     function remove($resourceId$message)
  302.     {
  303.         // convert single message to array
  304.         if (!is_array($message)) {
  305.             $message = array($message);
  306.         }
  307.  
  308.         // checking if we have bytes locations for this message
  309.         foreach ($message as $msg{
  310.             if (!is_array($this->_resources[$resourceId]["messages"][$msg])) {
  311.                 return PEAR::raiseError("Message $msg doesn't exist.");
  312.             }
  313.         }
  314.  
  315.         // changing umask for security reasons
  316.         $umaskOld   umask(077);
  317.         // creating temp file
  318.         $ftempname  tempnam ("/tmp"rand(09));
  319.         // returning to old umask
  320.         umask($umaskOld);
  321.  
  322.         $ftemp      fopen($ftempname"w");
  323.         if ($ftemp == false{
  324.             return PEAR::raiseError("Cannot create a temp file. Cannot handle this error.");            
  325.         }
  326.  
  327.         // writing only undeleted messages 
  328.         $messages $this->size($resourceId);
  329.  
  330.         for ($x = 0; $x $messages$x++{
  331.             if (in_array($x$message)) {
  332.                 continue;    
  333.             }
  334.  
  335.             $messageThis $this->get($resourceId$x);
  336.             if (is_string($messageThis)) {
  337.                 fwrite($ftemp$messageThisstrlen($messageThis));
  338.             }
  339.         }
  340.  
  341.         // closing file
  342.         $filename $this->_resources[$resourceId]["filename"];
  343.         $this->close($resourceId);
  344.         fclose($ftemp);
  345.  
  346.         return $this->_move($resourceId$ftempname$filename);
  347.     }
  348.  
  349.     /**
  350.      * Update a message
  351.      *
  352.      * Note: Mail_Mbox auto adds \n\n at end of the message
  353.      *
  354.      * Note: messages start with 0.
  355.      *
  356.      * @param    int $resourceId     Mbox resouce id created by open
  357.      * @param    int $message        The number of Message to updated
  358.      * @param    string $content     The new content of the Message
  359.      * @return   mixed               Return true else pear error class
  360.      * @access   public
  361.      */
  362.     function update($resourceId$message$content)
  363.     {
  364.         // checking if we have bytes locations for this message
  365.         if (!is_array($this->_resources[$resourceId]["messages"][$message])) {
  366.             return PEAR::raiseError("Message doesnt exists.");
  367.         }
  368.  
  369.         // creating temp file
  370.         $ftempname  tempnam ("/tmp"rand(09));
  371.         $ftemp fopen($ftempname"w");
  372.         if ($ftemp == false{
  373.             return PEAR::raiseError("Cannot create a temp file. Cannot handle this error.");
  374.         }
  375.  
  376.         // writing only undeleted messages
  377.         $messages $this->size($resourceId);
  378.  
  379.         for ($x = 0; $x $messages$x++{
  380.             if ($x == $message{
  381.                 $messageThis $content "\n\n";
  382.             else {
  383.                 $messageThis $this->get($resourceId$x);
  384.             }
  385.  
  386.             if (is_string($messageThis)) {
  387.                 fwrite($ftemp$messageThisstrlen($messageThis));
  388.             }
  389.         }
  390.  
  391.         // closing file
  392.         $filename $this->_resources[$resourceId]["filename"];
  393.         $this->close($resourceId);
  394.         fclose($ftemp);
  395.  
  396.         return $this->_move($resourceId$ftempname$filename);
  397.     }
  398.  
  399.     /**
  400.      * Insert a message
  401.      *
  402.      * PEAR::Mail_Mbox will insert the message according its offset.
  403.      * 0 means before the actual message 0. 3 means before the message 3
  404.      * (Remember: message 3 is the forth message). The default is put
  405.      * AFTER the last message.
  406.      *
  407.      * Note: PEAR::Mail_Mbox auto adds \n\n at end of the message
  408.      *
  409.      * @param    int $resourceId     Mbox resouce id created by open
  410.      * @param    string $content     The content of the new Message
  411.      * @param    int offset          Before the offset. Default: last message
  412.      * @return   mixed               Return true else pear error class
  413.      * @access   public
  414.      */
  415.     function insert($resourceId$content$offset = NULL)
  416.     {
  417.         // checking if we have bytes locations for this message
  418.         if (!is_array($this->_resources[$resourceId])) {
  419.             return PEAR::raiseError("ResourceId doesnt exists.");
  420.         }
  421.  
  422.         // creating temp file
  423.         $ftempname  tempnam ("/tmp"rand(09));
  424.         $ftemp fopen($ftempname"w");
  425.         if ($ftemp == false{
  426.             return PEAR::raiseError("Cannot create a temp file. Cannot handle this error.");
  427.         }
  428.  
  429.         // writing only undeleted messages
  430.         $messages $this->size($resourceId);
  431.         $content .= "\n\n";
  432.  
  433.         if ($messages == 0 && $offset !== NULL{
  434.             fwrite($ftemp$contentstrlen($content));
  435.         else {
  436.             for ($x = 0; $x $messages$x++)  {
  437.                 if ($offset !== NULL && $x == $offset{
  438.                     fwrite($ftemp$contentstrlen($content));
  439.                 }
  440.                 $messageThis $this->get($resourceId$x);
  441.     
  442.                 if (is_string($messageThis)) {
  443.                     fwrite($ftemp$messageThisstrlen($messageThis));
  444.                 }
  445.             }
  446.         }
  447.  
  448.         if ($offset === NULL{
  449.             fwrite($ftemp$contentstrlen($content));
  450.         }
  451.  
  452.         // closing file
  453.         $filename $this->_resources[$resourceId]["filename"];
  454.         $this->close($resourceId);
  455.         fclose($ftemp);
  456.  
  457.         return $this->_move($resourceId$ftempname$filename);
  458.     }
  459.  
  460.     /**
  461.      * Copy a file to another
  462.      *
  463.      * Used internally to copy the content of the temp file to the mbox file
  464.      *
  465.      * @parm     int $resourceId     Resource file
  466.      * @parm     string $ftempname   Source file - will be removed
  467.      * @param    string $filename    Output file
  468.      * @access   private
  469.      */
  470.     function _move($resourceId$ftempname$filename
  471.     {
  472.         // opening ftemp to read
  473.         $ftemp fopen($ftempname"r");
  474.  
  475.         if ($ftemp == false{
  476.             return PEAR::raiseError("Cannot open temp file.");
  477.         }
  478.  
  479.         // copy from ftemp to fp
  480.         $fp @fopen($filename"w");
  481.         if ($fp == false{
  482.             return PEAR::raiseError("Cannot write on mbox file.");
  483.         }
  484.  
  485.         while (feof($ftemp!= true{
  486.             $strings fread($ftemp4096);
  487.             if (fwrite($fp$stringsstrlen($strings)) === false{
  488.                 return PEAR::raiseError("Cannot write to file.");
  489.             }
  490.         }
  491.  
  492.         fclose($fp);
  493.         fclose($ftemp);
  494.         unlink($ftempname);
  495.  
  496.         // open another resource and substitute it to the old one
  497.         $mid $this->open($filename);
  498.         $this->_resources[$resourceId$this->_resources[$mid];
  499.         unset($this->_resources[$mid]);
  500.  
  501.         return true;
  502.     }
  503.  
  504.     /**
  505.      * Process the Mbox
  506.      *
  507.      * Roles:
  508.      * - Count the messages
  509.      * - Get start bytes and end bytes of each messages
  510.      *
  511.      * @param    int $resourceId     Mbox resouce id created by open
  512.      * @access   private
  513.      */
  514.     function _process($resourceId)
  515.     {
  516.         // sanity check
  517.         if (!is_resource($this->_resources[$resourceId]['fresource'])) {
  518.             return PEAR::raiseError("Resource isn't valid.");
  519.         }
  520.  
  521.         // going to start
  522.         if (@fseek($this->_resources[$resourceId]['fresource']== -1{
  523.             return PEAR::raiseError("Cannot read mbox");
  524.         }
  525.  
  526.         // current start byte position
  527.         $start      = 0;
  528.         // last start byte position
  529.         $laststart  = 0;
  530.         // there aren't any message
  531.         $hasmessage = false;
  532.  
  533.         while ($line fgets($this->_resources[$resourceId]['fresource']4096)) {
  534.             // if line start with "From ", it is a new message
  535.             if (0 === strncmp($line'From '5)) {
  536.                 // save last start byte position
  537.                 $laststart  $start;
  538.         
  539.                 // new start byte position is the start of the line 
  540.                 $start      ftell($this->_resources[$resourceId]['fresource']strlen($line);
  541.  
  542.                 // if it is not the first message add message positions
  543.                 if ($start > 0{
  544.                     $this->_resources[$resourceId]["messages"][= array($laststart$start - 1);
  545.                 else {
  546.                     // tell that there is really a message on the file
  547.                     $hasmessage = true;
  548.                 }
  549.             }
  550.         }
  551.  
  552.         // if there are just one message, or if it's the last one,
  553.         // add it to messages positions
  554.         if (($start == 0 && $hasmessage === true|| ($start > 0)) {
  555.             $this->_resources[$resourceId]["messages"][= array($startftell($this->_resources[$resourceId]['fresource']));
  556.         }
  557.     }
  558. }
  559.   
  560. ?>

Documentation generated on Mon, 11 Mar 2019 13:58:17 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.