#native_company# #native_desc#
#native_cta#

POP3.CLASS – message handler library – FINALLY!

By James Brindle
on January 18, 2002

Version: 1.2

Type: Class

Category: Other

License: GNU General Public License

Description: After months of posting messages in the PHPBuilder forums and various other places and getting no response, I finally got around to writing this.

It’s a php class that will connect to a POP3 server, authenticate you and then provide methods to return the list of messages in the mailbox, get a message, split the message into segments (readable text and attachments), each is a subarray.

Example:

$msgData = $pop->GetMessage(n);
$chunks = $pop->SplitMessage($msgData);

$chunks is returned as follows:

$chunks[n] =
Array(“headers” => Array(),
“body” => body);

Example:

$chunks[0][“headers”][“from”] contains the data of who the message was from.
$chunks[0][“headers”][“content-type”] contains the type of data in this segment.

In 99% of cases there will be a “text/plain” or “text/html” segment.

Sorry for lack of documentation, i’ll write that in a later release.

If you need further information, contact me at: [email protected].

If you want a working example of this to follow then mail me and i’ll send you a zip.

All feedback on this highly appreciated.

<?

set_time_limit(500000);

/*
  POP3.CLASS:
	Finally it's here, the shit that's been asked of me for ages, finally I got my head around
	an algorithm to properly decode POP3 mailboxes without tons if type checking.

	Came down to that nasty recursive code your mother told you to avoid being needed.

	This is an extensible "base" API which includes:
		Get Message List
		Get Headers
		Get Message
		Split Message
		Age In Days of message
*/

define("pop3NoProblem",	"+OK");				// Standard OK message from a POP3 server
define("pop3FinalBoundary",	"----POP3_Class_Final_Boundary_". mktime());

class pop3 {

  var $pop_server = "";
  var $pop_port = "";
  var $pop_uid = "";
  var $pop_pass = "";
  var $fhandle = "";
  var $LoggedOn = false;

  function DateDiff($startdate, $enddate) {

    // This code snippet courtessy of "stew" from PHPBuilder.COM

    ## difference between the two in seconds
    $time_period = ( $enddate - $startdate );

    $days = 0;
    $hours = 0;
    $minutes = 0;
    $seconds = 0;

    $time_increments = array('Days' => 86400, 'Hours' => 3600, 'Minutes' => 60, 'Seconds' => 1);

    ## will hold our values for ( day, minute, hour, seconds )

    $time_span = array();

    ## cycle through time_increments

    while (list( $key, $value ) = each( $time_increments )) {
      $this_value = (int) ( $time_period / $value );
      $time_period = ( $time_period % $value );
      $time_span[$key] = $this_value;
    }

    ## Also the difference in minutes between the two - JB
    $time_span["min_diff"] = ($time_span["Days"] * 1440) + ($time_span["Hours"] * 60) + $time_span["Minutes"];

    return $time_span;
  }


  function pop3($pop_server, $pop_port, $pop_uid, $pop_pass) {

    $this->pop_server = 	$pop_server;
    $this->pop_port = 		$pop_port;
    $this->pop_uid = 		$pop_uid;
    $this->pop_pass = 		$pop_pass;

    // return 1 on sucessful connect.

    if ($this->Connect()) { return 1; } else { return 0; }

  }

  function Connect() {
    $this->fhandle = "";
    $fp = fsockopen($this->pop_server, $this->pop_port, $errno, $errstr, 10);
    if (!$fp) {
      echo "Error: POP3/Connect: (Err # $errno) $errstrn";
      return 0;
    } else {
      $this->fhandle = $fp;
      list($status, $msg) = $this->GetString();
      if ($status == pop3NoProblem) {
        return 1;
      } else {
        $this->fhandle = "";
        echo "Error: POP3/Connect: Established port but did not receive standard opening response from server.n";
        echo "Status: $status, Msg: $msgn";
        return 0;
      }
      return 1;
    }
  } // END Connect()

  function Disconnect() {
    if ($this->fhandle) {
      if ($this->LoggedOn) {
        $this->Logoff();
      }
      fclose($this->fhandle);
    }
  } // END Disconnect()

  function SendString($message) {
    if ($this->fhandle) {
      fwrite($this->fhandle, $message);
    }
  } // END SendString()

  function GetString() {
    if ($this->fhandle) {
      $retval = fgets($this->fhandle, 65535);
      $retArr = split(" ", $retval, 2);
    }
    return $retArr;
  } // END GetString()

  function GetDataBlock() {
    $retval = "";
    if ($this->fhandle) {
      $quitflag = false;
      while (!$quitflag) {
        $tmpstr = fgets($this->fhandle, 65535);
        $tmpstr = eregi_replace("n|rn", "", $tmpstr);
        if ($tmpstr == ".") {
          $quitflag = true;
        } else {
          $retval .= "$tmpstrn";
        }
      }
    }
    return $retval;
  }

