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.11 2004/10/07 14:04:55 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. class Mail_Mbox extends PEAR
  146. {
  147.     /**
  148.     * Resources data like file name, file resource, mbox number, and other
  149.     * cacheds things are stored here.
  150.     *
  151.     * Note that it isnt really a valid resource type. It is of array type.
  152.     *
  153.     * @var      array 
  154.     * @access   private
  155.     */
  156.     var $_resources;
  157.  
  158.     /**
  159.     * Debug mode
  160.     *
  161.     * Set to true to turn on debug mode
  162.     *
  163.     * @var      bool 
  164.     * @access   public
  165.     */
  166.     var $debug = false;
  167.  
  168.     /**
  169.       * Open a Mbox
  170.       *
  171.       * Open the Mbox file and return an resource identificator.
  172.       *
  173.       * Also, this function will process the Mbox and create a cache
  174.       * that tells each message start and end bytes.
  175.       * 
  176.       * @param  int $file   Mbox file to open
  177.       * @return mixed       ResourceID on success else pear error class
  178.       * @access public
  179.       */
  180.     function open($file)
  181.     {
  182.         // check if file exists else return pear error
  183.         if (!file_exists($file)) {
  184.             return PEAR::raiseError("Cannot open the mbox file: file doesnt exists.");
  185.         }
  186.  
  187.         // getting next resource it to set
  188.         $resourceId sizeof($this->_resources+ 1;
  189.  
  190.         // setting filename to the resource id
  191.         $this->_resources[$resourceId]["filename"$file;
  192.  
  193.         // opening the file
  194.         $this->_resources[$resourceId]["fresource"fopen($file"r");
  195.         if (!is_resource($this->_resources[$resourceId]["fresource"])) {
  196.             return PEAR::raiseError("Cannot open the mbox file: maybe without permission.");
  197.         }
  198.  
  199.         // process the file and get the messages bytes offsets
  200.         $this->_process($resourceId);
  201.  
  202.         return $resourceId;
  203.     }
  204.  
  205.     /**
  206.     * Close a Mbox
  207.     *
  208.     * Close the Mbox file opened by open()
  209.     *
  210.     * @param    int $resourceId     Mbox resouce id created by open
  211.     * @return   mixed               true on success else pear error class
  212.     * @access   public
  213.     */
  214.     function close($resourceId)
  215.     {
  216.         if (!is_resource($this->_resources[$resourceId]["fresource"])) {
  217.             return PEAR::raiseError("Cannot close the mbox file because it wanst open.");
  218.         }
  219.  
  220.         if (!fclose($this->_resources[$resourceId]["fresource"])) {
  221.             return PEAR::raiseError("Cannot close the mbox, maybe file is being used (?)");
  222.         }
  223.  
  224.         return true;
  225.     }
  226.  
  227.     /**
  228.     * Mbox Size
  229.     * 
  230.     * Get Mbox Number of Messages
  231.     *
  232.     * @param    int $resourceId     Mbox resouce id created by open
  233.     * @return   int                 Number of messages on Mbox (starting on 1,
  234.     *                                0 if no message exists)
  235.     * @access   public
  236.     */
  237.     function size($resourceId)
  238.     {
  239.         if (array_key_exists('messages'$this->_resources[$resourceId])) {
  240.             return sizeof($this->_resources[$resourceId]["messages"]);
  241.         else {
  242.             return 0;
  243.         }
  244.     }    
  245.  
  246.     /**
  247.     * Mbox Get
  248.     *
  249.     * Get a Message from Mbox
  250.     *
  251.     * Note: Message number start from 0.
  252.     *
  253.     * @param    int $resourceId     Mbox resouce id created by open
  254.     * @param    int $message        The number of Message
  255.     * @return   string              Return the message else pear error class
  256.     * @access   public
  257.     */
  258.     function get($resourceId$message)
  259.     {
  260.         // checking if we have bytes locations for this message
  261.         if (!is_array($this->_resources[$resourceId]["messages"][$message])) {
  262.             return PEAR::raiseError("Message doesnt exists.");
  263.         }
  264.  
  265.         // getting bytes locations
  266.         $bytesStart $this->_resources[$resourceId]["messages"][$message][0];
  267.         $bytesEnd $this->_resources[$resourceId]["messages"][$message][1];
  268.  
  269.         // a debug feature to show the bytes locations
  270.         if ($this->debug{
  271.             printf("%08d=%08d<br />"$bytesStart$bytesEnd);
  272.         }
  273.  
  274.         // seek to start of message
  275.         if (@fseek($this->_resources[$resourceId]["fresource"]$bytesStart== -1{
  276.             return PEAR::raiseError("Cannot read message bytes");
  277.         }
  278.  
  279.         if ($bytesEnd $bytesStart > 0{
  280.             // reading and returning message (bytes to read = difference of bytes locations)
  281.             $msg fread($this->_resources[$resourceId]["fresource"],
  282.                          $bytesEnd $bytesStart"\n";
  283.             return $msg;
  284.         }
  285.     }
  286.  
  287.     /**
  288.     * Delete Message
  289.     *
  290.     * Remove a message from Mbox and save it.
  291.     *
  292.     * Note: messages start with 0.
  293.     *
  294.     * @param    int $resourceId     Mbox resouce id created by open
  295.     * @param    int $message        The number of Message to remove, or
  296.     *                                array of message ids to remove
  297.     * @return   mixed               Return true else pear error class
  298.     * @access   public
  299.     */
  300.     function remove($resourceId$message)
  301.     {
  302.         // convert single message to array
  303.         if (!is_array($message)) {
  304.             $message = array($message);
  305.         }
  306.  
  307.         // checking if we have bytes locations for this message
  308.         foreach ($message as $msg{
  309.             if (!is_array($this->_resources[$resourceId]["messages"][$msg])) {
  310.                 return PEAR::raiseError("Message $msg doesn't exist.");
  311.             }
  312.         }
  313.  
  314.         // changing umask for security reasons
  315.         $umaskOld   umask(077);
  316.         // creating temp file
  317.         $ftempname  tempnam ("/tmp"rand(09));
  318.         // returning to old umask
  319.         umask($umaskOld);
  320.  
  321.         $ftemp      fopen($ftempname"w");
  322.         if ($ftemp == false{
  323.             return PEAR::raiseError("Cannot create a temp file. Cannot handle this error.");            
  324.         }
  325.  
  326.         // writing only undeleted messages 
  327.         $messages $this->size($resourceId);
  328.  
  329.         for ($x = 0; $x $messages$x++{
  330.             if (in_array($x$message)) {
  331.                 continue;    
  332.             }
  333.  
  334.             $messageThis $this->get($resourceId$x);
  335.             if (is_string($messageThis)) {
  336.                 fwrite($ftemp$messageThisstrlen($messageThis));
  337.             }
  338.         }
  339.  
  340.         // closing file
  341.         $filename $this->_resources[$resourceId]["filename"];
  342.         $this->close($resourceId);
  343.         fclose($ftemp);
  344.  
  345.         return $this->_move($resourceId$ftempname$filename);
  346.     }
  347.  
  348.     /**
  349.     * Update a message
  350.     *
  351.     * Note: Mail_Mbox auto adds \n\n at end of the message
  352.     *
  353.     * Note: messages start with 0.
  354.     *
  355.     * @param    int $resourceId     Mbox resouce id created by open
  356.     * @param    int $message        The number of Message to updated
  357.     * @param    string $content     The new content of the Message
  358.     * @return   mixed               Return true else pear error class
  359.     * @access   public
  360.     */
  361.     function update($resourceId$message$content)
  362.     {
  363.         // checking if we have bytes locations for this message
  364.         if (!is_array($this->_resources[$resourceId]["messages"][$message])) {
  365.             return PEAR::raiseError("Message doesnt exists.");
  366.         }
  367.  
  368.         // creating temp file
  369.         $ftempname  tempnam ("/tmp"rand(09));
  370.         $ftemp fopen($ftempname"w");
  371.         if ($ftemp == false{
  372.             return PEAR::raiseError("Cannot create a temp file. Cannot handle this error.");
  373.         }
  374.  
  375.         // writing only undeleted messages
  376.         $messages $this->size($resourceId);
  377.  
  378.         for ($x = 0; $x $messages$x++{
  379.             if ($x == $message{
  380.                 $messageThis $content "\n\n";
  381.             else {
  382.                 $messageThis $this->get($resourceId$x);
  383.             }
  384.  
  385.             if (is_string($messageThis)) {
  386.                 fwrite($ftemp$messageThisstrlen($messageThis));
  387.             }
  388.         }
  389.  
  390.         // closing file
  391.         $filename $this->_resources[$resourceId]["filename"];
  392.         $this->close($resourceId);
  393.         fclose($ftemp);
  394.  
  395.         return $this->_move($resourceId$ftempname$filename);
  396.     }
  397.  
  398.     /**
  399.     * Insert a message
  400.     *
  401.     * PEAR::Mail_Mbox will insert the message according its offset.
  402.     * 0 means before the actual message 0. 3 means before the message 3
  403.     * (Remember: message 3 is the forth message). The default is put
  404.     * AFTER the last message.
  405.     *
  406.     * Note: PEAR::Mail_Mbox auto adds \n\n at end of the message
  407.     *
  408.     * @param    int $resourceId     Mbox resouce id created by open
  409.     * @param    string $content     The content of the new Message
  410.     * @param    int offset          Before the offset. Default: last message
  411.     * @return   mixed               Return true else pear error class
  412.     * @access   public
  413.     */
  414.     function insert($resourceId$content$offset = NULL)
  415.     {
  416.         // checking if we have bytes locations for this message
  417.         if (!is_array($this->_resources[$resourceId])) {
  418.             return PEAR::raiseError("ResourceId doesnt exists.");
  419.         }
  420.  
  421.         // creating temp file
  422.         $ftempname  tempnam ("/tmp"rand(09));
  423.         $ftemp fopen($ftempname"w");
  424.         if ($ftemp == false{
  425.             return PEAR::raiseError("Cannot create a temp file. Cannot handle this error.");
  426.         }
  427.  
  428.         // writing only undeleted messages
  429.         $messages $this->size($resourceId);
  430.         $content .= "\n\n";
  431.  
  432.         if ($messages == 0 && $offset !== NULL{
  433.             fwrite($ftemp$contentstrlen($content));
  434.         else {
  435.             for ($x = 0; $x $messages$x++)  {
  436.                 if ($offset !== NULL && $x == $offset{
  437.                     fwrite($ftemp$contentstrlen($content));
  438.                 }
  439.                 $messageThis $this->get($resourceId$x);
  440.     
  441.                 if (is_string($messageThis)) {
  442.                     fwrite($ftemp$messageThisstrlen($messageThis));
  443.                 }
  444.             }
  445.         }
  446.  
  447.         if ($offset === NULL{
  448.             fwrite($ftemp$contentstrlen($content));
  449.         }
  450.  
  451.         // closing file
  452.         $filename $this->_resources[$resourceId]["filename"];
  453.         $this->close($resourceId);
  454.         fclose($ftemp);
  455.  
  456.         return $this->_move($resourceId$ftempname$filename);
  457.     }
  458.  
  459.     /**
  460.     * Copy a file to another
  461.     *
  462.     * Used internally to copy the content of the temp file to the mbox file
  463.     *
  464.     * @parm     int $resourceId     Resource file
  465.     * @parm     string $ftempname   Source file - will be removed
  466.     * @param    string $filename    Output file
  467.     * @access   private
  468.     */
  469.     function _move($resourceId$ftempname$filename
  470.     {
  471.         // opening ftemp to read
  472.         $ftemp fopen($ftempname"r");
  473.  
  474.         if ($ftemp == false{
  475.             return PEAR::raiseError("Cannot open temp file.");
  476.         }
  477.  
  478.         // copy from ftemp to fp
  479.         $fp @fopen($filename"w");
  480.         if ($fp == false{
  481.             return PEAR::raiseError("Cannot write on mbox file.");
  482.         }
  483.  
  484.         while (feof($ftemp!= true{
  485.             $strings fread($ftemp4096);
  486.             if (fwrite($fp$stringsstrlen($strings)) === false{
  487.                 return PEAR::raiseError("Cannot write to file.");
  488.             }
  489.         }
  490.  
  491.         fclose($fp);
  492.         fclose($ftemp);
  493.         unlink($ftempname);
  494.  
  495.         // open another resource and substitute it to the old one
  496.         $mid $this->open($filename);
  497.         $this->_resources[$resourceId$this->_resources[$mid];
  498.         unset($this->_resources[$mid]);
  499.  
  500.         return true;
  501.     }
  502.  
  503.     /**
  504.     * Process the Mbox
  505.     *
  506.     * Roles:
  507.     * - Count the messages
  508.     * - Get start bytes and end bytes of each messages
  509.     *
  510.     * @param    int $resourceId     Mbox resouce id created by open
  511.     * @access   private
  512.     */
  513.     function _process($resourceId)
  514.     {
  515.         // sanity check
  516.         if (!is_resource($this->_resources[$resourceId]["fresource"])) {
  517.             return PEAR::raiseError("Resource isn't valid.");
  518.         }
  519.  
  520.         // going to start
  521.         if (@fseek($this->_resources[$resourceId]["fresource"]0== -1{
  522.             return PEAR::raiseError("Cannot read mbox");
  523.         }
  524.  
  525.         // starting values
  526.         $bytes = 0;
  527.         $bytesEnd = 0;
  528.         $lines = 0;
  529.  
  530.         $lineThis '';
  531.         unset($lineLast);
  532.  
  533.         while (feof($this->_resources[$resourceId]["fresource"]!= true{
  534.             // getting char by char
  535.             $c fgetc($this->_resources[$resourceId]["fresource"]);
  536.             $lineThis .= $c;
  537.             // each \n we will check things 
  538.             if ($c === "\n"{
  539.                 // checking if start with From
  540.                 if (substr($lineThis05=== "From "{
  541.                     // this line byte count is last line more 1 byte
  542.                     $bytesStart $bytesEnd + 1;
  543.                     // last line byte count is this line bytes minus this line length
  544.                     $bytesEnd $bytes strlen($lineThis);
  545.                     // we will check messages after they end
  546.                     if ($bytesStart != 1{
  547.                         if ($this->debug{
  548.                             printf("#################### from byte %08d to byte %08d ################### <br />"$bytesStart$bytesEnd);
  549.                         }
  550.  
  551.                         // setting new message points
  552.                         $messagesCount $this->size($resourceId);
  553.                         $this->_resources[$resourceId]["messages"][$messagesCount][0$bytesStart;
  554.                         $this->_resources[$resourceId]["messages"][$messagesCount][1$bytesEnd;
  555.                     }
  556.                 }
  557.  
  558.                 // increasing number of lines (doesn't matter)
  559.                 if ($this->debug{
  560.                     $lines++;
  561.                 }
  562.  
  563.                 // last line is this line
  564.                 $lineLast $lineThis;
  565.  
  566.                 // this line is blank now
  567.                 $lineThis '';
  568.                 if ($this->debug{
  569.                     printf("%08d:%08d %s<br/>"$lines$bytes$lineLast);
  570.                 }
  571.             }
  572.             $bytes++;
  573.         }
  574.         // last message must be made here - again same things - 
  575.  
  576.         // this line byte count is last line more 1 byte
  577.         $bytesStart $bytesEnd + 1;
  578.         // last line byte count is this line bytes minus this line length
  579.         $bytesEnd $bytes strlen($lineThis- 2;
  580.         // we will check messages after they end
  581.  
  582.         if ($bytesEnd > 0{
  583.             $messagesCount $this->size($resourceId);
  584.             $this->_resources[$resourceId]["messages"][$messagesCount][0$bytesStart;
  585.             $this->_resources[$resourceId]["messages"][$messagesCount][1$bytesEnd;
  586.         }
  587.     }    
  588. }
  589.   
  590. ?>

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