Reading email files

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
Pavilion
Forum Contributor
Posts: 301
Joined: Thu Feb 23, 2012 6:51 am

Reading email files

Post by Pavilion »

Hello:

I've figured out how to read my email directory for bounced emails. The end goal here is to cull the email addresses from bounced emails, insert them into a mySQL table so I can query against the userTbl and delete bounced emails. Following is what I've come up with so far:

Code: Select all

$directory = '/home/myaccount/mail/mysite.com/admin/new';

    // create an array to hold directory list
    $results = array();

    // create a handler for the directory
    $handler = opendir($directory);

    // open directory and walk through the filenames
    while ($file = readdir($handler)) {
      // if file isn't this directory or its parent, add it to the results
      if ($file != "." && $file != "..") {
        $results[] = $file;
		var_dump($results);
      }	  
    }

    // tidy up: close the handler
    closedir($handler);
var_dump is showing accurate file names. But, I can not figure out how to read those files and cull the email address.

Any suggestions are welcome.

Thanks in advance - Pavilion
User avatar
tr0gd0rr
Forum Contributor
Posts: 305
Joined: Thu May 11, 2006 8:58 pm
Location: Utah, USA

Re: Reading email files

Post by tr0gd0rr »

Reading emails manually is very complicated because of all the MIME rules etc. I'm using Zend_Mail right now and it works great. You can use it to connect via pop/imap and read emails or you can read from a file like you are wanting:

Code: Select all

$message = new Zend_Mail_Message(array('file'=>'/home/myaccount/mail/mysite.com/admin/new/message1.whatever'));
$from = $message->getHeader('from');
if (preg_match('/<([^>]+)>/', $from, $match)) {
	$fromEmail = $match[1];
}
else {
	$fromEmail = $from;
}
$fromEmail = strtolower(trim($fromEmail));
Pavilion
Forum Contributor
Posts: 301
Joined: Thu Feb 23, 2012 6:51 am

Re: Reading email files

Post by Pavilion »

tr0gd0rr wrote:Reading emails manually is very complicated because of all the MIME rules etc. I'm using Zend_Mail right now and it works great. You can use it to connect via pop/imap and read emails or you can read from a file like you are wanting:

Code: Select all

$message = new Zend_Mail_Message(array('file'=>'/home/myaccount/mail/mysite.com/admin/new/message1.whatever'));
$from = $message->getHeader('from');
if (preg_match('/<([^>]+)>/', $from, $match)) {
	$fromEmail = $match[1];
}
else {
	$fromEmail = $from;
}
$fromEmail = strtolower(trim($fromEmail));
tr0gd0rr -

Thank you so much. I will definitely look into this, and may be back with questions.

Thanks again - Pavilion
Pavilion
Forum Contributor
Posts: 301
Joined: Thu Feb 23, 2012 6:51 am

Re: Reading email files

Post by Pavilion »

Reading emails manually is very complicated because of all the MIME rules etc. I'm using Zend_Mail right now and it works great. You can use it to connect via pop/imap and read emails or you can read from a file like you are wanting:
Hello tr0gd0rr:

Well I'm successfully sending regular emails from Zend now. However I'm having problems getting mail to go out through an SMTP protocol, in addition the script you posted isn't working either.

Specifically with the script you suggested:

Code: Select all

$message = new Zend_Mail_Message(array('file'=>'/home/myaccount/mail/mysite.com/admin/new/message1.whatever'));
$from = $message->getHeader('from');
if (preg_match('/<([^>]+)>/', $from, $match)) {
        $fromEmail = $match[1];
}
else {
        $fromEmail = $from;
}
$fromEmail = strtolower(trim($fromEmail));
I am getting the following error in my error log:
[26-Aug-2012 20:21:29] PHP Fatal error: Class 'Zend_Mail_Message' not found in /home/user/public_html/process_mail_stats.php on line 27
This is a similar error message to the one given when I try to set up SMTP protocol, it can't find what it's suppose to.

I'm not sure what I'm doing wrong, for your script I'm requiring the following:

Code: Select all

require_once '../Zend/Mail.php';
And my script follows:

Code: Select all

$message = new Zend_Mail_Message(array('file'=>'/home/user/mail/new/1346028410.H867539P3046.box385.host.com,S=2514'));
$from = $message->getHeader('from');
if (preg_match('/<([^>]+)>/', $from, $match)) {
        $fromEmail = $match[1];
}
else {
        $fromEmail = $from;
}
$fromEmail = strtolower(trim($fromEmail));
Any thoughts on where I'm tripping up would certainly be appreciated.


Thanks in advance, Pavilion
Pavilion
Forum Contributor
Posts: 301
Joined: Thu Feb 23, 2012 6:51 am

Re: Reading email files

Post by Pavilion »