  function Logon() {
    if ($this->fhandle) {
      if ($this->LoggedOn) { 
        return 1;				// if we're already logged on, just return
      }
      $this->SendString("user ". $this->pop_uid ."rn");
      list($status, $msg) = $this->GetString();
      if ($status == pop3NoProblem) {
        $this->SendString("pass ". $this->pop_pass ."rn");
        list($status, $msg) = $this->GetString();
        if ($status == pop3NoProblem) {
          $this->LoggedOn = true;
          return 1;
        } else {
          echo "Error: POP3/Logon: $status, $msgn";
          return 0;
        }
      } else {
        echo "Error: POP3/Logon: $status, $msgn";
        return 0;
      }  
    }
  } // END Logon()

  function Logoff() {
    if ($this->LoggedOn) {
      $this->SendString("quitrn");
      list($status, $msg) = $this->GetString();
      if ($status == pop3NoProblem) {
        $this->LoggedOn = false;
        return 1;
      } else {
        echo "Error: POP3/Logoff: $status, $msgn";
        return 0;
      }
    }
  } // END Logoff()

  function GetNumberOfMessages() {
    $tmpArr = Array();
    if ($this->Logon()) {
      $this->SendString("statrn");
      list($status, $msg) = $this->GetString();
      if ($status == pop3NoProblem) {
        $retArr = split(" ", $msg);
        $tmpArr["messages"] = $retArr[0];
        $tmpArr["mailbox_size"] = $retArr[1];
        return $tmpArr;
      } else {
        echo "Error: POP3/GetNumberOfMessages(): $status, $msgn";
        return 0;
      }
    } else {
      return 0;
    }
  }  

  function GetHeaders($msgnum) {
    $headArr = Array();
    if ($this->Logon()) {
      $this->SendString("list $msgnumrn");
      $val = $this->GetString();
      $sizeArr = split(" ", $val[1]);
      $headArr["size_of_message"] = trim($sizeArr[1]);
      $this->SendString("top $msgnum 0rn");
      $textblock = $this->GetDataBlock();
      $lineArr = split("n", $textblock);
      for ($t=0; $t<count($lineArr); $t++) {
        if (eregi("^([0-9A-Za-z_]{1,}):", $lineArr[$t])) {
          $tmpArr = split(":", $lineArr[$t], 2);
          $tmpArr[0] = strtolower(trim($tmpArr[0]));
          $tmpArr[1] = trim($tmpArr[1]);
          $headArr[$tmpArr[0]] = $tmpArr[1];
        } elseif (eregi("^ *([0-9A-Za-z_]{1,})", $lineArr[$t])) {
        }
      }
    }
    return $headArr;
  } // END GetHeaders()

  function GetMessageList() {
    $tmpArr = Array();
    if ($this->Logon()) {
      $msgCountArr = $this->GetNumberOfMessages();
      for ($t=0; $t<$msgCountArr["messages"]; $t++) {
        $msgnum = $t+1;
        $headArr = $this->GetHeaders($msgnum);
        $tmpArr[] = $headArr;
      }
    }
    return $tmpArr;
  } // END GetMessageList()

  function ReadMessage($msgnum) {
    if ($this->Logon()) {
      $this->SendString("retr $msgnumrn");
      $textblock = $this->GetDataBlock();
      return $textblock;
    }
  }

  function DeleteMessage($msgnum) {
    if ($this->Logon()) {
      $this->SendString("dele $msgnumrn");
      $respArr = $this->GetString();
    }
  }

  function FindLine($start_at, $regex, $msgblock) {
    $linepos = -1;
    $posfound = false;
    $curpos = $start_at;
    while (!$posfound) {
      $line = $msgblock[$curpos];
      if ($regex != "") {
        if (eregi($regex, $line)) {
          $linepos = $curpos;
          $posfound = true;
        }
      } else {
        if ($line == "") {
          $linepos = $curpos;
          $posfound = true;
        }
      }
      $curpos++;
      if ($curpos >= count($msgblock)) {
        $posfound = true;
      }
    }
    return $linepos;
  } // END FindLine

  function GetStringBlock($start_line, $end_line, $msgArr) {
    $tmpArr = Array();
    for ($x=$start_line; $x<$end_line; $x++) {
      $tmpArr[] = trim($msgArr[$x]);
    }
    return $tmpArr;
  } // END GetStringBlock

