This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).
wget 'https://lists2.roe3.org/hesk/inc/mail/HeskOAuthTokenProvider.php'
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
namespace PHPMailer\PHPMailer;
class HeskOAuthTokenProvider implements OAuthTokenProvider
{
public $username;
public $provider;
/**
* Generate a base64-encoded OAuth token.
* @return string or boolean
*/
public function getOauth64()
{
if (($access_token = hesk_fetch_access_token($this->provider)) === false) {
return false;
}
return base64_encode(
'user=' .
$this->username .
"\001auth=Bearer " .
$access_token .
"\001\001"
);
}
}
wget 'https://lists2.roe3.org/hesk/inc/mail/email_parser.php'
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
/* Check if this is a valid include */
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
function getFileExtension($fileName)
{
$parts=explode(".",$fileName);
return $parts[count($parts)-1];
}
function get_temp_fname($tmpdir)
{
return $tmpdir.md5(uniqid(mt_rand(), true));
}
// read config file , return value of varname or false
function get_config($varname)
{
global $hesk_settings;
$tmpdir = get_temp_fname(HESK_PATH . $hesk_settings['cache_dir'] . '/');
mkdir($tmpdir, 0777);
$config = array(
"TempDir" => $tmpdir,
"AttachmentsDir" => $tmpdir,
);
if ( array_key_exists($varname,$config) )
{
return $config[$varname];
}
return FALSE;
}
function parser($eml_file='')
{
$tempdir = get_config('TempDir');
if ( ! is_dir($tempdir) )
{
die('The temporary directory "'.$tempdir.'" doesn\'t exist.');
}
// get a unique temporary file name
$tmpfilepath = tempnam($tempdir, strval(mt_rand(1000,9999)));
if( ! file_exists($tmpfilepath))
{
die('Cannot create temporary file: '.$tmpfilepath);
}
if (defined('HESK_IMAP'))
{
global $imap, $email_number;
$is_saved = $imap->saveMessageToFile($email_number, $tmpfilepath);
}
else
{
// read the mail that is forwarded to the script
// then save the mail to a temporary file
$is_saved = save_forward_mail($tmpfilepath, $eml_file);
}
if ($is_saved === false)
{
deleteAll($tempdir);
return false;
}
$ret = analyze($tmpfilepath,$tempdir);
//die (print_r($ret));
if ($ret === false)
{
deleteAll($tempdir);
return false;
}
return $ret;
}
function analyze($tmpfilepath,$tempdir)
{
$mime=new mime_parser_class;
$mime->mbox = 0;
$mime->decode_bodies = 1;
$mime->ignore_syntax_errors = 1;
$mime->track_lines = 0;
$parameters=array(
'File'=>$tmpfilepath,
'SaveBody'=>$tempdir,
'HeskAttachmentRules'=>hesk_email_get_attachment_rules(),
);
/* only process the first email */
if($mime->Decode($parameters, $decoded))
{
if($mime->decode_bodies)
{
# print_r($decoded);
if($mime->Analyze($decoded[0], $results))
{
# echo "MIME:\n\n";
# print_r($results);
# echo "\nEND MIME\n\n";
return process_results($results,$tempdir) ;
}
else
{
echo 'MIME message analyse error: '.$mime->error."\n";
}
}
}
return False;
}
function process_addrname_pairs($email_info)
{
$result = array();
foreach($email_info as $info)
{
$address = "";
$name = "";
$encoding = "";
if ( array_key_exists("address", $info) )
{
$address = $info["address"];
}
if ( array_key_exists("name", $info) )
{
$name = $info["name"];
}
if ( array_key_exists("encoding", $info) )
{
$encoding = $info["encoding"];
}
$result[] = array("address"=>$address, "name"=>$name, "encoding"=>$encoding);
}
return $result;
}
function hesk_email_get_attachment_rules()
{
global $hesk_settings;
$allowed_types = array();
if (isset($hesk_settings['attachments']['allowed_types']) && is_array($hesk_settings['attachments']['allowed_types']))
{
foreach ($hesk_settings['attachments']['allowed_types'] as $ext)
{
$ext = strtolower(trim($ext));
if ($ext === '')
{
continue;
}
$allowed_types[] = $ext[0] === '.' ? $ext : '.' . $ext;
}
}
return array(
'use' => !empty($hesk_settings['attachments']['use']),
'save_embedded' => !empty($hesk_settings['save_embedded']),
'max_number' => isset($hesk_settings['attachments']['max_number']) ? max(0, intval($hesk_settings['attachments']['max_number'])) : 0,
'max_size' => isset($hesk_settings['attachments']['max_size']) ? max(0, intval($hesk_settings['attachments']['max_size'])) : 0,
'allowed_types' => array_unique($allowed_types),
);
}
function hesk_email_attachment_extension($filename)
{
$filename = trim((string) $filename);
if ($filename === '' || strpos($filename, '.') === false)
{
return '';
}
return strtolower(strrchr($filename, '.'));
}
function hesk_email_is_allowed_attachment_name($filename)
{
$rules = hesk_email_get_attachment_rules();
if ( ! $rules['use'])
{
return false;
}
$ext = hesk_email_attachment_extension($filename);
return $ext !== '' && in_array($ext, $rules['allowed_types'], true);
}
function process_attachments($attachments, $tempdir)
{
$result = array();
foreach($attachments as $key => $info)
{
$orig_name = "";
$size = 0;
$stored_name = "";
$type = "";
if ( array_key_exists("Type", $info) )
{
$type = $info["Type"];
}
if ( array_key_exists("FileName", $info) )
{
$orig_name = $info["FileName"];
}
elseif ($type == 'message')
{
$orig_name = ($key + 1) . ".eml";
}
if ( ! strlen($orig_name))
{
continue;
}
if ( array_key_exists("Data", $info) )
{
$data = $info["Data"];
$size = strlen($data);
if ($size == 0)
{
continue;
}
// If this in-memory part would later be rejected, keep the metadata so
// hesk_email2ticket() can add the usual notice, but don't write it to disk.
if (hesk_email_is_allowed_attachment_name($orig_name))
{
if ( ! is_dir($tempdir) )
{
die('The attachments directory "'.$tempdir.'" doesn\'t exist.');
}
$stored_name = save_attachment($tempdir, getFileExtension($orig_name), $data);
}
}
elseif (isset($info['DataFile']) && is_file($info['DataFile']))
{
$stored_name = $info['DataFile'];
$size = filesize($stored_name);
}
elseif (isset($info['DataLength']))
{
$size = intval($info['DataLength']);
}
else
{
continue;
}
$result[] = array("orig_name"=>$orig_name,"size"=>$size,"stored_name"=>$stored_name,"type"=>$type);
}
return $result;
}
/*
save an attachment file into the predefined directory.
return stored name
*/
function save_attachment($dir,$extension,$data)
{
$dir = rtrim($dir,"/\\"); /* " */
$path = "";
$stored_name = "";
do
{
$stored_name = date("YmdHis")."_".strval(mt_rand()).".".$extension;
$path = $dir . "/" . $stored_name;
} while(file_exists($path));
$fp = fopen($path,"w");
if($fp === FALSE)
{
die("Cannot save file ".$path." .");
}
fwrite($fp,$data);
fclose($fp);
return $path;
}
function process_results($result,$tempdir)
{
global $hesk_settings;
$r = array();
// from address and name
$r["from"] = process_addrname_pairs($result["From"]);
// to address and name
$r["to"] = process_addrname_pairs($result["To"]);
// cc address and name
if( array_key_exists("Cc", $result) )
{
$r["cc"] = process_addrname_pairs($result["Cc"]);
}
else
{
$r["cc"] = array();
}
// bcc address and name
if( array_key_exists("Bcc", $result) )
{
$r["bcc"] = process_addrname_pairs($result["Bcc"]);
}
else
{
$r["bcc"] = array();
}
// reply-to address and name
if( array_key_exists("Reply-to", $result) )
{
$r["reply-to"] = process_addrname_pairs($result["Reply-to"]);
}
else
{
$r["reply-to"] = array();
}
// subject and subject encoding
$r["subject"] = $result["Subject"];
$r["subject_encoding"] = isset($result["SubjectEncoding"]) ? strtoupper($result["SubjectEncoding"]) : "";
// Message encoding
$r["encoding"] = isset($result["Encoding"]) ? strtoupper($result["Encoding"]) : "";
// If message is saved in a file get it from the file
if ( ! isset($result["Data"]) )
{
$result["Data"] = ( isset($result["DataFile"]) && ! ( isset($result["FileDisposition"]) && $result["FileDisposition"] == "attachment") ) ? file_get_contents($result["DataFile"]) : "";
}
// Convert to UTF-8 before processing further
if ($r["encoding"] != "" && $r["encoding"] != 'UTF-8')
{
$result["Data"] = $result["Data"] == "" ? "" : (function_exists('iconv') ? iconv($r["encoding"], 'UTF-8', $result["Data"]) : hesk_iso8859_1_to_utf8($result["Data"]));
$r["encoding"] = 'UTF-8';
}
// the message shall be converted to text if it is in html
if ( $result["Type"] === "html" )
{
$r["message"] = convert_html_to_text($result["Data"]);
}
else
{
$r["message"] = $result["Data"];
}
// Fix for inline attachments
if (isset($result["FileDisposition"]) && ($result["FileDisposition"] == "attachment" || $result["FileDisposition"] == "inline"))
{
$r["message"] = "";
}
// Message attachments
$r["attachments"] = array();
if (!empty($hesk_settings['attachments']['use']))
{
// Attachment with no message
if ( isset($result["FileDisposition"]) && ($result["FileDisposition"] == "attachment" || $result["FileDisposition"] == "inline"))
{
$tmp = array();
$tmp[0]['FileDisposition'] = "attachment";
if ( isset($result["Type"]) )
{
$tmp[0]['Type'] = $result["Type"];
}
if ( isset($result["SubType"]) )
{
$tmp[0]['SubType'] = $result["SubType"];
}
if ( isset($result["Description"]) )
{
$tmp[0]['Description'] = $result["Description"];
}
if ( isset($result["DataFile"]) )
{
$tmp[0]['DataFile'] = $result["DataFile"];
}
if ( isset($result["FileName"]) )
{
$tmp[0]['FileName'] = $result["FileName"];
}
$r["attachments"] = array_merge($r["attachments"], process_attachments($tmp, $tempdir) );
}
// File attachments
if ( array_key_exists("Attachments", $result) )
{
$r["attachments"] = array_merge($r["attachments"], process_attachments($result["Attachments"], $tempdir) );
// Attachments to attachments?
foreach ($result["Attachments"] as $att) {
if (isset($att["Attachments"])) {
$r["attachments"] = array_merge($r["attachments"], process_attachments($att["Attachments"], $tempdir) );
}
}
}
// Save embedded files (for example embedded images)
if ($hesk_settings['save_embedded'] && array_key_exists("Related", $result) )
{
$r["attachments"] = array_merge($r["attachments"], process_attachments($result["Related"], $tempdir) );
}
}
// Name of the temporary folder
$r["tempdir"] = $tempdir;
// Custom Hesk tag with tracking ID
$r["X-Hesk-Tracking_ID"] = isset($result["X-Hesk-Tracking_ID"]) ? strtoupper($result["X-Hesk-Tracking_ID"]) : "";
// Do we have a priority tag?
$r["X-Priority"] = isset($result["X-Priority"]) ? strtolower($result["X-Priority"]) : "low";
// Message ID and related tags
$r["Message-ID"] = isset($result["Message-ID"]) ? $result["Message-ID"] : "";
$r["In-Reply-To"] = isset($result["In-Reply-To"]) ? $result["In-Reply-To"] : "";
$r["References"] = isset($result["References"]) ? $result["References"] : "";
return $r;
}
/*
save the forwarded mail to a temporary file
no return value
*/
function save_forward_mail($tmpfilepath, $eml_file)
{
// create a temporary file
$tmpfp = @fopen($tmpfilepath, "wb");
if ($tmpfp === false)
{
return false;
}
// Just a line used for testing
// $eml_file = 'C:\\Users\\4N\\Desktop\\test.txt';
// open the stdin as a file handle or input file
$fp = $eml_file ? @fopen($eml_file, "rb") : @fopen("php://stdin", "rb");
if ($fp === false)
{
fclose($tmpfp);
return false;
}
$fileContent = @stream_get_contents($fp);
if ($fileContent === false)
{
fclose($fp);
fclose($tmpfp);
return false;
}
$written = fwrite($tmpfp, $fileContent);
fclose($fp);
fclose($tmpfp);
return $written !== false && $written === strlen($fileContent);
}
#test();
function test()
{
$results = parser();
print_r($results);
exit();
// from address and name
echo "from :\n";
echo $results["from"][0]["address"]."\n";
echo $results["from"][0]["name"]."\n";
echo "\nto :\n";
foreach( $results["to"] as $to ){
echo $to["address"]."\n";
echo $to["name"]."\n";
}
echo "\nreply-to :\n";
foreach( $results["reply-to"] as $to ){
echo $to["address"]."\n";
echo $to["name"]."\n";
}
echo "\ncc :\n";
foreach( $results["cc"] as $to ){
echo $to["address"]."\n";
echo $to["name"]."\n";
}
echo "\nbcc :\n";
foreach( $results["bcc"] as $to ){
echo $to["address"]."\n";
echo $to["name"]."\n";
}
echo "\nsubject :\n";
echo $results["subject"]."\n";
echo "\nmessage :\n";
echo $results["message"]."\n";
echo "\nattachments :\n";
foreach( $results["attachments"] as $attach ){
echo $attach["orig_name"]."\n";
echo $attach["size"]."\n";
echo $attach["stored_name"]."\n";
echo $attach["type"]."\n";
}
}
function deleteAll($directory, $empty = false)
{
if(substr($directory,-1) == "/")
{
$directory = substr($directory,0,-1);
}
if(!file_exists($directory) || !is_dir($directory))
{
return false;
}
elseif( ! is_readable($directory))
{
return false;
}
else
{
$directoryHandle = opendir($directory);
while ($contents = readdir($directoryHandle))
{
if($contents != '.' && $contents != '..')
{
$path = $directory . "/" . $contents;
if(is_dir($path))
{
deleteAll($path);
}
else
{
@unlink($path);
}
}
}
closedir($directoryHandle);
if($empty == false)
{
if(!rmdir($directory))
{
return false;
}
}
return true;
}
}
function convert_html_to_text($data)
{
$html2text = new html2text($data);
$h2t = & ref_new($html2text);
// Simply call the get_text() method for the class to convert
// the HTML to the plain text. Store it into the variable.
$text = $h2t->get_text();
return $text;
}
class html2text
{
/**
* Contains the HTML content to convert.
*
* @var string $html
* @access public
*/
var $html;
/**
* Contains the converted, formatted text.
*
* @var string $text
* @access public
*/
var $text;
/**
* Maximum width of the formatted text, in columns.
*
* Set this value to 0 (or less) to ignore word wrapping
* and not constrain text to a fixed-width column.
*
* @var integer $width
* @access public
*/
var $width = 70;
/**
* List of preg* regular expression patterns to search for,
* used in conjunction with $replace.
*
* @var array $search
* @access public
* @see $replace
*/
var $search = array(
"/\r/", // Non-legal carriage return
"/[\n\t]+/", // Newlines and tabs
'/[ ]{2,}/', // Runs of spaces, pre-handling
'/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with
'/<style[^>]*>.*?<\/style>/i', // <style>s -- which strip_tags supposedly has problems with
//'/<!-- .* -->/', // Comments -- which strip_tags might have problem a with
'/<h[123][^>]*>(.*?)<\/h[123]>/i', // H1 - H3
'/<h[456][^>]*>(.*?)<\/h[456]>/i', // H4 - H6
'/<div[^>]*>/i', // <div>
'/<p[^>]*>/i', // <p>
'/<br[^>]*>/i', // <br>
'/<b[^>]*>(.*?)<\/b>/i', // <b>
'/<strong[^>]*>(.*?)<\/strong>/i', // <strong>
'/<i[^>]*>(.*?)<\/i>/i', // <i>
'/<em[^>]*>(.*?)<\/em>/i', // <em>
'/(<ul[^>]*>|<\/ul>)/i', // <ul> and </ul>
'/(<ol[^>]*>|<\/ol>)/i', // <ol> and </ol>
'/<li[^>]*>(.*?)<\/li>/i', // <li> and </li>
'/<li[^>]*>/i', // <li>
'/<a [^>]*href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/i',
// <a href="">
'/<hr[^>]*>/i', // <hr>
'/(<table[^>]*>|<\/table>)/i', // <table> and </table>
'/(<tr[^>]*>|<\/tr>)/i', // <tr> and </tr>
'/<td[^>]*>(.*?)<\/td>/i', // <td> and </td>
'/<th[^>]*>(.*?)<\/th>/i', // <th> and </th>
);
/**
* List of pattern replacements corresponding to patterns searched.
*
* @var array $replace
* @access public
* @see $search
*/
var $replace = array(
'', // Non-legal carriage return
' ', // Newlines and tabs
' ', // Runs of spaces, pre-handling
'', // <script>s -- which strip_tags supposedly has problems with
'', // <style>s -- which strip_tags supposedly has problems with
//'', // Comments -- which strip_tags might have problem a with
"\n\n\\1\n\n", // H1 - H3
"\n\n\\1\n\n", // H4 - H6
"\n", // <div>
"\n\n", // <p>
"\n", // <br>
"\\1", // <b>
"\\1", // <strong>
'_\\1_', // <i>
'_\\1_', // <em>
"\n\n", // <ul> and </ul>
"\n\n", // <ol> and </ol>
"\t* \\1\n", // <li> and </li>
"\n\t* ", // <li>
"\\2:\n\\1", // <a href="">
"\n-------------------------\n", // <hr>
"\n\n", // <table> and </table>
"\n", // <tr> and </tr>
"\t\t\\1\n", // <td> and </td>
"\t\t\\1\n", // <th> and </th>
);
/**
* Contains a list of HTML tags to allow in the resulting text.
*
* @var string $allowed_tags
* @access public
* @see set_allowed_tags()
*/
var $allowed_tags = '';
/**
* Contains the base URL that relative links should resolve to.
*
* @var string $url
* @access public
*/
var $url;
/**
* Indicates whether content in the $html variable has been converted yet.
*
* @var boolean $_converted
* @access private
* @see $html, $text
*/
var $_converted = false;
/**
* Contains URL addresses from links to be rendered in plain text.
*
* @var string $_link_list
* @access private
* @see _build_link_list()
*/
var $_link_list = '';
/**
* Number of valid links detected in the text, used for plain text
* display (rendered similar to footnotes).
*
* @var integer $_link_count
* @access private
* @see _build_link_list()
*/
var $_link_count = 0;
/**
* Constructor.
*
* If the HTML source string (or file) is supplied, the class
* will instantiate with that source propagated, all that has
* to be done it to call get_text().
*
* @param string $source HTML content
* @param boolean $from_file Indicates $source is a file to pull content from
* @access public
* @return void
*/
function __construct( $source = '', $from_file = false )
{
if ( !empty($source) ) {
$this->set_html($source, $from_file);
}
$this->set_base_url();
}
/**
* Loads source HTML into memory, either from $source string or a file.
*
* @param string $source HTML content
* @param boolean $from_file Indicates $source is a file to pull content from
* @access public
* @return void
*/
function set_html( $source, $from_file = false )
{
$this->html = $source;
if ( $from_file && file_exists($source) ) {
$fp = fopen($source, 'r');
$this->html = fread($fp, filesize($source));
fclose($fp);
}
$this->_converted = false;
}
/**
* Returns the text, converted from HTML.
*
* @access public
* @return string
*/
function get_text()
{
if ( !$this->_converted ) {
$this->_convert();
}
return $this->text;
}
/**
* Prints the text, converted from HTML.
*
* @access public
* @return void
*/
function print_text()
{
print $this->get_text();
}
/**
* Alias to print_text(), operates identically.
*
* @access public
* @return void
* @see print_text()
*/
function p()
{
print $this->get_text();
}
/**
* Sets the allowed HTML tags to pass through to the resulting text.
*
* Tags should be in the form "<p>", with no corresponding closing tag.
*
* @access public
* @return void
*/
function set_allowed_tags( $allowed_tags = '' )
{
if ( !empty($allowed_tags) ) {
$this->allowed_tags = $allowed_tags;
}
}
/**
* Sets a base URL to handle relative links.
*
* @access public
* @return void
*/
function set_base_url( $url = '' )
{
if ( empty($url) ) {
if ( !empty($_SERVER['HTTP_HOST']) ) {
$this->url = 'http://' . $_SERVER['HTTP_HOST'];
} else {
$this->url = '';
}
} else {
// Strip any trailing slashes for consistency (relative
// URLs may already start with a slash like "/file.html")
if ( substr($url, -1) == '/' ) {
$url = substr($url, 0, -1);
}
$this->url = $url;
}
}
/**
* Workhorse function that does actual conversion.
*
* First performs custom tag replacement specified by $search and
* $replace arrays. Then strips any remaining HTML tags, reduces whitespace
* and newlines to a readable format, and word wraps the text to
* $width characters.
*
* @access private
* @return void
*/
function _convert()
{
global $hesklang;
// Variables used for building the link list
$this->_link_count = 0;
$this->_link_list = '';
$text = trim($this->html);
// Remove embedded image tags
$text = preg_replace('/<img.*?src=["\']+(.*?)["\']+.*?>/i', '$1', $text);
// Run our defined search-and-replace
$text = preg_replace($this->search, $this->replace, $text);
// Cleanup HTML entities and convert them to UTF-8
$text = hesk_convert_to_utf8_and_clean_html_entities($text);
// Strip any other HTML tags
#$text = strip_tags($text, $this->allowed_tags);
// Bring down number of empty lines to 2 max
$text = preg_replace("/\n\s+\n/", "\n\n", $text);
$text = preg_replace("/[\n]{3,}/", "\n\n", $text);
// Add link list
if ( !empty($this->_link_list) ) {
$text .= "\n\nLinks:\n------\n" . $this->_link_list;
}
// Wrap the text to a readable format
// for PHP versions >= 4.0.2. Default width is 75
// If width is 0 or less, don't wrap the text.
if ( $this->width > 0 ) {
$text = wordwrap($text, $this->width);
}
$this->text = $text;
$this->_converted = true;
}
/**
* Helper function called by preg_replace() on link replacement.
*
* Maintains an internal list of links to be displayed at the end of the
* text, with numeric indices to the original point in the text they
* appeared. Also makes an effort at identifying and handling absolute
* and relative links.
*
* @param string $link URL of the link
* @param string $display Part of the text to associate number with
* @access private
* @return string
*/
function _build_link_list( $link, $display )
{
if ( substr($link, 0, 7) == 'http://' || substr($link, 0, 8) == 'https://' ||
substr($link, 0, 7) == 'mailto:' ) {
$this->_link_count++;
$this->_link_list .= "[" . $this->_link_count . "] $link\n";
$additional = ' [' . $this->_link_count . ']';
} elseif ( substr($link, 0, 11) == 'javascript:' ) {
// Don't count the link; ignore it
$additional = '';
// what about href="#anchor" ?
} else {
$this->_link_count++;
$this->_link_list .= "[" . $this->_link_count . "] " . $this->url;
if ( substr($link, 0, 1) != '/' ) {
$this->_link_list .= '/';
}
$this->_link_list .= "$link\n";
$additional = ' [' . $this->_link_count . ']';
}
return $display . $additional;
}
}
/*
* Converts reserved (X)HTML entities into UTF-8 chars
* Some entities are handled in a different function and are commented out
* Some entities are replace with more acceptable ones
* Some entities are ignored
*/
function hesk_convert_to_utf8_and_clean_html_entities($text)
{
// Can we use the multibyte functionality of PHP?
if ( function_exists('mb_decode_numericentity') )
{
$text = mb_decode_numericentity($text, array(0x0, 0x2FFFF, 0, 0xFFFF), 'UTF-8');
}
// Alternatively, use a custom function
else
{
$text = preg_replace_callback('/&#([0-9a-fx]+);/mi', 'hesk_replace_num_entity', $text);
}
// Entities that are not case sensitive
$html_entities = array(
// Convert to double quotes
'"' => '"',
'”' => '"',
'“' => '"',
'”' => '"',
'„' => '"',
'′' => '"',
// Convert to single quotes
''' => '\'',
'‘' => '\'', // '‘' => '‘',
'’' => '\'', // '’' => '’',
'′' => '\'',
'´' => '\'',
// Convert to spaces
' ' => ' ',
' ' => ' ',
' ' => ' ',
' ' => ' ',
// Other chars often used
#'<' => '<',
#'>' => '>',
'¡' => '¡',
'¢' => '¢',
'£' => '£',
'¤' => '¤',
'¥' => '¥',
'¦' => '¦',
'§' => '§',
'¨' => '¨',
'©' => '©',
'ª' => 'ª',
'«' => '«',
'¬' => '¬',
'­' => '',
'®' => '®',
'¯' => '¯',
'°' => '°',
'±' => '±',
'²' => '²',
'³' => '³',
'µ' => 'µ',
'¶' => '¶',
'·' => '·',
'¸' => '¸',
'¹' => '¹',
'º' => 'º',
'»' => '»',
'¼' => '¼',
'½' => '½',
'¾' => '¾',
'¿' => '¿',
'×' => '×',
'÷' => '÷',
'∀' => '∀',
'∂' => '∂',
'∃' => '∃',
'∅' => '∅',
'∇' => '∇',
'∈' => '∈',
'∉' => '∉',
'∋' => '∋',
'∏' => '∏',
'∑' => '∑',
'−' => '−',
'∗' => '∗',
'√' => '√',
'∝' => '∝',
'∞' => '∞',
'∠' => '∠',
'∧' => '∧',
'∨' => '∨',
'∩' => '∩',
'∪' => '∪',
'∫' => '∫',
'∴' => '∴',
'∼' => '∼',
'≅' => '≅',
'≈' => '≈',
'≠' => '≠',
'≡' => '≡',
'≤' => '≤',
'≥' => '≥',
'⊂' => '⊂',
'⊃' => '⊃',
'⊄' => '⊄',
'⊆' => '⊆',
'⊇' => '⊇',
'⊕' => '⊕',
'⊗' => '⊗',
'⊥' => '⊥',
'⋅' => '⋅',
'ƒ' => 'ƒ',
'ˆ' => 'ˆ',
'˜' => '˜',
'–' => '–',
'—' => '—',
'‚' => ',',
'•' => '•',
'…' => '…',
'‰' => '‰',
'‹' => '‹',
'›' => '›',
'‾' => '‾',
'€' => '€',
'™' => '™',
'←' => '←',
'↑' => '↑',
'→' => '→',
'↓' => '↓',
'↔' => '↔',
'◊' => '◊',
'♠' => '♠',
'♣' => '♣',
'♥' => '♥',
'♦' => '♦',
);
$text = str_ireplace( array_keys($html_entities), array_values($html_entities), $text);
// Case sensitive entities
$html_entities = array(
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Æ' => 'Æ',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ð' => 'Ð',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'Ø' => 'Ø',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'Þ' => 'Þ',
'ß' => 'ß',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'æ' => 'æ',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ð' => 'ð',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'ø' => 'ø',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'þ' => 'þ',
'ÿ' => 'ÿ',
'Α' => 'Α',
'Β' => 'Β',
'Γ' => 'Γ',
'Δ' => 'Δ',
'Ε' => 'Ε',
'Ζ' => 'Ζ',
'Η' => 'Η',
'Θ' => 'Θ',
'Ι' => 'Ι',
'Κ' => 'Κ',
'Λ' => 'Λ',
'Μ' => 'Μ',
'Ν' => 'Ν',
'Ξ' => 'Ξ',
'Ο' => 'Ο',
'Π' => 'Π',
'Ρ' => 'Ρ',
'Σ' => 'Σ',
'Τ' => 'Τ',
'Υ' => 'Υ',
'Φ' => 'Φ',
'Χ' => 'Χ',
'Ψ' => 'Ψ',
'Ω' => 'Ω',
'α' => 'α',
'β' => 'β',
'γ' => 'γ',
'δ' => 'δ',
'ε' => 'ε',
'ζ' => 'ζ',
'η' => 'η',
'θ' => 'θ',
'ι' => 'ι',
'κ' => 'κ',
'λ' => 'λ',
'μ' => 'μ',
'ν' => 'ν',
'ξ' => 'ξ',
'ο' => 'ο',
'π' => 'π',
'ρ' => 'ρ',
'ς' => 'ς',
'σ' => 'σ',
'τ' => 'τ',
'υ' => 'υ',
'φ' => 'φ',
'χ' => 'χ',
'ψ' => 'ψ',
'ω' => 'ω',
'ϑ' => 'ϑ',
'ϒ' => 'ϒ',
'ϖ' => 'ϖ',
'Œ' => 'Œ',
'œ' => 'œ',
'Š' => 'Š',
'š' => 'š',
'Ÿ' => 'Ÿ',
'†' => '†',
'‡' => '‡',
);
$text = str_replace( array_keys($html_entities), array_values($html_entities), $text);
// Strip HTML tags
$text = strip_tags($text);
// Process <, > and & after all others
$text = str_ireplace( array('<', '>', '&', '&'), array('<', '>', '&', '&'), $text);
// Delete any unsupported entities, excess spaces and return
return preg_replace( '/[ ]{2,}/', ' ', $text);
} // END hesk_convert_to_utf8_and_clean_html_entities()
function hesk_replace_num_entity($ord)
{
$ord = $ord[1];
if (preg_match('/^x([0-9a-f]+)$/i', $ord, $match))
{
$ord = hexdec($match[1]);
}
else
{
$ord = intval($ord);
}
$no_bytes = 0;
$byte = array();
if ($ord < 128)
{
return chr($ord);
}
elseif ($ord < 2048)
{
$no_bytes = 2;
}
elseif ($ord < 65536)
{
$no_bytes = 3;
}
elseif ($ord < 1114112)
{
$no_bytes = 4;
}
else
{
return;
}
switch($no_bytes)
{
case 2:
{
$prefix = array(31, 192);
break;
}
case 3:
{
$prefix = array(15, 224);
break;
}
case 4:
{
$prefix = array(7, 240);
}
}
for ($i = 0; $i < $no_bytes; $i++)
{
$byte[$no_bytes - $i - 1] = (($ord & (63 * pow(2, 6 * $i))) / pow(2, 6 * $i)) & 63 | 128;
}
$byte[0] = ($byte[0] & $prefix[0]) | $prefix[1];
$ret = '';
for ($i = 0; $i < $no_bytes; $i++)
{
$ret .= chr($byte[$i]);
}
return $ret;
} // END hesk_replace_num_entity()
function hesk_quoted_printable_encode($str)
{
if ( function_exists('quoted_printable_encode') )
{
return quoted_printable_encode($str);
}
define('PHP_QPRINT_MAXL', 75);
$lp = 0;
$ret = '';
$hex = "0123456789ABCDEF";
$length = strlen($str);
$str_index = 0;
while ($length--)
{
if ((($c = $str[$str_index++]) == "\015") && ($str[$str_index] == "\012") && $length > 0)
{
$ret .= "\015";
$ret .= $str[$str_index++];
$length--;
$lp = 0;
}
else
{
if (ctype_cntrl($c)
|| (ord($c) == 0x7f)
|| (ord($c) & 0x80)
|| ($c == '=')
|| (($c == ' ') && ($str[$str_index] == "\015")))
{
if (($lp += 3) > PHP_QPRINT_MAXL)
{
$ret .= '=';
$ret .= "\015";
$ret .= "\012";
$lp = 3;
}
$ret .= '=';
$ret .= $hex[ord($c) >> 4];
$ret .= $hex[ord($c) & 0xf];
}
else
{
if ((++$lp) > PHP_QPRINT_MAXL)
{
$ret .= '=';
$ret .= "\015";
$ret .= "\012";
$lp = 1;
}
$ret .= $c;
}
}
}
return $ret;
} // END hesk_quoted_printable_encode()
wget 'https://lists2.roe3.org/hesk/inc/mail/hesk_imap.php'
#!/usr/bin/php -q
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
define('IN_SCRIPT',1);
define('HESK_PATH', dirname(dirname(dirname(__FILE__))) . '/');
// Do not send out the default UTF-8 HTTP header
define('NO_HTTP_HEADER',1);
// Get required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/oauth_functions.inc.php');
require(HESK_PATH . 'inc/mail/imap/HeskIMAP.php');
//============================================================================//
// OPTIONAL MODIFICATIONS //
//============================================================================//
// Set category ID where new tickets will be submitted to
$set_category = 1;
// Set ticket priority of new tickets with the following options:
// -1 = use default category priority
// 0 = critical
// 1 = high
// 2 = medium
// 3 = low
$set_priority = -1;
// Uncomment lines below to use different IMAP login details than in settings
/*
$hesk_settings['imap'] = 1;
$hesk_settings['imap_job_wait'] = 15;
$hesk_settings['imap_host_name'] = 'imap.gmail.com';
$hesk_settings['imap_host_port'] = 993;
$hesk_settings['imap_enc'] = 'ssl';
$hesk_settings['imap_keep'] = 0;
$hesk_settings['imap_user'] = 'test@example.com';
$hesk_settings['imap_password'] = 'password';
*/
//============================================================================//
// END OPTIONAL MODIFICATIONS //
//============================================================================//
// Do we require a key if not accessed over CLI?
hesk_authorizeNonCLI();
// Is this feature enabled?
if (empty($hesk_settings['imap']))
{
die($hesklang['ifd']);
}
// Are we in maintenance mode?
if ( hesk_check_maintenance(false) )
{
// If Debug mode is ON show "Maintenance mode" message
$message = $hesk_settings['debug_mode'] ? $hesklang['mm1'] : '';
die($message);
}
// Don't start IMAP fetching if an existing job is in progress
if ($hesk_settings['imap_job_wait'])
{
// A log file used to store start of IMAP fetching
$job_file = HESK_PATH . $hesk_settings['cache_dir'] . '/__imap-' . sha1(__FILE__) . '.txt';
// If the job file already exists, wait for the previous job to complete unless expired
if ( file_exists($job_file ) )
{
// Get time when the active IMAP fetching started
$last = intval( file_get_contents($job_file) );
// Give a running process at least X minutes to finish
if ( $last + $hesk_settings['imap_job_wait'] * 60 > time() )
{
$message = $hesk_settings['debug_mode'] ? $hesklang['ifr'] : '';
die($message);
}
else
{
// Start the process (force)
file_put_contents($job_file, time() );
}
}
else
{
// No job in progress, log when this one started
file_put_contents($job_file, time() );
}
}
// Get other required includes
require(HESK_PATH . 'inc/pipe_functions.inc.php');
// Tell Hesk we are in IMAP mode
define('HESK_IMAP', true);
// Connect to the database
hesk_dbConnect();
// Are we in test mode?
$hesk_settings['TEST-MODE'] = (hesk_GET('test_mode') == 1) ? true : false;
$imap = new HeskIMAP();
$imap->host = $hesk_settings['imap_host_name'];
$imap->port = $hesk_settings['imap_host_port'];
$imap->username = $hesk_settings['imap_user'];
if ($hesk_settings['imap_conn_type'] === 'basic') {
$imap->password = hesk_htmlspecialchars_decode($hesk_settings['imap_password']);
$imap->useOAuth = false;
} elseif ($hesk_settings['imap_conn_type'] === 'oauth') {
$access_token = hesk_fetch_access_token($hesk_settings['imap_oauth_provider']);
if (!$access_token) {
echo "<pre>" . $hesklang['oauth_error_retrieve'] . "</pre>";
if ($hesk_settings['imap_job_wait']) {
unlink($job_file);
}
return null;
}
$imap->accessToken = $access_token;
$imap->useOAuth = true;
$imap->password = null;
}
$imap->readOnly = $hesk_settings['TEST-MODE'];
$imap->ignoreCertificateErrors = $hesk_settings['imap_noval_cert'];
$imap->disableGSSAPI = $hesk_settings['imap_disable_GSSAPI'];
$imap->connectTimeout = 15;
$imap->responseTimeout = 15;
$imap->imap_mailbox = $hesk_settings['imap_mailbox'];// Added for IMAP Mailbox
$imap->folder = $hesk_settings['imap_mailbox']; //Change for IMAP Mailbox Folder;
if ($hesk_settings['imap_enc'] === 'ssl')
{
$imap->ssl = true;
$imap->tls = false;
}
elseif ($hesk_settings['imap_enc'] === 'tls')
{
$imap->ssl = false;
$imap->tls = true;
}
else
{
$imap->ssl = false;
$imap->tls = false;
}
// We don't want the script to run forever if we can't connect to IMAP...
set_time_limit($imap->connectTimeout * 4);
// Connect to IMAP
if ($imap->login())
{
echo $hesk_settings['debug_mode'] ? "<pre>Connected to the IMAP server "" . $imap->host . ":" . $imap->port . "".</pre>\n" : '';
$has_unseen_message = $imap->hasUnseenMessages();
if (is_int($has_unseen_message) && $has_unseen_message > 0)
{
$emails = $imap->getUnseenMessageIDs();
$emails_found = count($emails);
echo $hesk_settings['debug_mode'] ? "<pre>Unread messages found: $emails_found</pre>\n" : '';
//print_r($emails);
if ($hesk_settings['TEST-MODE'])
{
$imap->logout();
echo $hesk_settings['debug_mode'] ? "<pre>TEST MODE, NO EMAILS PROCESSED\n\nDisconnected from the IMAP server.</pre>\n" : '';
if ($hesk_settings['imap_job_wait'])
{
unlink($job_file);
}
return null;
}
$this_email = 0;
// This is a bit tricky - we don't want the fetching to run forever, but we also don't want it to end prematurely
// Let's try to figure out a reasonable time limit:
// - let it run at least 300 seconds (5 minutes) per unseen email, to handle large attachments
// - let it run least 1800 seconds (30 minutes) in total
// - if it runs for over 3600 seconds (60 minutes) in total, something probably went wrong
if (function_exists('set_time_limit'))
{
$time_limit = $emails_found * 300;
if ($time_limit < 1800)
{
$time_limit = 1800;
}
elseif ($time_limit > 3600)
{
$time_limit = 3600;
}
set_time_limit($time_limit);
echo $hesk_settings['debug_mode'] ? "<pre>Time limit set to {$time_limit} seconds.</pre>\n" : '';
}
// Download and parse each email
foreach($emails as $email_number)
{
$this_email++;
echo $hesk_settings['debug_mode'] ? "<pre>Parsing message $this_email of $emails_found.</pre>\n" : '';
// Parse email from the stream
if (($results = parser()) === false)
{
echo $hesk_settings['debug_mode'] ? "<pre>Error parsing email, see debug log. Aborting fetching.</pre>\n" : '';
break;
}
// Convert email into a ticket (or new reply)
if ( $id = hesk_email2ticket($results, 2, $set_category, $set_priority) )
{
echo $hesk_settings['debug_mode'] ? "<pre>Ticket $id created/updated.</pre>\n" : '';
}
elseif (isset($hesk_settings['DEBUG_LOG']['PIPE']))
{
echo "<pre>Ticket NOT inserted: " . $hesk_settings['DEBUG_LOG']['PIPE'] . "</pre>\n";
}
// Any additional notices?
if ($hesk_settings['debug_mode'] && isset($hesk_settings['DEBUG_LOG']['NOTICES'])) {
echo "<pre>";
foreach ($hesk_settings['DEBUG_LOG']['NOTICES'] as $notice) {
echo "$notice\n";
}
echo "</pre>\n";
}
// Queue message to be deleted on connection close
if ( ! $hesk_settings['imap_keep'])
{
$imap->delete($email_number);
}
echo $hesk_settings['debug_mode'] ? "<br /><br />\n\n" : '';
}
if ( ! $hesk_settings['imap_keep'])
{
$imap->expunge();
echo $hesk_settings['debug_mode'] ? "<pre>Expunged mail folder.</pre>\n" : '';
}
}
elseif ($has_unseen_message === false)
{
echo "<pre>Error: User is authenticated but not connected.</pre>\n";
}
else
{
echo $hesk_settings['debug_mode'] ? "<pre>No unread messages found.</pre>\n" : '';
}
$imap->logout();
echo $hesk_settings['debug_mode'] ? "<pre>Disconnected from the IMAP server.</pre>\n" : '';
}
elseif (!$hesk_settings['debug_mode'])
{
echo "<p>Unable to connect to the IMAP server.</p>\n";
}
if($errors = $imap->getErrors())
{
if ($hesk_settings['debug_mode'])
{
foreach ($errors as $error)
{
echo "<pre>" . hesk_htmlspecialchars($error) . "</pre>\n";
}
}
else
{
echo "<h2>An error occured.</h2><p>For details turn <b>Debug mode</b> ON in settings and run this script again.</p>\n";
}
}
unset($imap);
// Remove active IMAP fetching log file
if ($hesk_settings['imap_job_wait'])
{
unlink($job_file);
}
return NULL;
wget 'https://lists2.roe3.org/hesk/inc/mail/hesk_pipe.php'
#!/usr/bin/php -q
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
define('IN_SCRIPT',1);
define('HESK_PATH', dirname(dirname(dirname(__FILE__))) . '/');
// Do not send out the default UTF-8 HTTP header
define('NO_HTTP_HEADER',1);
// Get required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
//============================================================================//
// OPTIONAL MODIFICATIONS //
//============================================================================//
// Set category ID where new tickets will be submitted to
$set_category = 1;
// Set ticket priority of new tickets with the following options:
// -1 = use default category priority
// 0 = critical
// 1 = high
// 2 = medium
// 3 = low
$set_priority = -1;
//============================================================================//
// END OPTIONAL MODIFICATIONS //
//============================================================================//
// Do we require a key if not accessed over CLI?
hesk_authorizeNonCLI();
// Is this feature enabled?
if (empty($hesk_settings['email_piping']))
{
die($hesklang['epd']);
}
// Email piping is enabled, get other required includes
require(HESK_PATH . 'inc/pipe_functions.inc.php');
// Parse the incoming email
$results = parser();
// If parsing failed, do not pass invalid data to hesk_email2ticket()
if ($results === false)
{
if ($hesk_settings['debug_mode'])
{
$hesk_settings['DEBUG_LOG']['PIPE'] = 'Error parsing piped email; skipping ticket creation.';
}
return NULL;
}
// Convert email into a ticket (or new reply)
hesk_dbConnect();
hesk_email2ticket($results, 0, $set_category, $set_priority);
// Uncomment for debugging
/*
if (isset($hesk_settings['DEBUG_LOG']['PIPE'])) {
echo "<pre>Ticket NOT inserted: " . $hesk_settings['DEBUG_LOG']['PIPE'] . "</pre>\n";
}
if (isset($hesk_settings['DEBUG_LOG']['NOTICES'])) {
echo "<pre>";
foreach ($hesk_settings['DEBUG_LOG']['NOTICES'] as $notice) {
echo "$notice\n";
}
echo "</pre>\n";
}
/* */
return NULL;
wget 'https://lists2.roe3.org/hesk/inc/mail/hesk_pop3.php'
#!/usr/bin/php -q
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
define('IN_SCRIPT',1);
define('HESK_PATH', dirname(dirname(dirname(__FILE__))) . '/');
// Do not send out the default UTF-8 HTTP header
define('NO_HTTP_HEADER',1);
// Get required files and functions
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
//============================================================================//
// OPTIONAL MODIFICATIONS //
//============================================================================//
// Set category ID where new tickets will be submitted to
$set_category = 1;
// Set ticket priority of new tickets with the following options:
// -1 = use default category priority
// 0 = critical
// 1 = high
// 2 = medium
// 3 = low
$set_priority = -1;
// Uncomment lines below to use different POP3 login details than in settings
/*
$hesk_settings['pop3_host_name'] = 'mail.server.com';
$hesk_settings['pop3_host_port'] = 110;
$hesk_settings['pop3_tls'] = 0;
$hesk_settings['pop3_user'] = 'user@server.com';
$hesk_settings['pop3_password'] = 'password';
*/
//============================================================================//
// END OPTIONAL MODIFICATIONS //
//============================================================================//
// Do we require a key if not accessed over CLI?
hesk_authorizeNonCLI();
// Is this feature enabled?
if (empty($hesk_settings['pop3']))
{
die($hesklang['pfd']);
}
// Are we in maintenance mode?
if ( hesk_check_maintenance(false) )
{
// If Debug mode is ON show "Maintenance mode" message
$message = $hesk_settings['debug_mode'] ? $hesklang['mm1'] : '';
die($message);
}
// Don't start POP3 fetching if an existing job is in progress
if ($hesk_settings['pop3_job_wait'])
{
// A log file used to store start of POP3 fetching
$job_file = HESK_PATH . $hesk_settings['cache_dir'] . '/__pop3-' . sha1(__FILE__) . '.txt';
// If the job file already exists, wait for the previous job to complete unless expired
if ( file_exists($job_file ) )
{
// Get time when the active POP3 fetching started
$last = intval( file_get_contents($job_file) );
// Give a running process at least 31 minutes to finish
if ( $last + $hesk_settings['pop3_job_wait'] * 60 > time() )
{
$message = $hesk_settings['debug_mode'] ? $hesklang['pfr'] : '';
die($message);
}
else
{
// Start the process (force)
file_put_contents($job_file, time() );
}
}
else
{
// No job in progress, log when this one started
file_put_contents($job_file, time() );
}
}
// Get other required includes
require(HESK_PATH . 'inc/pipe_functions.inc.php');
// Get POP3 class
require(HESK_PATH . 'inc/mail/pop3.php');
// Uncomment when using SASL authentication mechanisms
# require(HESK_PATH . 'inc/mail/sasl/sasl.php');
// If a pop3 wrapper is registered un register it, we need our custom wrapper
if ( in_array('pop3', stream_get_wrappers() ) )
{
stream_wrapper_unregister('pop3');
}
// Register the pop3 stream handler class
stream_wrapper_register('pop3', 'pop3_stream');
// Setup required variables
$pop3 = new pop3_class;
$pop3->hostname = $hesk_settings['pop3_host_name'];
$pop3->port = $hesk_settings['pop3_host_port'];
$pop3->tls = $hesk_settings['pop3_tls'];
$pop3->debug = 0;
$pop3->join_continuation_header_lines = 1;
if ($hesk_settings['pop3_conn_type']=='oauth') {
require(HESK_PATH . 'inc/oauth_functions.inc.php');
$pop3->authentication_mechanism = 'XOAUTH2';
hesk_dbConnect();
$access_token = hesk_fetch_access_token($hesk_settings['pop3_oauth_provider']);
if (!$access_token) {
echo "<pre>" . $hesklang['error'] . ': ' . $hesklang['oauth_error_retrieve'] . "</pre>";
if ($hesk_settings['pop3_job_wait'])
{
unlink($job_file);
}
return null;
}
}
// Connect to POP3
if(($error=$pop3->Open())=="")
{
echo $hesk_settings['debug_mode'] ? "<pre>Connected to the POP3 server "" . $pop3->hostname . "".</pre>\n" : '';
// Authenticate
if(($error=$pop3->Login($hesk_settings['pop3_user'], ($hesk_settings['pop3_conn_type']=='oauth' ? $access_token : hesk_htmlspecialchars_decode(stripslashes($hesk_settings['pop3_password'])))))=="")
{
echo $hesk_settings['debug_mode'] ? "<pre>User "" . $hesk_settings['pop3_user'] . "" logged in.</pre>\n" : '';
// Get number of messages and total size
if(($error=$pop3->Statistics($messages,$size))=="")
{
echo $hesk_settings['debug_mode'] ? "<pre>There are $messages messages in the mail box with a total of $size bytes.</pre>\n" : '';
if (hesk_GET('test_mode') == 1)
{
$pop3->Close();
echo $hesk_settings['debug_mode'] ? "<pre>TEST MODE, NO EMAILS PROCESSED\n\nDisconnected from the POP3 server "" . $pop3->hostname . "".</pre>\n" : '';
if ($hesk_settings['pop3_job_wait'])
{
unlink($job_file);
}
return null;
}
// If we have any messages, process them
if($messages>0)
{
// This is a bit tricky - we don't want the fetching to run forever, but we also don't want it to end prematurely
// Let's try to figure out a reasonable time limit:
// - let it run at least 300 seconds (5 minutes) per email, to handle large attachments
// - let it run least 1800 seconds (30 minutes) in total
// - if it runs for over 3600 seconds (60 minutes) in total, something probably went wrong
if (function_exists('set_time_limit'))
{
$time_limit = $messages * 300;
if ($time_limit < 1800)
{
$time_limit = 1800;
}
elseif ($time_limit > 3600)
{
$time_limit = 3600;
}
set_time_limit($time_limit);
echo $hesk_settings['debug_mode'] ? "<pre>Time limit set to {$time_limit} seconds.</pre>\n" : '';
}
// Connect to the database
hesk_dbConnect();
for ($message = 1; $message <= $messages; $message++)
{
echo $hesk_settings['debug_mode'] ? "<pre>Parsing message $message of $messages.</pre>\n" : '';
$pop3->GetConnectionName($connection_name);
$message_file = 'pop3://'.$connection_name.'/'.$message;
// Parse the incoming email
$results = parser($message_file);
// If parsing failed, do not pass invalid data to hesk_email2ticket()
if ($results === false)
{
echo $hesk_settings['debug_mode'] ? "<pre>Error parsing message $message of $messages; skipping ticket creation.</pre>\n" : '';
// Keep existing POP3 behavior: processed messages are deleted unless pop3_keep is enabled.
if ( ! $hesk_settings['pop3_keep'])
{
$pop3->DeleteMessage($message);
}
echo $hesk_settings['debug_mode'] ? "<br /><br />\n\n" : '';
continue;
}
// Convert email into a ticket (or new reply)
if ( $id = hesk_email2ticket($results, 1, $set_category, $set_priority) )
{
echo $hesk_settings['debug_mode'] ? "<pre>Ticket $id created/updated.</pre>\n" : '';
}
elseif (isset($hesk_settings['DEBUG_LOG']['PIPE']))
{
echo "<pre>Ticket NOT inserted: " . $hesk_settings['DEBUG_LOG']['PIPE'] . "</pre>\n";
}
// Any additional notices?
if ($hesk_settings['debug_mode'] && isset($hesk_settings['DEBUG_LOG']['NOTICES'])) {
echo "<pre>";
foreach ($hesk_settings['DEBUG_LOG']['NOTICES'] as $notice) {
echo "$notice\n";
}
echo "</pre>\n";
}
// Queue message to be deleted on connection close
if ( ! $hesk_settings['pop3_keep'])
{
$pop3->DeleteMessage($message);
}
echo $hesk_settings['debug_mode'] ? "<br /><br />\n\n" : '';
}
}
// Disconnect from the server - this also deletes queued messages
if($error == "" && ($error=$pop3->Close()) == "")
{
echo $hesk_settings['debug_mode'] ? "<pre>Disconnected from the POP3 server "" . $pop3->hostname . "".</pre>\n" : '';
}
}
}
}
// Any error messages?
if($error != '')
{
echo "<h2>Error: " . hesk_htmlspecialchars($error) . "</h2>";
}
// Remove active POP3 fetching log file
if ($hesk_settings['pop3_job_wait'])
{
unlink($job_file);
}
return NULL;
wget 'https://lists2.roe3.org/hesk/inc/mail/index.htm'
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
You don't have permission to access this folder.<P>
<hr />
</BODY></HTML>
wget 'https://lists2.roe3.org/hesk/inc/mail/mime_parser.php'
<?php
/*
* mime_parser.php
*
* @(#) $Id: mime_parser.php,v 1.99 2024/11/03 17:06:57 mlemos Exp $
*
*/
define('MIME_PARSER_START', 1);
define('MIME_PARSER_HEADER', 2);
define('MIME_PARSER_HEADER_VALUE', 3);
define('MIME_PARSER_BODY', 4);
define('MIME_PARSER_BODY_START', 5);
define('MIME_PARSER_BODY_DATA', 6);
define('MIME_PARSER_BODY_DONE', 7);
define('MIME_PARSER_END', 8);
define('MIME_MESSAGE_START', 1);
define('MIME_MESSAGE_GET_HEADER_NAME', 2);
define('MIME_MESSAGE_GET_HEADER_VALUE', 3);
define('MIME_MESSAGE_GET_BODY', 4);
define('MIME_MESSAGE_GET_BODY_PART', 5);
define('MIME_ADDRESS_START', 1);
define('MIME_ADDRESS_FIRST', 2);
/*
{metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?>
<class>
<package>net.manuellemos.mimeparser</package>
<version>@(#) $Id: mime_parser.php,v 1.99 2024/11/03 17:06:57 mlemos Exp $</version>
<copyright>Copyright (C) Manuel Lemos 2006 - 2020</copyright>
<title>MIME parser</title>
<author>Manuel Lemos</author>
<authoraddress>mlemos-at-acm.org</authoraddress>
<documentation>
<idiom>en</idiom>
<purpose>Parse MIME encapsulated e-mail message data compliant with
the RFC 2822 or aggregated in mbox format.</purpose>
<usage>Use the function <functionlink>Decode</functionlink> function
to retrieve the structure of the messages to be parsed. Adjust its
parameters to tell how to return the decoded body data.
Use the <tt>SaveBody</tt> parameter to make the body parts be saved
to files when the message is larger than the available memory. Use
the <tt>SkipBody</tt> parameter to just retrieve the message
structure without returning the body data.<paragraphbreak />
If the message data is an archive that may contain multiple messages
aggregated in the mbox format, set the variable
<variablelink>mbox</variablelink> to <booleanvalue>1</booleanvalue>.</usage>
</documentation>
{/metadocument}
*/
class mime_parser_class
{
/*
{metadocument}
<variable>
<name>error</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>Store the message that is returned when an error
occurs.</purpose>
<usage>Check this variable to understand what happened when a call to
any of the class functions has failed.<paragraphbreak />
This class uses cumulative error handling. This means that if one
class functions that may fail is called and this variable was
already set to an error message due to a failure in a previous call
to the same or other function, the function will also fail and does
not do anything.<paragraphbreak />
This allows programs using this class to safely call several
functions that may fail and only check the failure condition after
the last function call.<paragraphbreak />
Just set this variable to an empty string to clear the error
condition.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $error='';
/*
{metadocument}
<variable>
<name>error_position</name>
<type>INTEGER</type>
<value>-1</value>
<documentation>
<purpose>Point to the position of the message data or file that
refers to the last error that occurred.</purpose>
<usage>Check this variable to determine the relevant position of the
message when a parsing error occurs.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $error_position = -1;
/*
{metadocument}
<variable>
<name>mbox</name>
<type>BOOLEAN</type>
<value>0</value>
<documentation>
<purpose>Specify whether the message data to parse is a single RFC
2822 message or it is an archive that contain multiple messages in
the mbox format.</purpose>
<usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
it is intended to parse an mbox message archive.<br />
mbox archives may contain multiple messages. Each message starts
with the header <tt>From</tt>. Since all valid RFC 2822 headers
must with a colon, the class will fail to parse a mbox archive if
this variable is set to <booleanvalue>0</booleanvalue>.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $mbox = 0;
/*
{metadocument}
<variable>
<name>decode_headers</name>
<type>BOOLEAN</type>
<value>1</value>
<documentation>
<purpose>Specify whether the message headers should be decoded.</purpose>
<usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
necessary to decode message headers that may have non-ASCII
characters and use other character set encodings.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $decode_headers = 1;
/*
{metadocument}
<variable>
<name>decode_bodies</name>
<type>BOOLEAN</type>
<value>1</value>
<documentation>
<purpose>Specify whether the message body parts should be decoded.</purpose>
<usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
necessary to parse the message bodies and extract its part
structure.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $decode_bodies = 1;
/*
{metadocument}
<variable>
<name>extract_addresses</name>
<type>BOOLEAN</type>
<value>1</value>
<documentation>
<purpose>Specify whether the message headers that usually contain
e-mail addresses should be parsed and the addresses should be
extracted by the <functionlink>Decode</functionlink> function.</purpose>
<usage>Set this variable to <booleanvalue>1</booleanvalue> if it is
necessary to extract the e-mail addresses contained in certain
message headers.<paragraphbreak />
The headers to be parsed are defined by the
<variablelink>address_headers</variablelink> variable.<paragraphbreak />
The parsed addresses are returned by the
<tt>ExtractedAddresses</tt> entry of the <argumentlink>
<function>Decode</function>
<argument>decoded</argument>
</argumentlink> argument of the
<functionlink>Decode</functionlink> function.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $extract_addresses = 1;
/*
{metadocument}
<variable>
<name>address_headers</name>
<type>HASH</type>
<value></value>
<documentation>
<purpose>Specify which headers contain addresses that should be
parsed and extracted.</purpose>
<usage>Change this variable if you need to extract e-mail addresses
from a different list of message headers.<paragraphbreak />
It must be set to an associative array with keys set to the names
of the headers to be parsed including the colon. The array values
must be set to a boolean flag to tell whether the headers with the
respective name should be parsed. The header names must be in lower
case.<paragraphbreak />
By default the class addresses from the headers:
<stringvalue>from:</stringvalue>, <stringvalue>to:</stringvalue>,
<stringvalue>cc:</stringvalue>, <stringvalue>bcc:</stringvalue>,
<stringvalue>return-path:</stringvalue>,
<stringvalue>reply-to:</stringvalue> and
<stringvalue>disposition-notification-to:</stringvalue>.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $address_headers = array(
'from:' => 1,
'to:' => 1,
'cc:' => 1,
'bcc:' => 1,
'return-path:'=>1,
'reply-to:'=>1,
'disposition-notification-to:'=>1
);
/*
{metadocument}
<variable>
<name>message_buffer_length</name>
<type>INTEGER</type>
<value>8000</value>
<documentation>
<purpose>Maximum length of the chunks of message data that the class
parse at one time.</purpose>
<usage>Adjust this value according to the available memory.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $message_buffer_length = 8000;
/* HESK: count accepted attachment-like body parts while decoding, so disabled,
* disallowed, excess, or oversized attachments can be skipped before they are
* fully written to disk. */
var $hesk_attachment_part_count = 0;
/*
{metadocument}
<variable>
<name>ignore_syntax_errors</name>
<type>BOOLEAN</type>
<value>1</value>
<documentation>
<purpose>Specify whether the class should ignore syntax errors in
malformed messages.</purpose>
<usage>Set this variable to <booleanvalue>0</booleanvalue> if it is
necessary to verify whether message data may be corrupted due to
to eventual bugs in the program that generated the
message.<paragraphbreak />
Currently the class only ignores some types of syntax errors.
Other syntax errors may still cause the
<functionlink>Decode</functionlink> to fail.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $ignore_syntax_errors=1;
/*
{metadocument}
<variable>
<name>warnings</name>
<type>HASH</type>
<value></value>
<documentation>
<purpose>Return a list of positions of the original message that
contain syntax errors.</purpose>
<usage>Check this variable to retrieve eventual message syntax
errors that were ignored when the
<variablelink>ignore_syntax_errors</variablelink> is set to
<booleanvalue>1</booleanvalue>.<paragraphbreak />
The indexes of this array are the positions of the errors. The
array values are the corresponding syntax error messages.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $warnings=array();
/*
{metadocument}
<variable>
<name>track_lines</name>
<type>BOOLEAN</type>
<value>0</value>
<documentation>
<purpose>Tell the class to keep track the position of each message
line.</purpose>
<usage>Set this variable to <integervalue>1</integervalue> if you
need to determine the line and column number associated to a given
position of the parsed message.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $track_lines = 0;
/*
{metadocument}
<variable>
<name>use_part_file_names</name>
<type>BOOLEAN</type>
<value>0</value>
<documentation>
<purpose>Tell the class to try to use the original message
attachment file names when saving body parts to files.</purpose>
<usage>Set this variable to <integervalue>1</integervalue> if you
would like to have attachments be saved to file with the names
that were used when the message was sent.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $use_part_file_names = 0;
/*
{metadocument}
<variable>
<name>custom_mime_types</name>
<type>HASH</type>
<value></value>
<documentation>
<purpose>List of MIME types not yet recognized by the
<functionlink>Analyze</functionlink> function that applications
may define.</purpose>
<usage>Set this variable to an associative array of custom MIME
types that your application recognizes, so the
<functionlink>Analyze</functionlink> function does not fail when
message parts are found with those MIME types.<paragraphbreak />
The indexes of this array are the MIME type names with lower case
letters. The array values are also associative arrays with an
entry named <tt>Type</tt> that defines the type of content and
another entry named <tt>Description</tt> that defines the content
type description.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $custom_mime_types = array();
/* Private variables */
var $state = MIME_PARSER_START;
var $buffer = '';
var $buffer_position = 0;
var $offset = 0;
var $parts = array();
var $part_position = 0;
var $headers = array();
var $body_parser;
var $body_parser_state = MIME_PARSER_BODY_DONE;
var $body_buffer = '';
var $body_buffer_position = 0;
var $body_offset = 0;
var $current_header = '';
var $file;
var $body_file;
var $position = 0;
var $body_part_number = 1;
var $next_token = '';
var $lines = array();
var $line_offset = 0;
var $last_line = 1;
var $last_carriage_return = 0;
var $header_name_characters = '!"#$%&\'()*+,-./0123456789;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}';
var $message_position = 0;
var $warn_unexpected_quoted_printable_characters = 0;
/* Private functions */
Function SetError($error)
{
$this->error = $error;
return(0);
}
Function SetErrorWithContact($error)
{
return($this->SetError($error.'. Please contact the author Manuel Lemos <mlemos@acm.org> and send a copy of this message to let him add support for this kind of messages'));
}
Function SetPositionedError($error, $position)
{
$this->error_position = $position;
return($this->SetError($error));
}
Function SetPositionedWarning($error, $position)
{
if(!$this->ignore_syntax_errors)
return($this->SetPositionedError($error, $position));
$this->warnings[$position]=$error;
return(1);
}
Function SetPHPError($error)
{
$php_error = error_get_last();
if(IsSet($php_error))
$error .= ': '.$php_error['message'];
return($this->SetError($error));
}
Function ResetParserState()
{
$this->error='';
$this->error_position = -1;
$this->state = MIME_PARSER_START;
$this->buffer = '';
$this->buffer_position = 0;
$this->offset = 0;
$this->parts = array();
$this->part_position = 0;
$this->headers = array();
$this->body_parser_state = MIME_PARSER_BODY_DONE;
$this->body_buffer = '';
$this->body_buffer_position = 0;
$this->body_offset = 0;
$this->current_header = '';
$this->position = 0;
$this->body_part_number = 1;
$this->next_token = '';
$this->lines = ($this->track_lines ? array(0 => 0) : array());
$this->line_offset = 0;
$this->last_line = 0;
$this->last_carriage_return = 0;
}
Function Tokenize($string,$separator="")
{
if(!strcmp($separator,""))
{
$separator=$string;
$string=$this->next_token;
}
for($character=0;$character<strlen($separator);$character++)
{
if(GetType($position=strpos($string,$separator[$character]))=='integer')
$found=(IsSet($found) ? min($found,$position) : $position);
}
if(IsSet($found))
{
$this->next_token=substr($string,$found+1);
return(substr($string,0,$found));
}
else
{
$this->next_token='';
return($string);
}
}
Function ParseStructuredHeader($value, &$type, &$parameters, &$character_sets, &$languages)
{
$type = strtolower(trim($this->Tokenize($value, ';')));
$p = trim($this->Tokenize(''));
$parameters = $character_sets = $languages = array();
while(strlen($p))
{
$parameter = trim(strtolower($this->Tokenize($p, '=')));
$remaining = trim($this->Tokenize(''));
$is_quoted = false;
if(strlen($remaining)
&& !strcmp($remaining[0], '"'))
{
for($position = 1; $position < strlen($remaining);)
{
if(GetType($quote = strpos($remaining, '"', $position)) !== 'integer')
break;
if($quote === 2
|| $remaining[$quote - 1] !== '\\')
{
$is_quoted = true;
break;
}
$position = $quote + 1;
}
if($is_quoted)
{
$value = substr($remaining, 1, $quote - 1);
$p = trim(substr($remaining, $quote + 1));
if(strlen($p) > 0
&& !strcmp($p[0], ';'))
$p = substr($p, 1);
}
}
if(!$is_quoted)
{
$value = trim($this->Tokenize($remaining, ';'));
$p = trim($this->Tokenize(''));
}
if(strpos($value, '\\') !== 0)
$value = preg_replace('/(\\\\)(.)/', '\\2', $value);
if(($l=strlen($parameter))
&& !strcmp($parameter[$l - 1],'*'))
{
$parameter=$this->Tokenize($parameter, '*');
if(IsSet($parameters[$parameter])
&& IsSet($character_sets[$parameter]))
$value = $parameters[$parameter] . UrlDecode($value);
else
{
$character_sets[$parameter] = strtolower($this->Tokenize($value, '\''));
$languages[$parameter] = $this->Tokenize('\'');
$value = UrlDecode($this->Tokenize(''));
}
}
$parameters[$parameter] = $value;
}
}
Function FindStringLineBreak($string, $position, &$break, &$line_break)
{
if(GetType($line_break=strpos($string, $break="\r", $position))=='integer')
{
if(GetType($new_line_break=strpos($string, "\n", $position))=='integer')
{
if($new_line_break < $line_break)
{
$break = "\n";
$line_break = $new_line_break;
return(1);
}
}
if($line_break>$position
&& $string[$line_break-1]=="\r")
{
$line_break--;
$break="\r\n";
}
return(1);
}
return(GetType($line_break=strpos($string, $break="\n", $position))=='integer');
}
Function FindLineBreak($position, &$break, &$line_break)
{
if(GetType($line_break=strpos($this->buffer, $break="\r", $position))=='integer')
{
if(GetType($new_line_break=strpos($this->buffer, "\n", $position))=='integer')
{
if($new_line_break < $line_break)
{
$break = "\n";
$line_break = $new_line_break;
return(1);
}
}
if(($n = $line_break + 1) < strlen($this->buffer)
&& $this->buffer[$n]=="\n")
$break="\r\n";
return(1);
}
return(GetType($line_break=strpos($this->buffer, $break="\n", $position))=='integer');
}
Function FindBodyLineBreak($position, &$break, &$line_break)
{
/*
* Is there a carriage return in the body buffer?
*
*/
if(GetType($line_break=strpos($this->body_buffer, $break="\r", $position))=='integer')
{
/*
* Yes
*
* Is there also a line feed in the body buffer?
*
*/
if(GetType($new_line_break=strpos($this->body_buffer, "\n", $position))=='integer')
{
/*
* Yes
*
* Is the line feed before the carriage return?
*
*/
if($new_line_break < $line_break)
{
/*
* Yes
*
* Return the position of the line feed
*/
$break = "\n";
$line_break = $new_line_break;
return(1);
}
}
/*
* Is the carriage return before the last character
* and the next character is line feed?
*
*/
if(($n = $line_break + 1) < strlen($this->body_buffer)
&& $this->body_buffer[$n]=="\n")
{
/*
* Yes
*
* Return the position of the carriage return.
*/
$break="\r\n";
}
return(1);
}
return(GetType($line_break=strpos($this->body_buffer, $break="\n", $position))=='integer');
}
Function ParseHeaderString($body, &$position, &$headers)
{
$l = strlen($body);
$headers = array();
for(;$position < $l;)
{
if($this->FindStringLineBreak($body, $position, $break, $line_break))
{
$line = substr($body, $position, $line_break - $position);
$position = $line_break + strlen($break);
}
else
{
$line = substr($body, $position);
$position = $l;
}
if(strlen($line)==0)
break;
$h = strtolower(strtok($line,':'));
$headers[$h] = trim(strtok(''));
}
}
Function ParsePart($end, &$part, &$need_more_data)
{
$need_more_data = 0;
switch($this->state)
{
case MIME_PARSER_START:
$part=array(
'Type'=>'MessageStart',
'Position'=>$this->offset + $this->buffer_position
);
$this->state = MIME_PARSER_HEADER;
break;
case MIME_PARSER_HEADER:
if($this->FindLineBreak($this->buffer_position, $break, $line_break))
{
$next = $line_break + strlen($break);
if(!strcmp($break,"\r")
&& strlen($this->buffer) == $next
&& !$end)
{
$need_more_data = 1;
break;
}
if($line_break==$this->buffer_position)
{
$part=array(
'Type'=>'BodyStart',
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = $next;
$this->state = MIME_PARSER_BODY;
break;
}
}
if(($break = strspn($this->buffer, $this->header_name_characters, $this->buffer_position) + $this->buffer_position) < strlen($this->buffer))
{
switch($this->buffer[$break])
{
case ':':
$next = $break + 1;
break;
case ' ':
if(substr($this->buffer, $this->buffer_position, $break - $this->buffer_position)==='From')
{
$next = $break + 1;
break;
}
default:
if(!$this->SetPositionedWarning('message headers do not end with empty line', $this->buffer_position))
return(0);
$part=array(
'Type'=>'BodyStart',
'Position'=>$this->offset + $this->buffer_position
);
$this->state = MIME_PARSER_BODY;
break 2;
}
}
else
{
$need_more_data = !$end;
if($end)
{
$part=array(
'Type'=>'BodyStart',
'Position'=>$this->offset + $this->buffer_position
);
$this->state = MIME_PARSER_BODY;
}
break;
}
$part=array(
'Type'=>'HeaderName',
'Name'=>substr($this->buffer, $this->buffer_position, $next - $this->buffer_position),
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = $next;
$this->state = MIME_PARSER_HEADER_VALUE;
break;
case MIME_PARSER_HEADER_VALUE:
$position = $this->buffer_position;
$value = '';
for(;;)
{
if($this->FindLineBreak($position, $break, $line_break))
{
$next = $line_break + strlen($break);
$line = substr($this->buffer, $position, $line_break - $position);
if(strlen($this->buffer) == $next)
{
if(!$end)
{
$need_more_data = 1;
break 2;
}
$value .= $line;
$part=array(
'Type'=>'HeaderValue',
'Value'=>$value,
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = $next;
$this->state = MIME_PARSER_END;
break ;
}
else
{
$character = $this->buffer[$next];
if(!strcmp($character, ' ')
|| !strcmp($character, "\t"))
{
$value .= $line;
$position = $next;
}
else
{
$value .= $line;
$part=array(
'Type'=>'HeaderValue',
'Value'=>$value,
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = $next;
$this->state = MIME_PARSER_HEADER;
break 2;
}
}
}
else
{
if(!$end)
{
$need_more_data = 1;
break;
}
else
{
$value .= substr($this->buffer, $position);
$part=array(
'Type'=>'HeaderValue',
'Value'=>$value,
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = strlen($this->buffer);
$this->state = MIME_PARSER_END;
break;
}
}
}
break;
case MIME_PARSER_BODY:
if($this->mbox)
{
$add = 0;
$append='';
if($this->FindLineBreak($this->buffer_position, $break, $line_break))
{
$next = $line_break + strlen($break);
$following = $next + strlen($break);
if($following >= strlen($this->buffer)
|| GetType($line=strpos($this->buffer, $break, $following))!='integer')
{
if(!$end)
{
$need_more_data = 1;
break;
}
}
$start = substr($this->buffer, $next, strlen($break.'From '));
if(!strcmp($break.'From ', $start))
{
if($line_break == $this->buffer_position)
{
$part=array(
'Type'=>'MessageEnd',
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = $following;
$this->state = MIME_PARSER_START;
break;
}
else
$add = strlen($break);
$next = $line_break;
}
elseif(($indent = strspn($this->buffer, '>', $next)) > 0)
{
$start = substr($this->buffer, $next + $indent, strlen('From '));
if(!strcmp('From ', $start))
{
$part=array(
'Type'=>'BodyData',
'Data'=>substr($this->buffer, $this->buffer_position, $next - $this->buffer_position),
'Position'=>$this->offset + $this->buffer_position
);
$this->buffer_position = $next + 1;
break;
}
}
}
else
{
if(!$end)
{
$need_more_data = 1;
break;
}
$next = strlen($this->buffer);
$append="\r\n";
}
if($next > $this->buffer_position)
{
$part=array(
'Type'=>'BodyData',
'Data'=>substr($this->buffer, $this->buffer_position, $next + $add - $this->buffer_position).$append,
'Position'=>$this->offset + $this->buffer_position
);
}
elseif($end)
{
$part=array(
'Type'=>'MessageEnd',
'Position'=>$this->offset + $this->buffer_position
);
$this->state = MIME_PARSER_END;
}
$this->buffer_position = $next;
}
else
{
if(strlen($this->buffer)-$this->buffer_position)
{
$data=substr($this->buffer, $this->buffer_position, strlen($this->buffer) - $this->buffer_position);
$end_line = (!strcmp(substr($data,-1),"\n") || !strcmp(substr($data,-1),"\r"));
if($end
&& !$end_line)
{
$data.="\n";
$end_line = 1;
}
$offset = $this->offset + $this->buffer_position;
$this->buffer_position = strlen($this->buffer);
$need_more_data = !$end;
if(!$end_line)
{
if(GetType($line_break=strrpos($data, "\n"))=='integer'
|| GetType($line_break=strrpos($data, "\r"))=='integer')
{
$line_break++;
$this->buffer_position -= strlen($data) - $line_break;
$data = substr($data, 0, $line_break);
}
else
{
$this->buffer_position -= strlen($data);
$data = '';
}
}
$part=array(
'Type'=>'BodyData',
'Data'=>$data,
'Position'=>$offset
);
}
else
{
if($end)
{
$part=array(
'Type'=>'MessageEnd',
'Position'=>$this->offset + $this->buffer_position
);
$this->state = MIME_PARSER_END;
}
else
$need_more_data = 1;
}
}
break;
default:
return($this->SetPositionedError($this->state.' is not a valid parser state', $this->buffer_position));
}
return(1);
}
Function QueueBodyParts()
{
for(;;)
{
if(!$this->body_parser->GetPart($part,$end))
return($this->SetError($this->body_parser->error));
if($end)
return(1);
if(!IsSet($part['Part']))
$part['Part']=$this->headers['Boundary'];
$this->parts[]=$part;
}
}
Function ParseParameters($value, &$first, &$parameters, $return)
{
$first = strtolower(trim(strtok($value, ';')));
$values = trim(strtok(''));
$parameters = array();
$return_value = '';
while(strlen($values))
{
$parameter = trim(strtolower(strtok($values, '=')));
$value = trim(strtok(';'));
$l = strlen($value);
if($l > 1
&& !strcmp($value[0], '"')
&& !strcmp($value[$l - 1], '"'))
$value = substr($value, 1, $l - 2);
$parameters[$parameter] = $value;
if(!strcmp($parameter, $return))
$return_value = $value;
$values = trim(strtok(''));
}
return($return_value);
}
Function ParseBody($data, $end, $offset)
{
$success = $this->body_parser->Parse($data, $end);
$tw = count($this->body_parser->warnings);
for(Reset($this->body_parser->warnings), $w = 0; $w < $tw; ++$w, Next($this->body_parser->warnings))
{
$position = Key($this->body_parser->warnings);
$this->SetPositionedWarning($this->body_parser->warnings[$position], $offset + $position + $this->body_parser->message_position);
}
if(!$success)
{
if(($this->error_position = $this->body_parser->error_position) != -1)
$this->body_parser->error_position += $offset + $this->body_parser->message_position;
return($this->SetError($this->body_parser->error));
}
return(1);
}
Function TrimEncodedWord($not_encoded)
{
if(strspn($not_encoded, " \t") == strlen($not_encoded))
$not_encoded = '';
return $not_encoded;
}
Function DecodePart($part)
{
switch($part['Type'])
{
case 'MessageStart':
$this->headers=array();
break;
case 'HeaderName':
if($this->decode_bodies)
$this->current_header = strtolower($part['Name']);
break;
case 'HeaderValue':
if($this->decode_headers)
{
$value = $part['Value'];
$error = '';
for($decoded_header = array(), $position = 0; $position<strlen($value); )
{
if(GetType($encoded=strpos($value,'=?', $position))!='integer')
{
if($position<strlen($value))
{
$not_encoded = $this->TrimEncodedWord(substr($value, $position));
if(count($decoded_header))
$decoded_header[count($decoded_header)-1]['Value'].=$not_encoded;
else
{
$decoded_header[]=array(
'Value'=>$not_encoded,
'Encoding'=>'ASCII'
);
}
}
break;
}
$set = $encoded + 2;
if(GetType($method=strpos($value,'?', $set))!='integer')
{
$error = 'invalid header encoding syntax '.$part['Value'];
$error_position = $part['Position'] + $set;
break;
}
$encoding=strtoupper(substr($value, $set, $method - $set));
$method += 1;
if(GetType($data=strpos($value,'?', $method))!='integer')
{
$error = 'invalid header encoding syntax '.$part['Value'];
$error_position = $part['Position'] + $set;
break;
}
$start = $data + 1;
if(GetType($end=strpos($value,'?=', $start))!='integer')
{
$error = 'invalid header encoding syntax '.$part['Value'];
$error_position = $part['Position'] + $start;
break;
}
if($encoded > $position)
{
$not_encoded = $this->TrimEncodedWord(substr($value, $position, $encoded - $position));
if(count($decoded_header))
$decoded_header[count($decoded_header)-1]['Value'].= $not_encoded;
else
{
$decoded_header[]=array(
'Value'=>$not_encoded,
'Encoding'=>'ASCII'
);
}
}
switch(strtolower(substr($value, $method, $data - $method)))
{
case 'q':
if($end>$start)
{
for($decoded = '', $position = $start; $position < $end ; )
{
switch($value[$position])
{
case '=':
$h = HexDec($hex = strtolower(substr($value, $position+1, 2)));
if($end - $position < 3
|| strcmp(sprintf('%02x', $h), $hex))
{
$warning = 'the header specified an invalid encoded character';
$warning_position = $part['Position'] + $position + 1;
if($this->ignore_syntax_errors)
{
$this->SetPositionedWarning($warning, $warning_position);
$decoded .= '=';
$position ++;
}
else
{
$error = $warning;
$error_position = $warning_position;
break 4;
}
}
else
{
$decoded .= Chr($h);
$position += 3;
}
break;
case '_':
$decoded .= ' ';
$position++;
break;
default:
$decoded .= $value[$position];
$position++;
break;
}
}
if(count($decoded_header)
&& (!strcmp($decoded_header[$last = count($decoded_header)-1]['Encoding'], 'ASCII')
|| !strcmp($decoded_header[$last]['Encoding'], $encoding)))
{
$decoded_header[$last]['Value'].= $decoded;
$decoded_header[$last]['Encoding']= $encoding;
}
else
{
$decoded_header[]=array(
'Value'=>$decoded,
'Encoding'=>$encoding
);
}
}
break;
case 'b':
$decoded=base64_decode(substr($value, $start, $end - $start));
if($end <= $start
|| GetType($decoded) != 'string'
|| strlen($decoded) == 0)
{
$warning = 'the header specified an invalid base64 encoded text';
$warning_position = $part['Position'] + $start;
if($this->ignore_syntax_errors)
$this->SetPositionedWarning($warning, $warning_position);
else
{
$error = $warning;
$error_position = $warning_position;
break 2;
}
}
if(count($decoded_header)
&& (!strcmp($decoded_header[$last = count($decoded_header)-1]['Encoding'], 'ASCII')
|| !strcmp($decoded_header[$last]['Encoding'], $encoding)))
{
$decoded_header[$last]['Value'].= $decoded;
$decoded_header[$last]['Encoding']= $encoding;
}
else
{
$decoded_header[]=array(
'Value'=>$decoded,
'Encoding'=>$encoding
);
}
break;
default:
$error = 'the header specified an unsupported encoding method';
$error_position = $part['Position'] + $method;
break 2;
}
$position = $end + 2;
if(($space = strspn($value, " \t", $position)) > 0)
$position += $space - 1;
}
if(strlen($error)==0
&& count($decoded_header))
$part['Decoded']=$decoded_header;
}
if($this->decode_bodies
|| $this->decode_headers)
{
switch($this->current_header)
{
case 'content-type:':
$boundary = $this->ParseParameters($part['Value'], $type, $parameters, 'boundary');
$this->headers['Type'] = $type;
if($this->decode_headers)
{
$part['MainValue'] = $type;
$part['Parameters'] = $parameters;
}
if(!strcmp(strtok($type, '/'), 'multipart'))
{
$this->headers['Multipart'] = 1;
if(strlen($boundary))
$this->headers['Boundary'] = $boundary;
else
return($this->SetPositionedError('multipart content-type header does not specify the boundary parameter', $part['Position']));
}
break;
case 'content-transfer-encoding:':
switch($this->headers['Encoding']=strtolower(trim($part['Value'])))
{
case 'quoted-printable':
$this->headers['QuotedPrintable'] = 1;
break;
case '7 bit':
case '8 bit':
if(!$this->SetPositionedWarning('"'.$this->headers['Encoding'].'" is an incorrect content transfer encoding type', $part['Position']))
return(0);
case '7bit':
case '8bit':
case 'binary':
break;
case 'base64':
$this->headers['Base64']=1;
break;
default:
if(!$this->SetPositionedWarning('decoding '.$this->headers['Encoding'].' encoded bodies is not yet supported', $part['Position']))
return(0);
}
break;
}
}
break;
case 'BodyStart':
if($this->decode_bodies
&& IsSet($this->headers['Multipart']))
{
$this->body_parser_state = MIME_PARSER_BODY_START;
$this->body_buffer = '';
$this->body_buffer_position = 0;
}
break;
case 'MessageEnd':
if($this->decode_bodies
&& IsSet($this->headers['Multipart'])
&& $this->body_parser_state != MIME_PARSER_BODY_DONE)
{
if($this->body_parser_state != MIME_PARSER_BODY_DATA)
return($this->SetPositionedError('incomplete message body part', $part['Position']));
if(!$this->SetPositionedWarning('truncated message body part', $part['Position']))
return(0);
}
break;
case 'BodyData':
if($this->decode_bodies)
{
if(strlen($this->body_buffer)==0)
{
$this->body_buffer = $part['Data'];
$this->body_offset = $part['Position'];
}
else
$this->body_buffer .= $part['Data'];
if(IsSet($this->headers['Multipart']))
{
$boundary = '--'.$this->headers['Boundary'];
switch($this->body_parser_state)
{
case MIME_PARSER_BODY_START:
for($position = $this->body_buffer_position; ;)
{
if(!$this->FindBodyLineBreak($position, $break, $line_break))
return(1);
$next = $line_break + strlen($break);
if(!strcmp(rtrim(substr($this->body_buffer, $position, $line_break - $position)), $boundary))
{
$part=array(
'Type'=>'StartPart',
'Part'=>$this->headers['Boundary'],
'Position'=>$this->body_offset + $next
);
$this->parts[]=$part;
UnSet($this->body_parser);
$this->body_parser = new mime_parser_class;
$this->body_parser->decode_bodies = 1;
$this->body_parser->decode_headers = $this->decode_headers;
$this->body_parser->mbox = 0;
$this->body_parser_state = MIME_PARSER_BODY_DATA;
$this->body_buffer = substr($this->body_buffer, $next);
$this->body_offset += $next;
$this->body_buffer_position = 0;
break;
}
else
$position = $next;
}
case MIME_PARSER_BODY_DATA:
for($position = $this->body_buffer_position; ;)
{
if(!$this->FindBodyLineBreak($position, $break, $line_break))
{
if($position > 0)
{
if(!$this->ParseBody(substr($this->body_buffer, 0, $position), 0, $this->body_offset))
return(0);
if(!$this->QueueBodyParts())
return(0);
}
$this->body_buffer = substr($this->body_buffer, $position);
$this->body_buffer_position = 0;
$this->body_offset += $position;
return(1);
}
$next = $line_break + strlen($break);
$line = rtrim(substr($this->body_buffer, $position, $line_break - $position));
if(!strcmp($line, $boundary.'--'))
{
if(!$this->ParseBody(substr($this->body_buffer, 0, $position), 1, $this->body_offset))
return(0);
if(!$this->QueueBodyParts())
return(0);
$part=array(
'Type'=>'EndPart',
'Part'=>$this->headers['Boundary'],
'Position'=>$this->body_offset + $position
);
$this->body_buffer = substr($this->body_buffer, $next);
$this->body_buffer_position = 0;
$this->body_offset += $next;
$this->body_parser_state = MIME_PARSER_BODY_DONE;
break 2;
}
elseif(!strcmp($line, $boundary))
{
if(!$this->ParseBody(substr($this->body_buffer, 0, $position), 1, $this->body_offset))
return(0);
if(!$this->QueueBodyParts())
return(0);
$part=array(
'Type'=>'EndPart',
'Part'=>$this->headers['Boundary'],
'Position'=>$this->body_offset + $position
);
$this->parts[] = $part;
$part=array(
'Type'=>'StartPart',
'Part'=>$this->headers['Boundary'],
'Position'=>$this->body_offset + $next
);
$this->parts[] = $part;
UnSet($this->body_parser);
$this->body_parser = new mime_parser_class;
$this->body_parser->decode_bodies = 1;
$this->body_parser->decode_headers = $this->decode_headers;
$this->body_parser->mbox = 0;
$this->body_buffer = substr($this->body_buffer, $next);
$this->body_buffer_position = 0;
$this->body_offset += $next;
$position=0;
continue;
}
$position = $next;
}
break;
case MIME_PARSER_BODY_DONE:
return(1);
default:
return($this->SetPositionedError($this->state.' is not a valid body parser state', $this->body_buffer_position));
}
}
elseif(IsSet($this->headers['QuotedPrintable']))
{
for($end = strlen($this->body_buffer), $decoded = '', $position = $this->body_buffer_position; $position < $end; )
{
if(GetType($equal = strpos($this->body_buffer, '=', $position))!='integer')
{
$decoded .= substr($this->body_buffer, $position);
$position = $end;
break;
}
$next = $equal + 1;
switch($end - $equal)
{
case 1:
$decoded .= substr($this->body_buffer, $position, $equal - $position);
$position = $equal;
break 2;
case 2:
$decoded .= substr($this->body_buffer, $position, $equal - $position);
if(!strcmp($this->body_buffer[$next],"\n"))
$position = $end;
else
$position = $equal;
break 2;
}
if(!strcmp(substr($this->body_buffer, $next, 2), $break="\r\n")
|| !strcmp($this->body_buffer[$next], $break="\n")
|| !strcmp($this->body_buffer[$next], $break="\r"))
{
$decoded .= substr($this->body_buffer, $position, $equal - $position);
$position = $next + strlen($break);
continue;
}
$decoded .= substr($this->body_buffer, $position, $equal - $position);
$hex=strtolower(substr($this->body_buffer, $next, 2));
if(!preg_match('/^[0-9a-f]{2}$/', $hex))
{
if($this->warn_unexpected_quoted_printable_characters
&& !$this->SetPositionedWarning('the body specified an invalid quoted-printable encoded character', $this->body_offset + $next))
return(0);
$decoded.='=';
$position=$next;
}
else
{
$decoded .= Chr(HexDec($hex));
$position = $equal + 3;
}
}
if(strlen($decoded)==0)
{
$this->body_buffer_position = $position;
return(1);
}
$part['Data'] = $decoded;
$this->body_buffer = substr($this->body_buffer, $position);
$this->body_buffer_position = 0;
$this->body_offset += $position;
}
elseif(IsSet($this->headers['Base64']))
{
$part['Data'] = base64_decode($this->body_buffer_position ? substr($this->body_buffer,$this->body_buffer_position) : $this->body_buffer);
$this->body_offset += strlen($this->body_buffer) - $this->body_buffer_position;
$this->body_buffer_position = 0;
$this->body_buffer = '';
}
else
{
$part['Data'] = substr($this->body_buffer, $this->body_buffer_position);
$this->body_buffer_position = 0;
$this->body_buffer = '';
}
}
break;
}
$this->parts[]=$part;
return(1);
}
Function HeskGetDecodedHeaderValue($decoded, $header)
{
if(!IsSet($decoded['Headers'][$header]))
return('');
$value = $decoded['Headers'][$header];
if(GetType($value) == 'array')
$value = count($value) ? $value[0] : '';
return(strtolower(trim($value)));
}
Function HeskGetDecodedContentType($decoded)
{
$content_type = $this->HeskGetDecodedHeaderValue($decoded, 'content-type:');
if(!strlen($content_type))
return('');
$semicolon = strpos($content_type, ';');
if(GetType($semicolon) == 'integer')
$content_type = substr($content_type, 0, $semicolon);
return(trim($content_type));
}
Function HeskGetDecodedAttachmentExtension($decoded)
{
if(IsSet($decoded['FileName']) && strlen(trim($decoded['FileName'])))
{
$filename = trim($decoded['FileName']);
$dot = strrpos($filename, '.');
return(GetType($dot) == 'integer' ? strtolower(substr($filename, $dot)) : '');
}
if(!strcmp($this->HeskGetDecodedContentType($decoded), 'message/rfc822'))
return('.eml');
return('');
}
Function HeskIsDecodedAttachment($decoded)
{
if(IsSet($decoded['FileName']) && strlen(trim($decoded['FileName'])))
return(1);
if(IsSet($decoded['FileDisposition']))
{
$disposition = strtolower($decoded['FileDisposition']);
if(!strcmp($disposition, 'attachment') || !strcmp($disposition, 'inline'))
return(1);
}
if(!strcmp($this->HeskGetDecodedContentType($decoded), 'message/rfc822'))
return(1);
return(0);
}
Function HeskCloseAndDeleteDecodedBodyFile(&$decoded)
{
if(IsSet($decoded['BodyFile']))
{
if(IsSet($this->body_file) && is_resource($this->body_file))
@fclose($this->body_file);
@unlink($decoded['BodyFile']);
UnSet($decoded['BodyFile']);
}
}
Function HeskShouldSaveDecodedBodyPart($parameters, &$decoded, $part_length)
{
if(!IsSet($parameters['HeskAttachmentRules']) || !$this->HeskIsDecodedAttachment($decoded))
return(1);
if(IsSet($decoded['HeskAttachmentSkip']) && $decoded['HeskAttachmentSkip'])
return(0);
$rules = $parameters['HeskAttachmentRules'];
$already_accepted = (IsSet($decoded['HeskAttachmentAccepted']) && $decoded['HeskAttachmentAccepted']);
/* Only make first-chunk accept/reject decisions here. Later chunks of an
* already accepted attachment must remain writable; otherwise the last
* allowed attachment can be silently truncated when max_number is reached.
* HESK's final max_number enforcement remains in inc/pipe_functions.inc.php,
* after process_results() has established HESK's intended attachment order. */
if(!$already_accepted)
{
$allowed_types = (IsSet($rules['allowed_types']) && GetType($rules['allowed_types']) == 'array') ? $rules['allowed_types'] : array();
$ext = $this->HeskGetDecodedAttachmentExtension($decoded);
if(empty($rules['use'])
|| !strlen($ext)
|| !in_array($ext, $allowed_types))
{
$decoded['HeskAttachmentSkip'] = 1;
return(0);
}
$max_number = IsSet($rules['max_number']) ? intval($rules['max_number']) : 0;
if($max_number > 0 && $this->hesk_attachment_part_count >= $max_number)
{
$decoded['HeskAttachmentSkip'] = 1;
return(0);
}
}
$max_size = IsSet($rules['max_size']) ? intval($rules['max_size']) : 0;
$current_length = IsSet($decoded['BodyLength']) ? $decoded['BodyLength'] : 0;
if($max_size > 0 && $current_length + $part_length > $max_size)
{
$decoded['HeskAttachmentSkip'] = 1;
if($already_accepted && $this->hesk_attachment_part_count > 0)
$this->hesk_attachment_part_count--;
UnSet($decoded['HeskAttachmentAccepted']);
$this->HeskCloseAndDeleteDecodedBodyFile($decoded);
return(0);
}
if(!$already_accepted)
{
$decoded['HeskAttachmentAccepted'] = 1;
$this->hesk_attachment_part_count++;
}
return(1);
}
Function DecodeStream($parameters, $position, &$end_of_message, &$decoded)
{
$this->message_position = $position;
$end_of_message = 1;
$state = MIME_MESSAGE_START;
for(;;)
{
if(!$this->GetPart($part, $end))
return(0);
if($end)
{
if(IsSet($parameters['File']))
{
$end_of_data = feof($this->file);
if($end_of_data)
break;
$data = @fread($this->file, $this->message_buffer_length);
if(GetType($data)!='string')
return($this->SetPHPError('could not read the message file'));
if(substr($data,0,3)==chr(hexdec('EF')).chr(hexdec('BB')).chr(hexdec('BF')))
$data = substr($data,3);
while(!($end_of_data = feof($this->file))
&& substr($data, -1) === "\r")
{
$next = @fread($this->file, 1);
if(GetType($next)!='string')
return($this->SetPHPError('could not read the message file'));
$data .= $next;
}
}
else
{
$end_of_data=($this->position>=strlen($parameters['Data']));
if($end_of_data)
break;
$data = substr($parameters['Data'], $this->position, $this->message_buffer_length);
$this->position += strlen($data);
$end_of_data = ($this->position >= strlen($parameters['Data']));
}
if(!$this->Parse($data, $end_of_data))
return(0);
continue;
}
$type = $part['Type'];
switch($state)
{
case MIME_MESSAGE_START:
switch($type)
{
case 'MessageStart':
$decoded=array(
'Headers'=>array(),
'Parts'=>array(),
'Position'=>$this->message_position,
);
$end_of_message = 0;
$state = MIME_MESSAGE_GET_HEADER_NAME;
continue 3;
case 'MessageEnd':
return($this->SetPositionedWarning('incorrectly ended body part', $part['Position']));
}
break;
case MIME_MESSAGE_GET_HEADER_NAME:
switch($type)
{
case 'HeaderName':
$header = strtolower($part['Name']);
$state = MIME_MESSAGE_GET_HEADER_VALUE;
continue 3;
case 'BodyStart':
$state = MIME_MESSAGE_GET_BODY;
$part_number = 0;
continue 3;
}
break;
case MIME_MESSAGE_GET_HEADER_VALUE:
switch($type)
{
case 'HeaderValue':
$value = trim($part['Value']);
if(!IsSet($decoded['Headers'][$header]))
{
$h = 0;
$decoded['Headers'][$header]=$value;
if($this->extract_addresses
&& IsSet($this->address_headers[$header]))
$decoded['HeaderPositions'][$header] = $part['Position'];
}
elseif(GetType($decoded['Headers'][$header])=='string')
{
$h = 1;
$decoded['Headers'][$header]=array($decoded['Headers'][$header], $value);
if($this->extract_addresses
&& IsSet($this->address_headers[$header]))
$decoded['HeaderPositions'][$header] = array($decoded['HeaderPositions'][$header], $part['Position']);
}
else
{
$h = count($decoded['Headers'][$header]);
$decoded['Headers'][$header][]=$value;
if($this->extract_addresses
&& IsSet($this->address_headers[$header]))
$decoded['HeaderPositions'][$header][] = $part['Position'];
}
if(IsSet($part['Decoded'])
&& (count($part['Decoded'])>1
|| strcmp($part['Decoded'][0]['Encoding'],'ASCII')
|| strcmp($value, trim($part['Decoded'][0]['Value']))))
{
$p=$part['Decoded'];
$p[0]['Value']=ltrim($p[0]['Value']);
$last=count($p)-1;
$p[$last]['Value']=rtrim($p[$last]['Value']);
$decoded['DecodedHeaders'][$header][$h]=$p;
}
switch($header)
{
case 'content-disposition:':
$filename='filename';
break;
case 'content-type:':
if(!IsSet($decoded['FileName']))
{
$filename='name';
break;
}
default:
$filename='';
break;
}
if(strlen($filename))
{
if(IsSet($decoded['DecodedHeaders'][$header][$h])
&& count($decoded['DecodedHeaders'][$header][$h]) == 1)
{
$value = $decoded['DecodedHeaders'][$header][$h][0]['Value'];
$encoding = $decoded['DecodedHeaders'][$header][$h][0]['Encoding'];
}
else
$encoding = '';
$this->ParseStructuredHeader($value, $type, $header_parameters, $character_sets, $languages);
if(IsSet($header_parameters[$filename]))
{
$decoded['FileName']=$header_parameters[$filename];
if(IsSet($character_sets[$filename])
&& strlen($character_sets[$filename]))
$decoded['FileNameCharacterSet']=$character_sets[$filename];
if(IsSet($character_sets['language'])
&& strlen($character_sets['language']))
$decoded['FileNameCharacterSet']=$character_sets[$filename];
if(!IsSet($decoded['FileNameCharacterSet'])
&& strlen($encoding))
$decoded['FileNameCharacterSet'] = $encoding;
if(!strcmp($header, 'content-disposition:'))
$decoded['FileDisposition']=$type;
}
}
$state = MIME_MESSAGE_GET_HEADER_NAME;
continue 3;
}
break;
case MIME_MESSAGE_GET_BODY:
switch($type)
{
case 'BodyData':
if(IsSet($parameters['SaveBody']))
{
if(!$this->HeskShouldSaveDecodedBodyPart($parameters, $decoded, strlen($part['Data'])))
{
if(!IsSet($decoded['BodyPart']))
{
$decoded['BodyPart'] = $this->body_part_number;
$decoded['BodyLength'] = 0;
$this->body_part_number++;
}
$decoded['BodyLength'] += strlen($part['Data']);
continue 3;
}
if(!IsSet($decoded['BodyFile']))
{
$directory_separator=(defined('DIRECTORY_SEPARATOR') ? DIRECTORY_SEPARATOR : '/');
$path = (strlen($parameters['SaveBody']) ? ($parameters['SaveBody'].(strcmp($parameters['SaveBody'][strlen($parameters['SaveBody'])-1], $directory_separator) ? $directory_separator : '')) : '');
$filename = strval($this->body_part_number);
if($this->use_part_file_names
&& !$this->GetPartFileName($decoded, $filename))
return(0);
if(file_exists($path.$filename))
{
if(GetType($dot = strrpos($filename, '.'))=='integer')
{
$base = substr($filename, 0, $dot);
$extension = substr($filename, $dot);
}
else
{
$base = $filename;
$extension = '';
}
$appendix = 0;
do
{
++$appendix;
$filename = $base.$appendix.$extension;
}
while(file_exists($path.$filename));
}
$path .= $filename;
if(!($this->body_file = fopen($path, 'wb')))
return($this->SetPHPError('could not create file '.$path.' to save the message body part'));
$decoded['BodyFile'] = $path;
$decoded['BodyPart'] = $this->body_part_number;
$decoded['BodyLength'] = 0;
$this->body_part_number++;
}
if(strlen($part['Data'])
&& !fwrite($this->body_file, $part['Data']))
{
$this->SetPHPError('could not save the message body part to file '.$decoded['BodyFile']);
fclose($this->body_file);
@unlink($decoded['BodyFile']);
return(0);
}
}
elseif(IsSet($parameters['SkipBody'])
&& $parameters['SkipBody'])
{
if(!IsSet($decoded['BodyPart']))
{
$decoded['BodyPart'] = $this->body_part_number;
$decoded['BodyLength'] = 0;
$this->body_part_number++;
}
}
else
{
if(IsSet($decoded['Body']))
$decoded['Body'].=$part['Data'];
else
{
$decoded['Body']=$part['Data'];
$decoded['BodyPart'] = $this->body_part_number;
$decoded['BodyLength'] = 0;
$this->body_part_number++;
}
}
$decoded['BodyLength'] += strlen($part['Data']);
continue 3;
case 'StartPart':
if(!$this->DecodeStream($parameters, $position + $part['Position'], $end_of_part, $decoded_part))
return(0);
$decoded['Parts'][$part_number]=$decoded_part;
$part_number++;
$state = MIME_MESSAGE_GET_BODY_PART;
continue 3;
case 'MessageEnd':
if(IsSet($decoded['BodyFile']))
fclose($this->body_file);
return(1);
}
break;
case MIME_MESSAGE_GET_BODY_PART:
switch($type)
{
case 'EndPart':
$state = MIME_MESSAGE_GET_BODY;
continue 3;
}
break;
}
return($this->SetError('unexpected decoded message part type '.$type.' in state '.$state));
}
return(1);
}
/* Public functions */
Function GetPartFileName($decoded, &$filename)
{
if(IsSet($decoded['FileName']))
$filename = basename($decoded['FileName']);
return(1);
}
Function Parse($data, $end)
{
if(strlen($this->error))
return(0);
if($this->state==MIME_PARSER_END)
return($this->SetError('the parser already reached the end'));
$length = strlen($data);
if($this->track_lines
&& $length)
{
$line = $this->last_line;
$position = 0;
if($this->last_carriage_return)
{
if($data[0] == "\n")
++$position;
$this->lines[++$line] = $this->line_offset + $position;
$this->last_carriage_return = 0;
}
while($position < $length)
{
$position += strcspn($data, "\r\n", $position) ;
if($position >= $length)
break;
if($data[$position] == "\r")
{
++$position;
if($position >= $length)
{
$this->last_carriage_return = 1;
break;
}
if($data[$position] == "\n")
++$position;
$this->lines[++$line] = $this->line_offset + $position;
}
else
{
++$position;
$this->lines[++$line] = $this->line_offset + $position;
}
}
$this->last_line = $line;
$this->line_offset += $length;
}
$this->buffer .= $data;
do
{
Unset($part);
if(!$this->ParsePart($end, $part, $need_more_data))
return(0);
if(IsSet($part)
&& !$this->DecodePart($part))
return(0);
}
while(!$need_more_data
&& $this->state!=MIME_PARSER_END);
if($end
&& $this->state!=MIME_PARSER_END)
return($this->SetError('reached a premature end of data'));
if($this->buffer_position>0)
{
$this->offset += $this->buffer_position;
$this->buffer = substr($this->buffer, $this->buffer_position);
$this->buffer_position = 0;
}
return(1);
}
Function ParseFile($file)
{
if(strlen($this->error))
return(0);
if(!($stream = @fopen($file, 'r')))
return($this->SetPHPError('Could not open the file '.$file));
for($end = 0;!$end;)
{
if(!($data = @fread($stream, $this->message_buffer_length)))
{
$this->SetPHPError('Could not read the file '.$file);
fclose($stream);
return(0);
}
$end=feof($stream);
if(!$this->Parse($data, $end))
{
fclose($stream);
return(0);
}
}
fclose($stream);
return(1);
}
Function GetPart(&$part, &$end)
{
$end = ($this->part_position >= count($this->parts));
if($end)
{
if($this->part_position)
{
$this->part_position = 0;
$this->parts = array();
}
}
else
{
$part = $this->parts[$this->part_position];
$this->part_position ++;
}
return(1);
}
/*
{metadocument}
<function>
<name>Decode</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Parse and decode message data and retrieve its structure.</purpose>
<usage>Pass an array to the <argumentlink>
<function>Decode</function>
<argument>parameters</argument>
</argumentlink>
parameter to define whether the message data should be read and
parsed from a file or a data string, as well additional parsing
options. The <argumentlink>
<function>Decode</function>
<argument>decoded</argument>
</argumentlink> returns the
data structure of the parsed messages.</usage>
<returnvalue>This function returns <booleanvalue>1</booleanvalue> if
the specified message data is parsed successfully. Otherwise,
check the variables <variablelink>error</variablelink> and
<variablelink>error_position</variablelink> to determine what
error occurred and the relevant message position.</returnvalue>
</documentation>
<argument>
<name>parameters</name>
<type>HASH</type>
<documentation>
<purpose>Associative array to specify parameters for the message
data parsing and decoding operation. Here follows the list of
supported parameters that should be used as indexes of the
array:<paragraphbreak />
<tt>File</tt><paragraphbreak />
Name of the file from which the message data will be read. It
may be the name of a file stream or a remote URL, as long as
your PHP installation is configured to allow accessing remote
files with the <tt>fopen()</tt> function.<paragraphbreak />
<tt>Data</tt><paragraphbreak />
String that specifies the message data. This should be used
as alternative data source for passing data available in memory,
like for instance messages stored in a database that was queried
dynamically and the message data was fetched into a string
variable.<paragraphbreak />
<tt>SaveBody</tt><paragraphbreak />
If this parameter is specified, the message body parts are saved
to files. The path of the directory where the files are saved is
defined by this parameter value. The information about the
message body part structure is returned by the <argumentlink>
<function>Decode</function>
<argument>decoded</argument>
</argumentlink> argument, but it just returns the body data part
file name instead of the actual body data. It is recommended for
retrieving messages larger than the available memory. The names
of the body part files are numbers starting from
<stringvalue>1</stringvalue>.<paragraphbreak />
<tt>SkipBody</tt><paragraphbreak />
If this parameter is set to <booleanvalue>1</booleanvalue>, the
message body parts are skipped. This means the information about
the message body part structure is returned by the <argumentlink>
<function>Decode</function>
<argument>decoded</argument>
</argumentlink> but it does not return any body data. It is
recommended just for parsing messages without the need to
retrieve the message body part data.</purpose>
</documentation>
</argument>
<argument>
<name>decoded</name>
<type>ARRAY</type>
<out />
<documentation>
<purpose>Retrieve the structure of the parsed message headers and
body data.<paragraphbreak />
The argument is used to return by reference an array of message
structure definitions. Each array entry refers to the structure
of each message that is found and parsed successfully.<paragraphbreak />
Each message entry consists of an associative array with several
entries that describe the message structure. Here follows the
list of message structure entries names and the meaning of the
respective values:<paragraphbreak />
<tt>Headers</tt><paragraphbreak />
Associative array that returns the list of all the message
headers. The array entries are the header names mapped to
lower case, including the end colon. The array values are the
respective header raw values without any start or trailing white
spaces. Long header values split between multiple message lines
are gathered in single string without line breaks. If an header
with the same name appears more than once in the message, the
respective value is an array with the values of all of the
header occurrences.<paragraphbreak />
<tt>DecodedHeaders</tt><paragraphbreak />
Associative array that returns the list of all the encoded
message headers when the
<variablelink>decode_headers</variablelink> variable is set. The
array entries are the header names mapped to lower case,
including the end colon. The array values are also arrays that
list only the occurrences of the header that originally were
encoded. Each entry of the decoded header array contains more
associative arrays that describe each part of the decoded
header. Each of those associative arrays have an entry named
<tt>Value</tt> that contains the decoded header part value, and
another entry named <tt>Encoding</tt> that specifies the
character set encoding of the value in upper case.<paragraphbreak />
<tt>ExtractedAddresses</tt><paragraphbreak />
If the <variablelink>extract_addresses</variablelink> variable
is set to <booleanvalue>1</booleanvalue>, this entry is set to an
associative array with the addresses found in the headers
specified by the <variablelink>address_headers</variablelink>
variable.<paragraphbreak />
The parsed addresses found on each header are returned as an
array with the format of the <link>
<data>addresses</data>
<url>rfc822_addresses_class.html#argument_ParseAddressList_addresses</url>
</link> argument of the <link>
<data>ParseAddressList</data>
<url>rfc822_addresses_class.html#function_ParseAddressList</url>
</link> function of the <link>
<data>RFC 822 addresses</data>
<url>rfc822_addresses_class.html</url>
</link> class.<paragraphbreak />
<tt>Parts</tt><paragraphbreak />
If this message content type is multipart, this entry is an
array that describes each of the parts contained in the message
body. Each message part is described by an associative array
with the same structure of a complete message
definition.<paragraphbreak />
<tt>Body</tt><paragraphbreak />
String with the decoded data contained in the message body. If
the <tt>SaveBody</tt> or <tt>SkipBody</tt> parameters are
defined, the <tt>Body</tt> entry is not set.<paragraphbreak />
<tt>BodyFile</tt><paragraphbreak />
Name of the file to which the message body data was saved when
the <tt>SaveBody</tt> parameter is defined.<paragraphbreak />
<tt>BodyLength</tt><paragraphbreak />
Length of the current decoded body part.<paragraphbreak />
<tt>BodyPart</tt><paragraphbreak />
Number of the current message body part.<paragraphbreak />
<tt>FileName</tt><paragraphbreak />
Name of the file for body parts composed from
files.<paragraphbreak />
<tt>FileNameCharacterSet</tt><paragraphbreak />
Character set encoding for file parts with names that may
include non-ASCII characters.<paragraphbreak />
<tt>FileNameLanguage</tt><paragraphbreak />
Language of file parts with names that may include non-ASCII
characters.<paragraphbreak />
<tt>FileDisposition</tt><paragraphbreak />
Disposition of parts which are files. It may be either
<tt><stringvalue>inline</stringvalue></tt> for file parts to be
displayed with the message, or
<tt><stringvalue>attachment</stringvalue></tt>
otherwise.<paragraphbreak />
<tt>Position</tt><paragraphbreak />
Position of the part in terms of bytes since the beginning of
the message.</purpose>
</documentation>
</argument>
<do>
{/metadocument}
*/
Function Decode($parameters, &$decoded)
{
if(IsSet($parameters['File']))
{
if(!($this->file = @fopen($parameters['File'], 'r')))
return($this->SetPHPError('could not open the message file to decode '.$parameters['File']));
}
elseif(IsSet($parameters['Data']))
$this->position = 0;
else
return($this->SetError('it was not specified a valid message to decode'));
$this->warnings = $decoded = array();
$this->ResetParserState();
$addresses = new rfc822_addresses_class;
$addresses->ignore_syntax_errors = $this->ignore_syntax_errors;
for($message = 0; ($success = $this->DecodeStream($parameters, 0, $end_of_message, $decoded_message)) && !$end_of_message; $message++)
{
if($this->extract_addresses)
{
$headers = $decoded_message['Headers'];
$positions = (IsSet($decoded_message['HeaderPositions']) ? $decoded_message['HeaderPositions'] : array());
$th = count($headers);
for(Reset($headers), $h = 0; $h<$th; Next($headers), ++$h)
{
$header = Key($headers);
if(IsSet($this->address_headers[$header])
&& $this->address_headers[$header])
{
$values = (GetType($headers[$header]) == 'array' ? $headers[$header] : array($headers[$header]));
$p = (GetType($positions[$header]) == 'array' ? $positions[$header] : array($positions[$header]));
$tv = count($values);
for($v = 0; $v<$tv; ++$v)
{
$address = trim($values[$v]);
if(strlen($address) === 0)
{
if(!$this->SetPositionedWarning('Address extraction warning from header '.$header.' empty email address', $p[$v]))
return(0);
}
elseif($addresses->ParseAddressList($address, $a))
{
if($v==0)
$decoded_message['ExtractedAddresses'][$header] = $a;
else
{
$tl = count($a);
for($l = 0; $l<$tl; ++$l)
$decoded_message['ExtractedAddresses'][$header][] = $a[$l];
}
$tw = count($addresses->warnings);
for($w = 0, Reset($addresses->warnings); $w < $tw; Next($addresses->warnings), $w++)
{
$warning = Key($addresses->warnings);
if(!$this->SetPositionedWarning('Address extraction warning from header '.$header.' '.$addresses->warnings[$warning], $warning + $p[$v]))
return(0);
}
}
elseif(!$this->SetPositionedWarning('Address extraction error from header '.$header.' '.$addresses->error, $addresses->error_position + $p[$v]))
return(0);
}
}
}
UnSet($decoded_message['HeaderPositions']);
}
$decoded[$message]=$decoded_message;
}
if(IsSet($parameters['File']))
fclose($this->file);
return($success);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
Function CopyAddresses($message, &$results, $header)
{
if(!IsSet($message['Headers'][$header]))
return;
if(!IsSet($message['ExtractedAddresses'][$header]))
{
$parser = new rfc822_addresses_class;
$parser->ignore_syntax_errors = $this->ignore_syntax_errors;
$values = (GetType($message['Headers'][$header]) == 'array' ? $message['Headers'][$header] : array($message['Headers'][$header]));
$tv = count($values);
$addresses = array();
for($v = 0; $v<$tv; ++$v)
{
if($parser->ParseAddressList($values[$v], $a))
{
if($v==0)
$addresses = $a;
else
{
$tl = count($a);
for($l = 0; $l<$tl; ++$l)
$addresses[] = $a[$l];
}
}
}
}
else
$addresses = $message['ExtractedAddresses'][$header];
if(count($addresses))
$results[ucfirst(substr($header, 0, strlen($header) -1))] = $addresses;
}
Function ReadMessageBody($message, &$body, $prefix)
{
if(IsSet($message[$prefix]))
$body = $message[$prefix];
elseif(IsSet($message[$prefix.'File']))
{
$path = $message[$prefix.'File'];
if(!($file = @fopen($path, 'rb')))
return($this->SetPHPError('could not open the message body file '.$path));
for($body = '', $end = 0;!$end;)
{
if(!($data = @fread($file, $this->message_buffer_length)))
{
$this->SetPHPError('Could not open the message body file '.$path);
fclose($stream);
return(0);
}
$end=feof($file);
$body.=$data;
}
fclose($file);
}
else
$body = '';
return(1);
}
/*
{metadocument}
<function>
<name>Analyze</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Analyze a parsed message to describe its contents.</purpose>
<usage>Pass an array to the <argumentlink>
<function>Analyze</function>
<argument>message</argument>
</argumentlink>
parameter with the decoded message array structure returned by the
<functionlink>Decode</functionlink> function. The <argumentlink>
<function>Analyze</function>
<argument>results</argument>
</argumentlink> returns details about the type of message that was
analyzed and its contents.</usage>
<returnvalue>This function returns <booleanvalue>1</booleanvalue> if
the specified message is analyzed successfully. Otherwise,
check the variables <variablelink>error</variablelink> and
<variablelink>error_position</variablelink> to determine what
error occurred.</returnvalue>
</documentation>
<argument>
<name>message</name>
<type>HASH</type>
<documentation>
<purpose>Pass an associative array with the definition of an
individual message returned by the <argumentlink>
<function>Decode</function>
<argument>decoded</argument>
</argumentlink> argument of the
<functionlink>Decode</functionlink> function..</purpose>
</documentation>
</argument>
<argument>
<name>results</name>
<type>HASH</type>
<out />
<documentation>
<purpose>Returns an associative array with the results of the
analysis. Some types of entries are returned for all types of
analyzed messages. Other entries are specific to each type of
message.<paragraphbreak />
<tt>Type</tt><paragraphbreak />
Type of message that was analyzed. Currently it supports the
types: <tt>binary</tt>, <tt>text</tt>, <tt>html</tt>,
<tt>video</tt>, <tt>image</tt>, <tt>audio</tt>, <tt>zip</tt>,
<tt>pdf</tt>, <tt>postscript</tt>, <tt>ms-word</tt>,
<tt>ms-excel</tt>, <tt>ms-powerpoint</tt>, <tt>ms-tnef</tt>,
<tt>odf-writer</tt>, <tt>signature</tt>, <tt>report-type</tt>,
<tt>delivery-status</tt> and <tt>message</tt>.<paragraphbreak />
<tt>SubType</tt><paragraphbreak />
Name of the variant of the message type format.<paragraphbreak />
<tt>Description</tt><paragraphbreak />
Human readable description in English of the message type.<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<b>From message headers:</b><paragraphbreak />
<tt>Encoding</tt><paragraphbreak />
Character set encoding of the message part.<paragraphbreak />
<tt>Subject</tt><paragraphbreak />
The message subject.<paragraphbreak />
<tt>SubjectEncoding</tt><paragraphbreak />
Character set encoding of the message subject.<paragraphbreak />
<tt>Date</tt><paragraphbreak />
The message date.<paragraphbreak />
<tt>From</tt><paragraphbreak />
<tt>To</tt><paragraphbreak />
<tt>Cc</tt><paragraphbreak />
<tt>Bcc</tt><paragraphbreak />
Array of e-mail addresses found in the <tt>From</tt>,
<tt>To</tt>, <tt>Cc</tt>, <tt>Bcc</tt>.<paragraphbreak />
Each of the entries consists of an associative array with an
entry named <tt>address</tt> with the e-mail address and
optionally another named <tt>name</tt> with the associated
name.<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<b>For content message parts:</b><paragraphbreak />
<paragraphbreak />
<tt>Data</tt><paragraphbreak />
String of data of the message part.<paragraphbreak />
<tt>DataFile</tt><paragraphbreak />
File with data of the message part.<paragraphbreak />
<tt>DataLength</tt><paragraphbreak />
Length of the data of the message part.<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<b>For message with embedded files:</b><paragraphbreak />
<paragraphbreak />
<tt>FileName</tt><paragraphbreak />
Original name of the file.<paragraphbreak />
<tt>ContentID</tt><paragraphbreak />
Content identifier of the file to be used in references from
other message parts.<paragraphbreak />
For instance, an HTML message may reference images embedded in
the message using URLs that start with the
<stringvalue>cid:</stringvalue> followed by the content
identifier of the embedded image file part.<paragraphbreak />
<tt>Disposition</tt><paragraphbreak />
Information of whether the embedded file should be displayed
inline when the message is presented, or it is an attachment
file.<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<b>For composite message:</b><paragraphbreak />
<paragraphbreak />
<tt>Attachments</tt><paragraphbreak />
List of files attached to the message.<paragraphbreak />
<tt>Alternative</tt><paragraphbreak />
List of alternative message parts that can be displayed if the
main message type is not supported by the program displaying
the message.<paragraphbreak />
<tt>Related</tt><paragraphbreak />
List of message parts related with the main message type.<paragraphbreak />
It may list for instance embedded images or CSS files related
with an HTML message type.<paragraphbreak />
<paragraphbreak />
<paragraphbreak />
<b>For bounced messages or other types of delivery status report
messages:</b><paragraphbreak />
<paragraphbreak />
<tt>Recipients</tt><paragraphbreak />
List of recipients of the original message.<paragraphbreak />
Each entry contains an associative array that may have the
entries: <tt>Recipient</tt> with the original recipient address,
<tt>Action</tt> with the name action that triggered the delivery
status report, <tt>Status</tt> with the code of the status of
the message delivery.<paragraphbreak />
<tt>Response</tt><paragraphbreak />
Human readable response sent by the server the originated the
report.<paragraphbreak />
</purpose>
</documentation>
</argument>
<do>
{/metadocument}
*/
Function Analyze($message, &$results)
{
$results = array();
if(!IsSet($message['Headers']['content-type:']))
$content_type = 'text/plain';
elseif(gettype($message['Headers']['content-type:']) == 'string')
$content_type = $message['Headers']['content-type:'];
else
{
if(!$this->SetPositionedWarning('message contains multiple content-type headers', $message['Position']))
return(0);
$content_type = $message['Headers']['content-type:'][0];
}
$disposition = $this->ParseParameters($content_type, $content_type, $parameters, 'disposition');
$type = $this->Tokenize($content_type, '/');
$sub_type = $this->Tokenize(';');
$copy_body = 1;
$tolerate_unrecognized = 1;
switch($type)
{
case 'multipart':
$tolerate_unrecognized = 0;
$copy_body = 0;
$lp = count($message['Parts']);
if($lp == 0)
return($this->SetError($this->decode_bodies ? 'No parts were found in the '.$content_type.' part message' : 'It is not possible to analyze multipart messages without parsing the contained message parts. Please set the decode_bodies variable to 1 before parsing the message'));
$parts = array();
for($p = 0; $p < $lp; ++$p)
{
if(!$this->Analyze($message['Parts'][$p], $parts[$p]))
return(0);
}
switch($sub_type)
{
case 'alternative':
$p = $lp;
$results = $parts[--$p];
for(--$p ; $p >=0 ; --$p)
$results['Alternative'][] = $parts[$p];
break;
case 'related':
$results = $parts[0];
for($p = 1; $p < $lp; ++$p)
$results['Related'][] = $parts[$p];
break;
case 'mixed':
$results = $parts[0];
for($p = 1; $p < $lp; ++$p)
$results['Attachments'][] = $parts[$p];
break;
case 'report':
if(IsSet($parameters['report-type']))
{
$report_type = $parameters['report-type'];
}
else
{
$report_type = null;
for($p = 1; $p < $lp; ++$p)
{
switch($parts[$p]['Type'])
{
case 'delivery-status':
case 'feedback-report':
$report_type = $parts[$p]['Type'];
break 2;
}
}
if(!IsSet($report_type))
return($this->SetError('this '.$content_type.' message is not well formed because it does not define the report type of a multipart report section'));
}
switch($report_type)
{
case 'delivery-status':
for($p = 1; $p < $lp; ++$p)
{
if(!strcmp($parts[$p]['Type'], $report_type))
{
$results = $parts[$p];
break;
}
}
if(!$this->ReadMessageBody($parts[0], $body, 'Data'))
return(0);
if(strlen($body))
$results['Response'] = $body;
break;
case 'feedback-report':
$body_part = $results = null;
for($p = 1; $p < $lp; ++$p)
{
if(!strcmp($parts[$p]['Type'], $report_type))
{
$results = $parts[$p];
UnSet($results['Data']);
}
elseif(!strcmp($parts[$p]['Type'], 'message'))
$body_part = $p;
}
if(!IsSet($results))
{
$this->SetErrorWithContact('the report of type '.$report_type.' does not contain the report details part');
$results['Response'] = $this->error;
$this->error = '';
break;
}
if(IsSet($body_part))
{
if(!$this->ReadMessageBody($parts[$body_part], $body, 'Data'))
return false;
if(strlen($body))
$results['Response'] = $body;
}
break;
default:
$this->SetErrorWithContact('the report type is '.$report_type.' is not yet supported');
$results['Response'] = $this->error;
$this->error = '';
break;
}
$results['Type'] = $report_type;
break;
case 'signed':
if($lp != 2)
return($this->SetError('this '.$content_type.' message does not have just 2 parts'));
if(strcmp($parts[1]['Type'], 'signature'))
{
$this->SetErrorWithContact('this '.$content_type.' message does not contain a signature');
$this->error = '';
}
$results = $parts[0];
$results['Signature'] = $parts[1];
break;
case 'appledouble':
if($lp != 2)
return($this->SetError('this '.$content_type.' message does not have just 2 parts'));
if(strcmp($parts[0]['Type'], 'applefile'))
{
$this->SetErrorWithContact('this '.$content_type.' message does not contain an Apple file header');
$this->error = '';
}
$results = $parts[1];
$results['AppleFileHeader'] = $parts[0];
break;
case 'form-data':
$results['Type'] = 'form-data';
$results['FormData'] = array();
for($p = 0; $p < $lp; ++$p)
{
if(!IsSet($message['Parts'][$p]['Headers']['content-disposition:']))
return($this->SetError('the form data part '.$p.' is missing the content-disposition header'));
$disposition = $message['Parts'][$p]['Headers']['content-disposition:'];
$name = $this->ParseParameters($disposition, $disposition, $parameters, 'name');
if(strcmp($disposition, 'form-data'))
{
if(!$this->SetPositionedWarning('disposition of part '.$p.' is not form-data', $message['Parts'][$p]['Position']))
return(0);
continue;
}
$results['FormData'][$name] = $parts[$p];
}
break;
}
break;
case 'text':
switch($sub_type)
{
case 'plain':
$results['Type'] = 'text';
$results['Description'] = 'Text message';
break;
case 'html':
$results['Type'] = 'html';
$results['Description'] = 'HTML message';
break;
case 'rtf':
$results['Type'] = 'rtf';
$results['Description'] = 'Document in Rich Text Format';
break;
default:
$results['Type'] = $type;
$results['SubType'] = $sub_type;
$results['Description'] = 'Text file in the '.strtoupper($sub_type).' format';
break;
}
break;
case 'video':
$results['Type'] = $type;
$results['SubType'] = $sub_type;
$results['Description'] = 'Video file in the '.strtoupper($sub_type).' format';
break;
case 'image':
$results['Type'] = $type;
$results['SubType'] = $sub_type;
$results['Description'] = 'Image file in the '.strtoupper($sub_type).' format';
break;
case 'audio':
$results['Type'] = $type;
$results['SubType'] = $sub_type;
$results['Description'] = 'Audio file in the '.strtoupper($sub_type).' format';
break;
case 'application':
switch($sub_type)
{
case 'octet-stream':
case 'x-msdownload':
$results['Type'] = 'binary';
$results['Description'] = 'Binary file';
break;
case 'pdf':
$results['Type'] = $sub_type;
$results['Description'] = 'Document in PDF format';
break;
case 'postscript':
$results['Type'] = $sub_type;
$results['Description'] = 'Document in Postscript format';
break;
case 'msword':
$results['Type'] = 'ms-word';
$results['Description'] = 'Word processing document in Microsoft Word format';
break;
case 'vnd.ms-powerpoint':
$results['Type'] = 'ms-powerpoint';
$results['Description'] = 'Presentation in Microsoft PowerPoint format';
break;
case 'vnd.ms-excel':
$results['Type'] = 'ms-excel';
$results['Description'] = 'Spreadsheet in Microsoft Excel format';
break;
case 'x-compressed':
if(!IsSet($parameters['name'])
|| GetType($dot = strpos($parameters['name'], '.'))!='integer'
|| strcmp($extension = strtolower(substr($parameters['name'], $dot + 1)), 'zip'))
break;
case 'zip':
case 'x-zip':
case 'x-zip-compressed':
$results['Type'] = 'zip';
$results['Description'] = 'ZIP archive with compressed files';
break;
case 'ms-tnef':
$results['Type'] = $sub_type;
$results['Description'] = 'Microsoft Exchange data usually sent by Microsoft Outlook';
break;
case 'pgp-signature':
$results['Type'] = 'signature';
$results['SubType'] = $sub_type;
$results['Description'] = 'Message signature for PGP';
break;
case 'x-pkcs7-signature':
case 'pkcs7-signature':
$results['Type'] = 'signature';
$results['SubType'] = $sub_type;
$results['Description'] = 'PKCS message signature';
break;
case 'vnd.oasis.opendocument.text':
$results['Type'] = 'odf-writer';
$results['Description'] = 'Word processing document in ODF text format used by OpenOffice Writer';
break;
case 'applefile':
$results['Type'] = 'applefile';
$results['Description'] = 'Apple file resource header';
break;
case 'rtf':
$results['Type'] = $sub_type;
$results['Description'] = 'Document in Rich Text Format';
break;
case 'x-httpd-php':
$results['Type'] = 'php';
$results['Description'] = 'PHP script';
break;
}
break;
case 'message':
$tolerate_unrecognized = 0;
switch($sub_type)
{
case 'delivery-status':
$results['Type'] = $sub_type;
$results['Description'] = 'Notification of the status of delivery of a message';
if(!$this->ReadMessageBody($message, $body, 'Body'))
return(0);
if(($l = strlen($body)))
{
$position = 0;
$this->ParseHeaderString($body, $position, $headers);
$recipients = array();
for(;$position<$l;)
{
$this->ParseHeaderString($body, $position, $headers);
if(count($headers))
{
$r = count($recipients);
if(IsSet($headers['action']))
$recipients[$r]['Action'] = $headers['action'];
if(IsSet($headers['status']))
$recipients[$r]['Status'] = $headers['status'];
if(IsSet($headers['original-recipient']))
{
strtok($headers['original-recipient'], ';');
$recipients[$r]['Address'] = trim(strtok(''));
}
elseif(IsSet($headers['final-recipient']))
{
strtok($headers['final-recipient'], ';');
$recipients[$r]['Address'] = trim(strtok(''));
}
}
}
$results['Recipients'] = $recipients;
}
$copy_body = 0;
break;
case 'feedback-report':
$results['Type'] = $sub_type;
$results['Description'] = 'Notification of the processing of a message sent';
if(!$this->ReadMessageBody($message, $body, 'Body'))
return(0);
if(($l = strlen($body)))
{
$position = 0;
$this->ParseHeaderString($body, $position, $headers);
$report = array();
for(;$position<$l;)
{
$this->ParseHeaderString($body, $position, $headers);
foreach($headers as $name => $value)
$report[$name] = $value;
}
$results['Report'] = $report;
}
break;
case 'rfc822':
$results['Type'] = 'message';
$results['Description'] = 'E-mail message';
break;
}
break;
default:
$tolerate_unrecognized = 0;
break;
}
if(!IsSet($results['Type']))
{
if(IsSet($this->custom_mime_types[$content_type]))
{
$results['Type'] = $this->custom_mime_types[$content_type]['Type'];
$results['Description'] = $this->custom_mime_types[$content_type]['Description'];
}
else
{
$this->SetErrorWithContact($content_type.' message parts are not yet recognized. You can define these part type names and descriptions setting the custom_mime_types class variable');
$results['Type'] = $this->error;
$this->error = '';
}
}
if(IsSet($parameters['charset']))
$results['Encoding'] = strtolower($parameters['charset']);
if(IsSet($message['Headers']['subject:']))
{
if(IsSet($message['DecodedHeaders']['subject:'])
&& count($message['DecodedHeaders']['subject:']) == 1
&& count($message['DecodedHeaders']['subject:'][0]) == 1)
{
$results['Subject'] = $message['DecodedHeaders']['subject:'][0][0]['Value'];
$results['SubjectEncoding'] = strtolower($message['DecodedHeaders']['subject:'][0][0]['Encoding']);
}
else
$results['Subject'] = $message['Headers']['subject:'];
}
if(IsSet($message['Headers']['x-hesk-tracking_id:']))
{
$results['X-Hesk-Tracking_ID'] = $message['Headers']['x-hesk-tracking_id:'];
}
if(
(IsSet($message['Headers']['x-priority:']) &&
(
$message['Headers']['x-priority:'] == 1 ||
stripos($message['Headers']['x-priority:'], 'high') !== false
)
) ||
(IsSet($message['Headers']['importance:']) && strtolower($message['Headers']['importance:']) == "high") ||
(IsSet($message['Headers']['x-msmail-priority:']) && strtolower($message['Headers']['x-msmail-priority:']) == "high")
)
{
$results['X-Priority'] = "high";
}
if(IsSet($message['Headers']['message-id:']))
{
$results['Message-ID'] = $message['Headers']['message-id:'];
}
if(IsSet($message['Headers']['in-reply-to:']))
{
$results['In-Reply-To'] = $message['Headers']['in-reply-to:'];
}
if(IsSet($message['Headers']['references:']))
{
$results['References'] = $message['Headers']['references:'];
}
if(IsSet($message['Headers']['date:']))
{
if(IsSet($message['DecodedHeaders']['date:'])
&& count($message['DecodedHeaders']['date:']) == 1
&& count($message['DecodedHeaders']['date:'][0]) == 1)
$results['Date'] = $message['DecodedHeaders']['date:'][0][0]['Value'];
else
$results['Date'] = $message['Headers']['date:'];
}
$l = count($this->address_headers);
for(Reset($this->address_headers), $h = 0; $h<$l; Next($this->address_headers), ++$h)
$this->CopyAddresses($message, $results, Key($this->address_headers));
if($copy_body)
{
if(IsSet($message['Body']))
$results['Data'] = $message['Body'];
elseif(IsSet($message['BodyFile']))
$results['DataFile'] = $message['BodyFile'];
elseif(IsSet($message['BodyLength']))
$results['DataLength'] = $message['BodyLength'];
if(IsSet($message['FileName']))
$results['FileName'] = $message['FileName'];
if(IsSet($message['FileDisposition']))
$results['FileDisposition'] = $message['FileDisposition'];
if(IsSet($message['Headers']['content-id:']))
{
$content_id = trim($message['Headers']['content-id:']);
$l = strlen($content_id);
if(!strcmp($content_id[0], '<')
&& !strcmp($content_id[$l - 1], '>'))
$results['ContentID'] = substr($content_id, 1, $l - 2);
}
}
return(1);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
/*
{metadocument}
<function>
<name>GetPositionLine</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Get the line number of the document that corresponds to a
given position.</purpose>
<usage>Pass the document offset number as the position to be
located. Make sure the <variablelink>track_lines</variablelink>
variable is set to <booleanvalue>1</booleanvalue> before parsing
the document.</usage>
<returnvalue>This function returns <booleanvalue>1</booleanvalue> if
the <variablelink>track_lines</variablelink> variable is set to
<booleanvalue>1</booleanvalue> and it was given a valid positive
position number that does not exceed the position of the last
parsed document line.</returnvalue>
</documentation>
<argument>
<name>position</name>
<type>INTEGER</type>
<documentation>
<purpose>Position of the line to be located.</purpose>
</documentation>
</argument>
<argument>
<name>line</name>
<type>INTEGER</type>
<out />
<documentation>
<purpose>Returns the number of the line that corresponds to the
given document position.</purpose>
</documentation>
</argument>
<argument>
<name>column</name>
<type>INTEGER</type>
<out />
<documentation>
<purpose>Returns the number of the column of the line that
corresponds to the given document position.</purpose>
</documentation>
</argument>
<do>
{/metadocument}
*/
Function GetPositionLine($position, &$line, &$column)
{
if(!$this->track_lines)
return($this->SetPositionedError('line positions are not being tracked', $position));
$bottom = 0;
$top = count($this->lines) - 1;
if($position < 0)
return($this->SetPositionedError('it was not specified a valid position', $position));
for(;;)
{
$line = intval(($bottom + $top) / 2);
$current = $this->lines[$line];
if($current < $position)
$bottom = $line + 1;
elseif($current > $position)
$top = $line - 1;
else
break;
if($top < $bottom)
{
$line = $top;
break;
}
}
$column = $position - $this->lines[$line] + 1;
++$line;
return(1);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
};
/*
{metadocument}
</class>
{/metadocument}
*/
wget 'https://lists2.roe3.org/hesk/inc/mail/pop3.php'
<?php
/*
* pop3.php
*
* @(#) $Header: /opt2/ena/metal/pop3/pop3.php,v 1.24 2014/01/27 10:46:48 mlemos Exp $
*
* 27th Sep 2022
* Modified for Hesk by Klemen Stirn: added XOAUTH2 support
* https://www.hesk.com
*
*/
#[AllowDynamicProperties]
class pop3_class
{
var $hostname="";
var $port=110;
var $tls=0;
var $quit_handshake=1;
var $error="";
var $authentication_mechanism="USER";
var $realm="";
var $workstation="";
var $join_continuation_header_lines=1;
/* Private variables - DO NOT ACCESS */
var $connection=0;
var $state="DISCONNECTED";
var $greeting="";
var $must_update=0;
var $debug=0;
var $html_debug=0;
var $next_token="";
var $message_buffer="";
var $connection_name = '';
/* Private methods - DO NOT CALL */
Function Tokenize($string,$separator="")
{
if(!strcmp($separator,""))
{
$separator=$string;
$string=$this->next_token;
}
for($character=0;$character<strlen($separator);$character++)
{
if(GetType($position=strpos($string,$separator[$character]))=="integer")
$found=(IsSet($found) ? min($found,$position) : $position);
}
if(IsSet($found))
{
$this->next_token=substr($string,$found+1);
return(substr($string,0,$found));
}
else
{
$this->next_token="";
return($string);
}
}
Function SetError($error)
{
return($this->error=$error);
}
Function OutputDebug($message)
{
$message.="\n";
if($this->html_debug)
$message=str_replace("\n","<br />\n",HtmlSpecialChars($message));
echo $message;
#flush();
}
Function GetLine()
{
for($line="";;)
{
if(feof($this->connection))
return(0);
$line.=@fgets($this->connection,100);
$length=strlen($line);
if($length>=2
&& substr($line,$length-2,2)=="\r\n")
{
$line=substr($line,0,$length-2);
if($this->debug)
$this->OutputDebug("S $line");
return($line);
}
}
}
Function PutLine($line)
{
if($this->debug)
$this->OutputDebug("C $line");
return(fputs($this->connection,"$line\r\n"));
}
Function OpenConnection()
{
if($this->tls)
{
$version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
$php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]);
if($php_version<4003000)
return("establishing TLS connections requires at least PHP version 4.3.0");
if(!function_exists("extension_loaded")
|| !extension_loaded("openssl"))
return("establishing TLS connections requires the OpenSSL extension enabled");
}
if($this->hostname=="")
return($this->SetError("2 it was not specified a valid hostname"));
if($this->debug)
$this->OutputDebug("Connecting to ".$this->hostname." ...");
if(($this->connection=@fsockopen(($this->tls ? "tls://" : "").$this->hostname, $this->port, $error, $error_message))==0)
{
switch($error)
{
case -3:
return($this->SetError("-3 socket could not be created"));
case -4:
return($this->SetError("-4 dns lookup on hostname \"$hostname\" failed"));
case -5:
return($this->SetError("-5 connection refused or timed out"));
case -6:
return($this->SetError("-6 fdopen() call failed"));
case -7:
return($this->SetError("-7 setvbuf() call failed"));
default:
return($this->SetError($error." could not connect to the host \"".$this->hostname."\": ".$error_message));
}
}
return("");
}
Function CloseConnection()
{
if($this->debug)
$this->OutputDebug("Closing connection.");
if($this->connection!=0)
{
fclose($this->connection);
$this->connection=0;
}
}
/* Public methods */
/* Open method - set the object variable $hostname to the POP3 server address. */
Function Open()
{
if($this->state!="DISCONNECTED")
return($this->SetError("1 a connection is already opened"));
if(($error=$this->OpenConnection())!="")
return($error);
$greeting=$this->GetLine();
if(GetType($greeting)!="string"
|| $this->Tokenize($greeting," ")!="+OK")
{
$this->CloseConnection();
return($this->SetError("3 POP3 server greeting was not found"));
}
$this->Tokenize("<");
$this->greeting = $this->Tokenize(">");
$this->must_update=0;
$this->state="AUTHORIZATION";
return("");
}
/* Close method - this method must be called at least if there are any
messages to be deleted */
Function Close()
{
if($this->state=="DISCONNECTED")
return($this->SetError("no connection was opened"));
while($this->state=='GETMESSAGE')
{
if(strlen($error=$this->GetMessage(8000, $message, $end_of_message)))
return($error);
}
if($this->must_update
|| $this->quit_handshake)
{
if($this->PutLine("QUIT")==0)
return($this->SetError("Could not send the QUIT command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get quit command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not quit the connection: ".$this->Tokenize("\r\n")));
}
$this->CloseConnection();
$this->state="DISCONNECTED";
$pop3_class = new pop3_class();
$pop3_class->SetConnection(-1, $this->connection_name, $this);
return("");
}
/* Login method - pass the user name and password of POP account. Set
$apop to 1 or 0 wether you want to login using APOP method or not. */
Function Login($user,$password,$apop=0)
{
if($this->state!="AUTHORIZATION")
return($this->SetError("connection is not in AUTHORIZATION state"));
if($apop)
{
if(!strcmp($this->greeting,""))
return($this->SetError("Server does not seem to support APOP authentication"));
if($this->PutLine("APOP $user ".md5("<".$this->greeting.">".$password))==0)
return($this->SetError("Could not send the APOP command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get APOP login command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("APOP login failed: ".$this->Tokenize("\r\n")));
}
else
{
$authenticated=0;
if(strcmp($this->authentication_mechanism,"USER")
&& function_exists("class_exists")
&& class_exists("sasl_client_class"))
{
if(strlen($this->authentication_mechanism))
$mechanisms=array($this->authentication_mechanism);
else
{
$mechanisms=array();
if($this->PutLine("CAPA")==0)
return($this->SetError("Could not send the CAPA command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get CAPA command response"));
if(!strcmp($this->Tokenize($response," "),"+OK"))
{
for(;;)
{
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not retrieve the supported authentication methods"));
switch($this->Tokenize($response," "))
{
case ".":
break 2;
case "SASL":
for($method=1;strlen($mechanism=$this->Tokenize(" "));$method++)
$mechanisms[]=$mechanism;
break;
}
}
}
}
$sasl=new sasl_client_class;
$sasl->SetCredential("user",$user);
$sasl->SetCredential("password",$password);
if(strlen($this->realm))
$sasl->SetCredential("realm",$this->realm);
if(strlen($this->workstation))
$sasl->SetCredential("workstation",$this->workstation);
do
{
$status=$sasl->Start($mechanisms,$message,$interactions);
}
while($status==SASL_INTERACT);
switch($status)
{
case SASL_CONTINUE:
break;
case SASL_NOMECH:
if(strlen($this->authentication_mechanism))
return($this->SetError("authenticated mechanism ".$this->authentication_mechanism." may not be used: ".$sasl->error));
break;
default:
return($this->SetError("Could not start the SASL authentication client: ".$sasl->error));
}
if(strlen($sasl->mechanism))
{
if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0)
return("Could not send the AUTH command");
$response=$this->GetLine();
if(GetType($response)!="string")
return("Could not get AUTH command response");
switch($this->Tokenize($response," "))
{
case "+OK":
$response="";
break;
case "+":
$response=base64_decode($this->Tokenize("\r\n"));
break;
default:
return($this->SetError("Authentication error: ".$this->Tokenize("\r\n")));
}
for(;!$authenticated;)
{
do
{
$status=$sasl->Step($response,$message,$interactions);
}
while($status==SASL_INTERACT);
switch($status)
{
case SASL_CONTINUE:
if($this->PutLine(base64_encode($message))==0)
return("Could not send message authentication step message");
$response=$this->GetLine();
if(GetType($response)!="string")
return("Could not get authentication step message response");
switch($this->Tokenize($response," "))
{
case "+OK":
$authenticated=1;
break;
case "+":
$response=base64_decode($this->Tokenize("\r\n"));
break;
default:
return($this->SetError("Authentication error: ".$this->Tokenize("\r\n")));
}
break;
default:
return($this->SetError("Could not process the SASL authentication step: ".$sasl->error));
}
}
}
}
if(!$authenticated)
{
// Perform XOAUTH2 authentication for Hesk
if($this->authentication_mechanism == 'XOAUTH2') {
if($this->PutLine("AUTH XOAUTH2")==0)
return($this->SetError("Could not send the AUTH XOAUTH2 command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return("Could not get AUTH command response");
if ($this->Tokenize($response," ") != "+")
return("Unexpected AUTH command response");
if($this->PutLine(base64_encode("user=" . $user . "\1auth=Bearer " . $password . "\1\1"))==0)
return($this->SetError("Could not send the AUTH XOAUTH2 command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return("Could not get AUTH command response");
switch($this->Tokenize($response," "))
{
case "+OK":
$response="";
break;
case "+":
$response=base64_decode($this->Tokenize("\r\n"));
break;
default:
return($this->SetError("Authentication error: ".$this->Tokenize("\r\n")));
}
$this->state="TRANSACTION";
return("");
}
if($this->PutLine("USER $user")==0)
return($this->SetError("Could not send the USER command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get user login entry response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("User error: ".$this->Tokenize("\r\n")));
if($this->PutLine("PASS $password")==0)
return($this->SetError("Could not send the PASS command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get login password entry response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Password error: ".$this->Tokenize("\r\n")));
}
}
$this->state="TRANSACTION";
return("");
}
/* Statistics method - pass references to variables to hold the number of
messages in the mail box and the size that they take in bytes. */
Function Statistics(&$messages,&$size)
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($this->PutLine("STAT")==0)
return($this->SetError("Could not send the STAT command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get the statistics command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not get the statistics: ".$this->Tokenize("\r\n")));
$messages=$this->Tokenize(" ");
$size=$this->Tokenize(" ");
return("");
}
/* ListMessages method - the $message argument indicates the number of a
message to be listed. If you specify an empty string it will list all
messages in the mail box. The $unique_id flag indicates if you want
to list the each message unique identifier, otherwise it will
return the size of each message listed. If you list all messages the
result will be returned in an array. */
Function ListMessages($message,$unique_id)
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($unique_id)
$list_command="UIDL";
else
$list_command="LIST";
if($this->PutLine("$list_command".($message ? " ".$message : ""))==0)
return($this->SetError("Could not send the $list_command command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get message list command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not get the message listing: ".$this->Tokenize("\r\n")));
if($message=="")
{
for($messages=array();;)
{
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get message list response"));
if($response==".")
break;
$message=intval($this->Tokenize($response," "));
if($unique_id)
$messages[$message]=$this->Tokenize(" ");
else
$messages[$message]=intval($this->Tokenize(" "));
}
return($messages);
}
else
{
$message=intval($this->Tokenize(" "));
$value=$this->Tokenize(" ");
return($unique_id ? $value : intval($value));
}
}
/* RetrieveMessage method - the $message argument indicates the number of
a message to be listed. Pass a reference variables that will hold the
arrays of the $header and $body lines. The $lines argument tells how
many lines of the message are to be retrieved. Pass a negative number
if you want to retrieve the whole message. */
Function RetrieveMessage($message,&$headers,&$body,$lines)
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($lines<0)
{
$command="RETR";
$arguments="$message";
}
else
{
$command="TOP";
$arguments="$message $lines";
}
if($this->PutLine("$command $arguments")==0)
return($this->SetError("Could not send the $command command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get message retrieval command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not retrieve the message: ".$this->Tokenize("\r\n")));
for($headers=$body=array(),$line=0;;)
{
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not retrieve the message"));
switch($response)
{
case ".":
return("");
case "":
break 2;
default:
if(substr($response,0,1)==".")
$response=substr($response,1,strlen($response)-1);
break;
}
if($this->join_continuation_header_lines
&& $line>0
&& ($response[0]=="\t"
|| $response[0]==" "))
$headers[$line-1].=$response;
else
{
$headers[$line]=$response;
$line++;
}
}
for($line=0;;$line++)
{
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not retrieve the message"));
switch($response)
{
case ".":
return("");
default:
if(substr($response,0,1)==".")
$response=substr($response,1,strlen($response)-1);
break;
}
$body[$line]=$response;
}
return("");
}
/* OpenMessage method - the $message argument indicates the number of
a message to be opened. The $lines argument tells how many lines of
the message are to be retrieved. Pass a negative number if you want
to retrieve the whole message. */
Function OpenMessage($message, $lines=-1)
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($lines<0)
{
$command="RETR";
$arguments="$message";
}
else
{
$command="TOP";
$arguments="$message $lines";
}
if($this->PutLine("$command $arguments")==0)
return($this->SetError("Could not send the $command command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get message retrieval command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not retrieve the message: ".$this->Tokenize("\r\n")));
$this->state="GETMESSAGE";
$this->message_buffer="";
return("");
}
/* GetMessage method - the $count argument indicates the number of bytes
to be read from an opened message. The $message returns by reference
the data read from the message. The $end_of_message argument returns
by reference a boolean value indicated whether it was reached the end
of the message. */
Function GetMessage($count, &$message, &$end_of_message)
{
if($this->state!="GETMESSAGE")
return($this->SetError("connection is not in GETMESSAGE state"));
$message="";
$end_of_message=0;
while($count>strlen($this->message_buffer)
&& !$end_of_message)
{
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not retrieve the message headers"));
if(!strcmp($response,"."))
{
$end_of_message=1;
$this->state="TRANSACTION";
break;
}
else
{
if(substr($response,0,1)==".")
$response=substr($response,1,strlen($response)-1);
$this->message_buffer.=$response."\r\n";
}
}
if($end_of_message
|| $count>=strlen($this->message_buffer))
{
$message=$this->message_buffer;
$this->message_buffer="";
}
else
{
$message=substr($this->message_buffer, 0, $count);
$this->message_buffer=substr($this->message_buffer, $count);
}
return("");
}
/* DeleteMessage method - the $message argument indicates the number of
a message to be marked as deleted. Messages will only be effectively
deleted upon a successful call to the Close method. */
Function DeleteMessage($message)
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($this->PutLine("DELE $message")==0)
return($this->SetError("Could not send the DELE command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get message delete command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not delete the message: ".$this->Tokenize("\r\n")));
$this->must_update=1;
return("");
}
/* ResetDeletedMessages method - Reset the list of marked to be deleted
messages. No messages will be marked to be deleted upon a successful
call to this method. */
Function ResetDeletedMessages()
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($this->PutLine("RSET")==0)
return($this->SetError("Could not send the RSET command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not get reset deleted messages command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not reset deleted messages: ".$this->Tokenize("\r\n")));
$this->must_update=0;
return("");
}
/* IssueNOOP method - Just pings the server to prevent it auto-close the
connection after an idle timeout (tipically 10 minutes). Not very
useful for most likely uses of this class. It's just here for
protocol support completeness. */
Function IssueNOOP()
{
if($this->state!="TRANSACTION")
return($this->SetError("connection is not in TRANSACTION state"));
if($this->PutLine("NOOP")==0)
return($this->SetError("Could not send the NOOP command"));
$response=$this->GetLine();
if(GetType($response)!="string")
return($this->SetError("Could not NOOP command response"));
if($this->Tokenize($response," ")!="+OK")
return($this->SetError("Could not issue the NOOP command: ".$this->Tokenize("\r\n")));
return("");
}
Function &SetConnection($set, &$current_name, &$pop3)
{
static $connections = array();
if($set>0)
{
$current_name = strval(count($connections));
$connections[$current_name] = &$pop3;
}
elseif($set<0)
{
$connections[$current_name] = '';
$current_name = '';
}
elseif(IsSet($connections[$current_name])
&& GetType($connections[$current_name])!='string')
{
$connection = &$connections[$current_name];
return($connection);
}
return($pop3);
}
/* GetConnectionName method - Retrieve the name associated to an
established POP3 server connection to use as virtual host name for
use in POP3 stream wrapper URLs. */
Function GetConnectionName(&$connection_name)
{
if($this->state!="TRANSACTION")
return($this->SetError("cannot get the name of a POP3 connection that was not established and the user has logged in"));
if(strlen($this->connection_name) == 0)
{
$pop3_class = new pop3_class();
$pop3_class->SetConnection(1, $this->connection_name, $this);
}
$connection_name = $this->connection_name;
return('');
}
};
#[AllowDynamicProperties]
class pop3_stream
{
var $opened = 0;
var $report_errors = 1;
var $read = 0;
var $buffer = "";
var $end_of_message=1;
var $previous_connection = 0;
var $pop3;
Function SetError($error)
{
if($this->report_errors)
trigger_error($error);
return(FALSE);
}
Function ParsePath($path, &$url)
{
if(!$this->previous_connection)
{
if(IsSet($url["host"]))
$this->pop3->hostname=$url["host"];
if(IsSet($url["port"]))
$this->pop3->port=intval($url["port"]);
if(IsSet($url["scheme"])
&& !strcmp($url["scheme"],"pop3s"))
$this->pop3->tls=1;
if(!IsSet($url["user"]))
return($this->SetError("it was not specified a valid POP3 user"));
if(!IsSet($url["pass"]))
return($this->SetError("it was not specified a valid POP3 password"));
if(!IsSet($url["path"]))
return($this->SetError("it was not specified a valid mailbox path"));
}
if(IsSet($url["query"]))
{
parse_str($url["query"],$query);
if(IsSet($query["debug"]))
$this->pop3->debug = intval($query["debug"]);
if(IsSet($query["html_debug"]))
$this->pop3->html_debug = intval($query["html_debug"]);
if(!$this->previous_connection)
{
if(IsSet($query["tls"]))
$this->pop3->tls = intval($query["tls"]);
if(IsSet($query["realm"]))
$this->pop3->realm = UrlDecode($query["realm"]);
if(IsSet($query["workstation"]))
$this->pop3->workstation = UrlDecode($query["workstation"]);
if(IsSet($query["authentication_mechanism"]))
$this->pop3->realm = UrlDecode($query["authentication_mechanism"]);
}
if(IsSet($query["quit_handshake"]))
$this->pop3->quit_handshake = intval($query["quit_handshake"]);
}
return(TRUE);
}
Function stream_open($path, $mode, $options, &$opened_path)
{
$this->report_errors = (($options & STREAM_REPORT_ERRORS) !=0);
if(strcmp($mode, "r") && strcmp($mode, "rb"))
return($this->SetError("the message can only be opened for reading"));
$url=parse_url($path);
$host = $url['host'];
$pop3_class = new pop3_class();
$pop3 = &$pop3_class->SetConnection(0, $host, $this->pop3);
if(IsSet($pop3))
{
$this->pop3 = &$pop3;
$this->previous_connection = 1;
}
else
$this->pop3=new pop3_class;
if(!$this->ParsePath($path, $url))
return(FALSE);
$message=substr($url["path"],1);
if(strcmp(intval($message), $message)
|| $message<=0)
return($this->SetError("it was not specified a valid message to retrieve"));
if(!$this->previous_connection)
{
if(strlen($error=$this->pop3->Open()))
return($this->SetError($error));
$this->opened = 1;
$apop = (IsSet($url["query"]["apop"]) ? intval($url["query"]["apop"]) : 0);
if(strlen($error=$this->pop3->Login(UrlDecode($url["user"]), UrlDecode($url["pass"]),$apop)))
{
$this->stream_close();
return($this->SetError($error));
}
}
if(strlen($error=$this->pop3->OpenMessage($message,-1)))
{
$this->stream_close();
return($this->SetError($error));
}
$this->end_of_message=FALSE;
if($options & STREAM_USE_PATH)
$opened_path=$path;
$this->read = 0;
$this->buffer = "";
return(TRUE);
}
Function stream_eof()
{
if($this->read==0)
return(FALSE);
return($this->end_of_message);
}
Function stream_read($count)
{
if($count<=0)
return($this->SetError("it was not specified a valid length of the message to read"));
if($this->end_of_message)
return("");
if(strlen($error=$this->pop3->GetMessage($count, $read, $this->end_of_message)))
return($this->SetError($error));
$this->read += strlen($read);
return($read);
}
Function stream_close()
{
while(!$this->end_of_message)
$this->stream_read(8000);
if($this->opened)
{
$this->pop3->Close();
$this->opened = 0;
}
}
};
?>
wget 'https://lists2.roe3.org/hesk/inc/mail/rfc822_addresses.php'