OK - I've a problem with installing the Zend plugin and I'm really hoping someone can help me. Details follow:
  1. For whatever reason file references in Zend plugin as follows: require_once 'Zend/Mail.php';
  2. To get them to work on my server I have to have them as follows: require_once '../Zend/Mail.php'; (notice the ../).. OK fine - it's a pain to go determine all the file dependencies and add the ../ - but doable. But... it get's more complicated.
    In the ../Zend/Loader.php file there is a loadClass() which constructs a $file variable. OK - logic dictates that the preceding ../ be added to the $file= snippet. Did that - the file name echos out just fine and code continues to run until it hits the next error. The next error is:
  3. 2 17:22:51] PHP Warning: include() [function.include]: Failed opening 'Zend/Validate/Hostname/Com.php' for inclusion (include_path='.:/usr/lib64/php:/usr/lib/php:/usr/share/pear') in /home/user/public_html/Zend/Validate/Hostname.php on line 586 [27-Aug-2012 17:22:51] PHP Fatal error: Unsupported operand types in /home3/onestco1/public_html/Zend/Validate/Hostname.php on line 586
  4. line 586 is regex (what that has to do with file names I've no idea). The applicable code snippet follows:

Code: Select all

                    $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
                    if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
                        if (is_string($this->_validIdns[strtoupper($this->_tld)])) { // LINE 585
                            $regexChars += include($this->_validIdns[strtoupper($this->_tld)]); // LINE 586
                        } else {
                            $regexChars += $this->_validIdns[strtoupper($this->_tld)];
                        }
                    }
Larger context follows:

Code: Select all

    public function isValid($value)
    {
        if (!is_string($value)) {
            $this->_error(self::INVALID);
            return false;
        }

        $this->_setValue($value);
        // Check input against IP address schema
        if (preg_match('/^[0-9a-f:.]*$/i', $value) &&
            $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
            if (!($this->_options['allow'] & self::ALLOW_IP)) {
                $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
                return false;
            } else {
                return true;
            }
        }

        // RFC3986 3.2.2 states:
        // 
        //     The rightmost domain label of a fully qualified domain name
        //     in DNS may be followed by a single "." and should be if it is 
        //     necessary to distinguish between the complete domain name and
        //     some local domain.
        //     
        // (see ZF-6363)
        
        // Local hostnames are allowed to be partitial (ending '.')
        if ($this->_options['allow'] & self::ALLOW_LOCAL) {
            if (substr($value, -1) === '.') {
                $value = substr($value, 0, -1);
                if (substr($value, -1) === '.') {
                    // Empty hostnames (ending '..') are not allowed
                    $this->_error(self::INVALID_LOCAL_NAME);
                    return false;
                }
            }
        }

        $domainParts = explode('.', $value);

        // Prevent partitial IP V4 adresses (ending '.')
        if ((count($domainParts) == 4) && preg_match('/^[0-9.a-e:.]*$/i', $value) &&
            $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
            $this->_error(self::INVALID_LOCAL_NAME);
        }

        // Check input against DNS hostname schema
        if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
            $status = false;

            $origenc = iconv_get_encoding('internal_encoding');
            iconv_set_encoding('internal_encoding', 'UTF-8');
            do {
                // First check TLD
                $matches = array();
                if (preg_match('/([^.]{2,10})$/i', end($domainParts), $matches) ||
                    (end($domainParts) == 'ایران') || (end($domainParts) == '中国') ||
                    (end($domainParts) == '公司') || (end($domainParts) == '网络')) {

                    reset($domainParts);

                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
                    // id-prefix: alpha / digit
                    // ldh: alpha / digit / dash

                    // Match TLD against known list
                    $this->_tld = strtolower($matches[1]);
                    if ($this->_options['tld']) {
                        if (!in_array($this->_tld, $this->_validTlds)) {
                            $this->_error(self::UNKNOWN_TLD);
                            $status = false;
                            break;
                        }
                    }

                    /**
                     * Match against IDN hostnames
                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
                     * @see Zend_Validate_Hostname_Interface
                     */
                    $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
                    if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
                        if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
                            $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
                        } else {
                            $regexChars += $this->_validIdns[strtoupper($this->_tld)];
                        }
                    }

                    // Check each hostname part
                    $check = 0;
                    foreach ($domainParts as $domainPart) {
                        // Decode Punycode domainnames to IDN
                        if (strpos($domainPart, 'xn--') === 0) {
                            $domainPart = $this->decodePunycode(substr($domainPart, 4));
                            if ($domainPart === false) {
                                return false;
                            }
                        }

                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
                        if ((strpos($domainPart, '-') === 0)
                            || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
                            || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
                                $this->_error(self::INVALID_DASH);
                            $status = false;
                            break 2;
                        }

                        // Check each domain part
                        $checked = false;
                        foreach($regexChars as $regexKey => $regexChar) {
                            $status = @preg_match($regexChar, $domainPart);
                            if ($status > 0) {
                                $length = 63;
                                if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
                                    && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
                                    $length = $this->_idnLength[strtoupper($this->_tld)];
                                }

                                if (iconv_strlen($domainPart, 'UTF-8') > $length) {
                                    $this->_error(self::INVALID_HOSTNAME);
                                } else {
                                    $checked = true;
                                    break;
                                }
                            }
                        }

                        if ($checked) {
                            ++$check;
                        }
                    }

                    // If one of the labels doesn't match, the hostname is invalid
                    if ($check !== count($domainParts)) {
                        $this->_error(self::INVALID_HOSTNAME_SCHEMA);
                        $status = false;
                    }
                } else {
                    // Hostname not long enough
                    $this->_error(self::UNDECIPHERABLE_TLD);
                    $status = false;
                }
            } while (false);

            iconv_set_encoding('internal_encoding', $origenc);
            // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
            // passes validation
            if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
                return true;
            }
        } else if ($this->_options['allow'] & self::ALLOW_DNS) {
            $this->_error(self::INVALID_HOSTNAME);
        }

        // Check for URI Syntax (RFC3986)
        if ($this->_options['allow'] & self::ALLOW_URI) {
            if (preg_match("/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value)) {
                return true;
            } else {
                $this->_error(self::INVALID_URI);
            }
        }

        // Check input against local network name schema; last chance to pass validation
        $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}[\x2e]{0,1}){1,254}$/';
        $status = @preg_match($regexLocal, $value);

        // If the input passes as a local network name, and local network names are allowed, then the
        // hostname passes validation
        $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
        if ($status && $allowLocal) {
            return true;
        }

        // If the input does not pass as a local network name, add a message
        if (!$status) {
            $this->_error(self::INVALID_LOCAL_NAME);
        }

        // If local network names are not allowed, add a message
        if ($status && !$allowLocal) {
            $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
        }

        return false;
    }