  function AssembleTypeArray($start_line, $end_line, $msgArr, $boundarySTR = "") {
    $typeArr = Array();
    $tmpArr = $this->GetStringBlock($start_line, $end_line, $msgArr);
    for ($x=0; $x<count($tmpArr); $x++) {
      $line = $tmpArr[$x];
      $htArr = split(";", $line);
      for ($jj=0; $jj<count($htArr); $jj++) {
        $item = $htArr[$jj];
        if (eregi("[:=]( *)", $item)) {
          $tmpArr2 = split("[:=]( *)", $item, 2);
          $typeArr[strtolower(trim($tmpArr2[0]))] = trim($tmpArr2[1]);
        } elseif (eregi("="", $item)) {
          $tmpArr2 = split("="", $item, 2);
          $tmpArr2[1] = eregi_replace(""$", "", $tmpArr2[1]);
          $typeArr[strtolower(trim($tmpArr2[0]))] = trim($tmpArr2[1]);
        }
      }
    }
    if ($typeArr["name"]) {
      $typeArr["name"] = eregi_replace("^"|"$", "", $typeArr["name"]);
    }
    if ($typeArr["filename"]) {
      $typeArr["filename"] = eregi_replace("^"|"$", "", $typeArr["filename"]);
    }
    if ($typeArr["boundary"]) {
      $typeArr["boundary"] = eregi_replace(""","",$typeArr["boundary"]);
    } else {
      $typeArr["boundary"] = $boundarySTR;
    }
    if (!$typeArr["boundary"]) {
      $typeArr["boundary"] = pop3FinalBoundary;
    }
    if (!$typeArr["content-type"]) {
      $typeArr["content-type"] = "text/plain";
    }
    return $typeArr;
  } // END AssembleTypeArray

  function GetChunksFromBody($parts, $boundary, $level = 1, $headArr = "") {
    $end_chunk = count($parts);
    $chunkArr = Array();
//    echo "GetChunksFromBody[$level]: Entering: $end_chunk segments, Boundary = $boundaryn";
    for ($i=0; $i<$end_chunk; $i++) {
      $parts[$i] = eregi_replace("^(n|rn*)", "", $parts[$i]);
      $mArr = split("n", $parts[$i]);
      if (count($mArr) > 0 & $mArr[0] != "--") {
//        echo "GetChunksFromBody[$level]: First Line oF Segment $i: $mArr[0]n";
        if (eregi("content-type", $mArr[0])) {
//          echo "GetChunksFromBody[$level]: Segment Foundn";
          $head_end = $this->FindLine(0, "", $mArr);
          $headArr = $this->AssembleTypeArray(0, $head_end, $mArr, $boundary);
          $bodyArr = $this->GetStringBlock($head_end + 1, count($mArr), $mArr);
//          echo "GetChunksFromBody[$level]: Content-Type: ". $headArr["content-type"] ."n";
          if ($headArr["boundary"] != $boundary) { // we've got an embedded segment to explode
//            echo "GetChunksFromBody[$level]: Recursing, new boundary = ". $headArr["boundary"] . "n";
            $moreparts = explode("--". $headArr["boundary"], join("n", $bodyArr));
            $extras = $this->GetChunksFromBody($moreparts, $headArr["boundary"], $level+1);
            for ($z=0; $z<count($extras); $z++) {
              $chunkArr[] = $extras[$z];
            }          
          } else {
            $t["headers"] = $headArr;
            $t["body"] = join("n", $bodyArr);
            $chunkArr[] = $t;
          }
        } else {
          if (!$headArr) { $headArr = $this->AssembleTypeArray(0, 1, $mArr, $boundary); }
          $bodyArr = $this->GetStringBlock(0, count($mArr), $mArr);
          $t["headers"] = $headArr;
          $t["body"] = join("n", $bodyArr);
          $chunkArr[] = $t;
        }
      } else {
//        echo "GetChunksFromBody[$level]: No data in segment $in";
      }
    }
//    echo "GetChunksFromBody[$level]: Leavingn";
    return $chunkArr;
  }

  function SplitMessage($msgblock) {
    $chunks = Array();
    $msgArr = split("n", $msgblock);
    $msgArr[] = "--". pop3FinalBoundary."--";

    $first_head_end = $this->FindLine(1, "", $msgArr);
    $hArr = $this->AssembleTypeArray(0, $first_head_end, $msgArr);
    $body = join("n", $this->GetStringBlock($first_head_end + 1, count($msgArr) -1, $msgArr));

    $parts = explode("--". $hArr["boundary"], $body);

    $chunks = $this->GetChunksFromBody($parts, $hArr["boundary"], 1, $hArr);
    
    return $chunks;
  }

  function AgeInDays($date_received) {
    $dStamp = strtotime($date_received);
    $cStamp = mktime();
    $diff = $this->DateDiff($dStamp, $cStamp);
    return $diff["Days"];
  }

  function SplitSender($sender) {
    $senderParts["name"] = $sender;

    if (($pos = strrpos($sender, "<")) && substr($sender, -1) == ">") {
      $senderParts["name"] = substr($sender, 0, $pos - 1);
      $senderParts["email"] = substr($sender, $pos);
    }

    $senderParts["name"] = eregi_replace("<|>", "", $senderParts["name"]);

    $firstChar = substr($senderParts["name"], 0, 1);
    $lastChar = substr($senderParts["name"], -1);
    if ($firstChar == """ || $firstChar == "'") $senderParts["name"] = substr($senderParts["name"], 1);
    if ($lastChar == """ || $lastChar == "'") $senderParts["name"] = substr($senderParts["name"], 0, -1);

    return $senderParts;
  }

  function HTMLFormatSender($sender) {
    $sender = eregi_replace(""", "", $sender);
    $sender = eregi_replace("<", "&lt;", $sender);
    $sender = eregi_replace(">", "&gt;", $sender);
    return $sender;
  }

} // END pop3