Does anyone have any idea what's going on with this error? :banghead:

Thanks in advance - Pavilion
Pavilion
Forum Contributor
Posts: 301
Joined: Thu Feb 23, 2012 6:51 am

Re: Reading email files

Post by Pavilion »

tr0gd0rr wrote:Reading emails manually is very complicated because of all the MIME rules etc. I'm using Zend_Mail right now and it works great. You can use it to connect via pop/imap and read emails or you can read from a file like you are wanting:

Code: Select all

$message = new Zend_Mail_Message(array('file'=>'/home/myaccount/mail/mysite.com/admin/new/message1.whatever'));
$from = $message->getHeader('from');
if (preg_match('/<([^>]+)>/', $from, $match)) {
	$fromEmail = $match[1];
}
else {
	$fromEmail = $from;
}
$fromEmail = strtolower(trim($fromEmail));
OK - I've finally got Zend functioning. I'm successfully sending via SMTP and reading emails. Now - I've got some questions, following is my script:

For sending:

Code: Select all

try
{
$mail = new Zend_Mail();
$mail->setReplyTo('Admin@1stContactDatabases.com');
$mail->addHeader('MessageID',$participant);
$mail->setFrom($user_email);
$mail->addTo($email);
$mail->setSubject(stripslashes($subject));
$mail->setBodyHtml
(
For Reading

Code: Select all

 
    echo $mail->countMessages() . " messages found\n";

    foreach ($mail as $message) {
		var_dump($message);
		echo "<br /><br />";
		echo "Mail Return Path: " . $message->getHeader('content-type', 'string') . "<br /><br />";
		echo "Mail Received: '{$message->received}': {$message->subject}\n" . "<br /><br />";
        echo "Mail TO: '{$message->to}': {$message->subject}\n" . "<br />";
		echo "Mail From: '{$message->from}': {$message->subject}\n" . "<br /><br />";		
    }

Each recipient has a UserID, How do I define the UserID as a uniqueID header that will return in a bounced email's header?


Thanks in advance - Pavilion
User avatar
tr0gd0rr
Forum Contributor
Posts: 305
Joined: Thu May 11, 2006 8:58 pm
Location: Utah, USA

Re: Reading email files

Post by tr0gd0rr »

I don't think you can define a custom email header that will get returned in a bounced email. What you'll need to do is use a "Message-ID" header that uniquely identifies the email. The Message-ID will get passed around when the message is returned, forwarded, or replied to.

It depends on your SMTP server / service on whether you can set the Message-ID yourself. Then you'll need to store the Message-ID in your database with columns for UserID and whatever info you want.

I use Amazon SES (Simple Email Service), for example, and you cannot set your own Message-ID, but the Amazon-assigned Message-ID is returned in the success message.
Post Reply