mirror of
https://github.com/bcit-ci/CodeIgniter.git
synced 2025-02-20 11:13:29 +08:00
added class
parent
210f1605b5
commit
4903506fd8
972
IMAP-POP3-Wrapper-Class.md
Executable file
972
IMAP-POP3-Wrapper-Class.md
Executable file
@ -0,0 +1,972 @@
|
||||
[code]
|
||||
<? if (!defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/*
|
||||
|
||||
An interface for the PHP IMAP functions
|
||||
Grabs email messages from a POP3 or IMAP
|
||||
account so they can be put into a database
|
||||
|
||||
This code's only link to a database is the nested
|
||||
associative array built in the grab_email_as_array()
|
||||
function - it serves as a rudimentary 'interface'
|
||||
but, is limited because it embeds the field names
|
||||
directly in this class
|
||||
|
||||
If there are attachments, this class
|
||||
creates a directory for each email that
|
||||
has an attachment and puts them all there
|
||||
|
||||
Known limitations:
|
||||
tested on POP3 server only, should work on IMAP, but never been tested
|
||||
only extracts the last HTML and PLAIN sub parts in a message
|
||||
may be conflicts with filenames of attachments all in one dir
|
||||
|
||||
Class Created by: sophistry
|
||||
--inspired by CodeIgniter, but not beholden to it
|
||||
20079019
|
||||
|
||||
*/
|
||||
|
||||
class email_grabber {
|
||||
|
||||
// despite the names, these IMAP vars
|
||||
// can be pop3 server vars too
|
||||
var $IMAP_server;
|
||||
var $IMAP_login;
|
||||
var $IMAP_pass;
|
||||
|
||||
var $IMAP_service_flags;
|
||||
var $IMAP_mailbox;
|
||||
|
||||
var $IMAP_resource;
|
||||
var $IMAP_state;
|
||||
var $connected;
|
||||
|
||||
// /full/path/to/attachment/directory/
|
||||
// (dir must have www chmod read/write permissions)
|
||||
var $IMAP_attachment_dir;
|
||||
|
||||
// make this property so we can separate
|
||||
// the parts parsing into its own method
|
||||
var $parts_array = array();
|
||||
|
||||
// when extract_a_part() is called
|
||||
// it fills these vars with the string
|
||||
// found in the PLAIN and HTML parts
|
||||
// for the current message being processed
|
||||
// only keeps the last part, so it will only
|
||||
// keep the last of multiple HTML parts
|
||||
var $PLAIN;
|
||||
var $HTML;
|
||||
|
||||
var $msg_id = 0;
|
||||
var $msg_count = 0;
|
||||
|
||||
|
||||
// constructor function handles array items passed
|
||||
// by the config file loader in CI, or they can be passed
|
||||
// directly into the new() step
|
||||
// this design allows the library to be autoloaded
|
||||
// and used in multiple controllers
|
||||
function email_grabber($init_array = NULL)
|
||||
{
|
||||
// if no init parameter sent
|
||||
// nothing happens in constructor
|
||||
// user has to call connect_and_count()
|
||||
// and pass the parameters
|
||||
if ( !is_null($init_array) )
|
||||
{
|
||||
// handle the array items
|
||||
// pass to connect_and_count()
|
||||
$this->connect_and_count($init_array);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// grab email from a POP3 account
|
||||
function connect_and_count($init_array)
|
||||
{
|
||||
// these array items need to be the
|
||||
// same names as the config items OR
|
||||
// the db table fields that store the account info
|
||||
$this->IMAP_server = $init_array['server'];
|
||||
$this->IMAP_login = $init_array['login'];
|
||||
$this->IMAP_pass = $init_array['pass'];
|
||||
|
||||
$this->IMAP_service_flags = $init_array['service_flags'];
|
||||
$this->IMAP_mailbox = $init_array['mailbox'];
|
||||
// grab the resource returned by imap_open()
|
||||
// suppress warning with @ so we can handle it internally
|
||||
$imap_str = '{'. $this->IMAP_server . $this->IMAP_service_flags.'}'.$this->IMAP_mailbox;
|
||||
//p($imap_str);
|
||||
$this->IMAP_resource = @imap_open($imap_str, $this->IMAP_login, $this->IMAP_pass);
|
||||
|
||||
if($this->IMAP_resource)
|
||||
{
|
||||
// handle the strange mailbox is empty error
|
||||
// so we can just get on with things
|
||||
// this clears all errors in the stack
|
||||
// but, at this point there shouldn't be more than one
|
||||
$err = imap_errors();
|
||||
if($err[0] == 'Mailbox is empty')
|
||||
{
|
||||
// keep the state
|
||||
$this->IMAP_state = 'Mailbox is empty';
|
||||
}
|
||||
|
||||
// store the number of emails
|
||||
// waiting at server into var
|
||||
$m = $this->count_messages();
|
||||
$this->IMAP_state = 'Connected. Message count: ' . $m;
|
||||
$this->connected = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->IMAP_state = 'Not connected';
|
||||
$this->connected = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of emails at server
|
||||
* Calling this function updates msg_count var
|
||||
*/
|
||||
function count_messages()
|
||||
{
|
||||
$this->msg_count = imap_num_msg($this->IMAP_resource);
|
||||
return $this->msg_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of messages returned by latest
|
||||
* call to the imap_num_msg() function that
|
||||
* was stored in the property
|
||||
* ACCESSOR method
|
||||
*/
|
||||
function get_message_count()
|
||||
{
|
||||
return $this->msg_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of emails waiting
|
||||
* returns empty array if no messages
|
||||
* SLOW: all these functions process about 5 messages per second!
|
||||
* But, there seems to be some kind of cache that
|
||||
* speeds up subsequent mailbox requests
|
||||
*/
|
||||
function get_message_list_overview()
|
||||
{
|
||||
$a = imap_fetch_overview($this->IMAP_resource,'1:*');
|
||||
return $a;
|
||||
}
|
||||
|
||||
// this is fairly useless because imap_headers()
|
||||
// returns a string rather than an array
|
||||
// not faster than the other listers
|
||||
// but it may be useful later
|
||||
function get_message_list_headers()
|
||||
{
|
||||
$a = imap_headers($this->IMAP_resource);
|
||||
return $a;
|
||||
}
|
||||
|
||||
// loop over the messages by hand
|
||||
// may be the best function to use
|
||||
// to allow user feedback (i.e., progress bar)
|
||||
function get_message_list_loop($start=0, $end=0)
|
||||
{
|
||||
$a = array();
|
||||
|
||||
// start is the id
|
||||
// start it one down
|
||||
// because id immediately
|
||||
// increments in the next loop
|
||||
$id = ($start==0) ? 0 : $start-1;
|
||||
$end = ($end==0) ? $this->msg_count : $end;
|
||||
|
||||
while ($id++ < $end)
|
||||
{
|
||||
$a[] = imap_headerinfo($this->IMAP_resource, $id);
|
||||
// it is not faster to use imap_fetchheader
|
||||
//$a[] = imap_fetchheader($this->IMAP_resource, $id);
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
|
||||
// set the directory where we will store
|
||||
// attachments. check it is there and
|
||||
// writeable by the webserver
|
||||
function set_IMAP_attachment_dir($dir)
|
||||
{
|
||||
if (is_dir($dir))
|
||||
{
|
||||
$this->IMAP_state = 'Attachment directory exists: ' . $dir;
|
||||
if (is_writeable($dir))
|
||||
{
|
||||
$this->IMAP_state = 'Attachment directory writeable: ' . $dir;
|
||||
$this->IMAP_attachment_dir = $dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->IMAP_state = 'Attachment directory NOT writeable: ' . $dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets the msg_id and then checks it
|
||||
// returns FALSE if id is > msg_count
|
||||
// otherwise TRUE
|
||||
// should also set an error here
|
||||
function set_msg_id($id)
|
||||
{
|
||||
$this->msg_id = $id;
|
||||
// make sure it is less than message count
|
||||
// and greater than zero
|
||||
$id_ok = (bool)( ($this->msg_count >= $this->msg_id) && ($this->msg_id > 0) );
|
||||
if (!$id_ok)
|
||||
{
|
||||
$this->IMAP_state = 'Not a valid message id: ' . $this->msg_id;
|
||||
//unset($this->msg_id);
|
||||
}
|
||||
return $id_ok;
|
||||
}
|
||||
|
||||
// Normal POP3 does not mark messages for later deletion
|
||||
// must delete them and expunge them in same connection
|
||||
// TRUE on success, FALSE on failure
|
||||
// BUT... Google's gmail service does mark as read and
|
||||
// then does not serve them up to POP3 again even though
|
||||
// they are still in the INBOX. They are doing some extra
|
||||
// thing to the message to make it invisible once it has been
|
||||
// picked up by ANY POP3 request that grabs the email data
|
||||
// So, gmail does not seem to be affected by imap_delete() imap_expunge(),
|
||||
// it only cares about its own settings with regard to how
|
||||
// to handle message storage after a POP3 connection
|
||||
function delete_and_expunge($id_or_range)
|
||||
{
|
||||
// make sure we've got a message there of that id
|
||||
// and it's not a bogus id like 0 or -1
|
||||
// also allow ranges to be sent to this function
|
||||
// format 1:5, so if a colon is sent, we assume
|
||||
// it is properly formatted - not the best idea
|
||||
// should check the full formatting with regexp '/[0-9]+:[0-9\*]/'
|
||||
if ($this->set_msg_id($id_or_range) || strpos($id_or_range,':') || strpos($id_or_range,','))
|
||||
{
|
||||
// should capture error here when the message doesn't exist
|
||||
imap_delete($this->IMAP_resource, $this->msg_id);
|
||||
imap_expunge($this->IMAP_resource);
|
||||
$this->IMAP_state = 'Deleted and Expunged message id_or_range: ' . $this->msg_id;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->IMAP_state = 'Could not delete and expunge message id_or_range: ' . $this->msg_id;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// deal with most of the variations in the msg_id spec
|
||||
// just handles 0, single value whole number, or colon range x:y
|
||||
// other variations (not handled at the moment)
|
||||
// are comma separated list of ids and 1:* (star)
|
||||
function _prepare_msg_ids ($id_or_range=0)
|
||||
{
|
||||
if (!$id_or_range)
|
||||
{
|
||||
// zero or EMPTY means get all emails in mailbox
|
||||
$id_start = 1;
|
||||
$id_end = $this->msg_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// just a number, assign both start and end
|
||||
$id_start = $id_end = $id_or_range;
|
||||
}
|
||||
|
||||
// range value, explode, overwrite previous
|
||||
if (strpos($id_or_range,':'))
|
||||
{
|
||||
list($id_start, $id_end) = explode(':',$id_or_range);
|
||||
}
|
||||
|
||||
return array($id_start,$id_end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get email by coordinating multiple imap functions
|
||||
* imap_fetchstructure, imap_fetchbody,
|
||||
* imap_fetchheader, and/or imap_body
|
||||
* (imap_fetchbody is called in sub-routine extract_a_part)
|
||||
* gmail "removes" the message from the POP3 INBOX (if set to do so)
|
||||
* when you call any body or structure type of imap_ function
|
||||
* but does not "remove" it when you call header imap_ functions
|
||||
*/
|
||||
function grab_email_as_array($id = 0)
|
||||
{
|
||||
//p($id);
|
||||
// set this to 1 to keep errors in imap parsing out of the php stack
|
||||
$cap_errs = 0;
|
||||
// we are going to return this
|
||||
// array with all the email data
|
||||
// from one email message in it
|
||||
// hold the email addresses in arrays
|
||||
// for transport to the related db tables
|
||||
$email_arrays_array = array();
|
||||
// email elements that are not arrays -
|
||||
// they are in the email table
|
||||
$email_strings_array = array();
|
||||
// an array to hold them both
|
||||
$main_email_array = array();
|
||||
// set the msg_id if one is sent in
|
||||
// if not valid, return empty array
|
||||
$bool = $this->set_msg_id($id);
|
||||
|
||||
if (!$bool) return $email_array;
|
||||
|
||||
// make sure we start fresh
|
||||
unset($this->PLAIN);
|
||||
unset($this->HTML);
|
||||
|
||||
// get the header info first
|
||||
// NOTE: this function does not remove the message from gmail's INBOX
|
||||
$header_obj=imap_headerinfo($this->IMAP_resource, $this->msg_id);
|
||||
//p($header_obj);exit();
|
||||
|
||||
// check for errors here to clear the
|
||||
// error stack and prevent it from posting
|
||||
// errors about badly formatted emails
|
||||
// should probably store the errors with the email in the db
|
||||
if ($cap_errs) $err = imap_errors();
|
||||
//p($err);
|
||||
|
||||
// fill the parts_array var with the parts
|
||||
// $structure is the map to the email message
|
||||
|
||||
// NOTE: calling this function removes message from
|
||||
// gmail's POP3 INBOX - not by deleting it, but making
|
||||
// it effectively invisible (depending on gmail account's POP3 settings)
|
||||
$structure = imap_fetchstructure($this->IMAP_resource, $this->msg_id);
|
||||
//p($structure);
|
||||
//exit();
|
||||
// check for erros here to clear the
|
||||
// error stack and prevent it from posting
|
||||
// errors about badly formatted emails
|
||||
// should probably store the errors with the email in the db
|
||||
if ($cap_errs) $err = imap_errors();
|
||||
|
||||
// could pull out the raw email body here for storage/export potential
|
||||
//$text=imap_body($this->IMAP_resource,$this->msg_id);
|
||||
//p($text);//exit();
|
||||
// see if it is a multipart messsage
|
||||
// should handle bothe these cases in the extract_a_part function
|
||||
if (isset($structure->parts) && count($structure->parts))
|
||||
{
|
||||
// extract every part of the email into the array_parts var
|
||||
// this is a custom array to help unify what we need from the parts
|
||||
foreach ($structure->parts as $index => $part_def_obj)
|
||||
{
|
||||
// extract this part of email
|
||||
// if this is a PLAIN or HTML part it will
|
||||
// be written to the respective property
|
||||
$this->extract_a_part($part_def_obj,$index+1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a multipart message
|
||||
// get the body of message
|
||||
|
||||
// NOTE: calling this function removes message from
|
||||
// gmail's POP3 INBOX - not deleting it, but making
|
||||
// it effectively invisible
|
||||
$text=imap_body($this->IMAP_resource,$this->msg_id);
|
||||
// decode if quoted-printable
|
||||
if ($structure->encoding==4) $text=quoted_printable_decode($text);
|
||||
|
||||
// create a var for $this->PLAIN or $this->HTML
|
||||
$this->{$structure->subtype} = $text;
|
||||
$this->parts_array['not multipart']['text'] = array('type'=>$structure->subtype,'string'=>$text);
|
||||
}
|
||||
//p($this->parts_array);
|
||||
//exit();
|
||||
|
||||
// start stuffing the single email array
|
||||
|
||||
// first make sure the header_obj has the properties
|
||||
// we want to use later in this code so we don't have to
|
||||
// do a bunch of isset checks to avoid PHP warnings
|
||||
// from to message_id subject date udate Size
|
||||
// also gather the data for later fingerprinting
|
||||
// by stringing out the data points that won't change
|
||||
// these are items in the email that come here as arrays
|
||||
// rather than strings so they are handled differently
|
||||
// NOTE: the only time a BCC array will be set is if
|
||||
// you are looking at sent mail or the mailserver does something
|
||||
// unusual to show that there was a BCC but it was removed.
|
||||
// otherwise the BCC state will
|
||||
// have to be deduced from the lack of address in the to or cc
|
||||
// fields (as well as any other fields like Resent-To: etc...)
|
||||
// and its presence in one of the Received: header strings
|
||||
// Resent-To: addresses have to be parsed out of the header manually
|
||||
$address_keys_we_need_to_be_set = explode(' ', 'from to cc bcc reply_to sender return_path');
|
||||
$other_keys_we_need_to_be_set = explode(' ', 'message_id subject date udate Size');
|
||||
$data_points_for_fingerprint = explode(' ', 'fromaddress toaddress subject date');
|
||||
$email_data_to_use_in_fingerprint = '';
|
||||
foreach ($other_keys_we_need_to_be_set as $prop)
|
||||
{
|
||||
$header_obj->$prop = isset($header_obj->$prop) ? $header_obj->$prop : '';
|
||||
}
|
||||
|
||||
// turn each of the arrays of objects into arrays of arrays
|
||||
// with each address part getting encoding
|
||||
foreach ($address_keys_we_need_to_be_set as $key)
|
||||
{
|
||||
// make sure the array item is set
|
||||
if (isset($header_obj->$key))
|
||||
{
|
||||
// it's there,
|
||||
// variable is named for the key
|
||||
$$key = array();
|
||||
$arr = array();
|
||||
foreach ($header_obj->$key as $obj)
|
||||
{
|
||||
// coerce it to an array
|
||||
$arr[] = (array)$obj;
|
||||
//p($key);
|
||||
//p($arr);
|
||||
// take the personal part and apply decoding
|
||||
if (isset($arr['personal']))
|
||||
{
|
||||
$arr['personal'] = $this->decode_mime_text($arr['personal']);
|
||||
}
|
||||
|
||||
// push the array onto the array
|
||||
array_push( $$key, $arr );
|
||||
}
|
||||
$email_arrays_array[$key] = $arr;
|
||||
//p($$key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//p($to);p($from);p($header_obj);exit();
|
||||
|
||||
foreach ($data_points_for_fingerprint as $prop)
|
||||
{
|
||||
// will use this subset of raw strings later in fingerprinting
|
||||
$email_data_to_use_in_fingerprint .= $header_obj->$prop;
|
||||
}
|
||||
//p($email_data_to_use_in_fingerprint);exit();
|
||||
|
||||
// the email_strings_array keys correspond to database fields
|
||||
// this will make it easy to add the data to the email table
|
||||
|
||||
// NOTE: this header function does not remove the message from gmail's INBOX
|
||||
$email_strings_array['header'] = imap_fetchheader($this->IMAP_resource, $this->msg_id);
|
||||
//p($email_strings_array['header']);exit();
|
||||
|
||||
// use the extract_headers_to_array() fn
|
||||
// to get any header that is not included
|
||||
// in the native imap_ function calls
|
||||
// commented here since it is not in use
|
||||
//$header_array = $this->extract_headers_to_array($email_strings_array['header']);
|
||||
|
||||
$email_strings_array['message_id'] = $header_obj->message_id;
|
||||
$email_strings_array['subject'] = $this->decode_mime_text($header_obj->subject);
|
||||
$email_strings_array['date_string']= $header_obj->date;
|
||||
$email_strings_array['date_sent_stamp'] = date("Y-m-d H:i:s",$header_obj->udate);
|
||||
// this is actually the datestamp of
|
||||
// when the message was put into this array
|
||||
// rather than "received" (which should better
|
||||
// be the datestamp for when the message
|
||||
// was accepted to the receiving SMTP server
|
||||
$email_strings_array['date_received_stamp'] = date("Y-m-d H:i:s");
|
||||
$email_strings_array['size'] = $header_obj->Size;
|
||||
$email_strings_array['text'] = (isset($this->PLAIN)) ? $this->PLAIN : '';
|
||||
$email_strings_array['html'] = (isset($this->HTML)) ? $this->HTML : '';
|
||||
|
||||
// set a temporary array item to enable
|
||||
// message to be deleted at mailserver to
|
||||
// sync with db, unset before db insert
|
||||
// not needed for google's gmail
|
||||
// since they make POP3 pulled emails invisible
|
||||
// once they are pulled one time
|
||||
$email_strings_array['temp_msg_id']= $this->msg_id;
|
||||
|
||||
// generate a unique id so we
|
||||
// can do reliable dupe detection
|
||||
// hashes the string representation
|
||||
// of some datapoints of this email
|
||||
// so, if we get the same email again
|
||||
// it will result in the same hash
|
||||
// we could do basic dupe detection using
|
||||
// message_id, but it is not reliable
|
||||
// because it is not always there
|
||||
$email_strings_array['email_fingerprint_auto'] = $this->email_fingerprint( $email_data_to_use_in_fingerprint );
|
||||
//p($email_array);exit();
|
||||
|
||||
$this->IMAP_state = 'Got email as array, message id: ' . $this->msg_id;
|
||||
|
||||
// load them up as two items in the main array
|
||||
$main_email_array['strings'] = $email_strings_array;
|
||||
// arrays are separated because thay will be used to
|
||||
// populate related database tables in a normalized email storage schema
|
||||
$main_email_array['arrays'] = $email_arrays_array;
|
||||
|
||||
return $main_email_array;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// standard place to do fingerprinting
|
||||
// so we can use it to check email dupes
|
||||
// send it a string with unchanging data
|
||||
// that are pulled from the email header
|
||||
// could "salt" this but md5'ing the var_export
|
||||
// is kind of like salt (except if everyone does it!)
|
||||
function email_fingerprint($str)
|
||||
{
|
||||
return md5(var_export($str,TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse e-mail structure into array var
|
||||
* this will handle nested parts properly
|
||||
* it will recurse and use the initial part number
|
||||
* concatenating it with nested parts using dot .
|
||||
* Useful information copied from http://php.net
|
||||
|
||||
Table 142. Returned Objects for imap_fetchstructure()
|
||||
|
||||
type Primary body type
|
||||
encoding Body transfer encoding
|
||||
ifsubtype TRUE if there is a subtype string
|
||||
subtype MIME subtype
|
||||
ifdescription TRUE if there is a description string
|
||||
description Content description string
|
||||
ifid TRUE if there is an identification string
|
||||
id Identification string
|
||||
lines Number of lines
|
||||
bytes Number of bytes
|
||||
ifdisposition TRUE if there is a disposition string
|
||||
disposition Disposition string
|
||||
ifdparameters TRUE if the dparameters array exists
|
||||
dparameters An array of objects where each object has an "attribute" and a "value" property corresponding to the parameters on the Content-disposition MIMEheader.
|
||||
ifparameters TRUE if the parameters array exists
|
||||
parameters An array of objects where each object has an "attribute" and a "value" property.
|
||||
parts An array of objects identical in structure to the top-level object, each of which corresponds to a MIME body part.
|
||||
|
||||
Table 143. Primary body type
|
||||
0 text
|
||||
1 multipart
|
||||
2 message
|
||||
3 application
|
||||
4 audio
|
||||
5 image
|
||||
6 video
|
||||
7 model
|
||||
8 other
|
||||
9 unknown/unknown
|
||||
|
||||
Table 144. Transfer encodings
|
||||
0 7BIT
|
||||
1 8BIT
|
||||
2 BINARY
|
||||
3 BASE64
|
||||
4 QUOTED-PRINTABLE
|
||||
5 OTHER
|
||||
*/
|
||||
|
||||
function extract_a_part($part_def_obj, $part_number)
|
||||
{
|
||||
// get just one part as a string
|
||||
$part_string=imap_fetchbody($this->IMAP_resource, $this->msg_id, $part_number);
|
||||
|
||||
//p($part_string);
|
||||
// DECODE the part
|
||||
// if base64
|
||||
if ($part_def_obj->encoding==3) $part_string=base64_decode($part_string);
|
||||
// if quoted printable
|
||||
if ($part_def_obj->encoding==4) $part_string=quoted_printable_decode($part_string);
|
||||
// If binary or 8bit - we don't need to decode
|
||||
|
||||
$sub_type = strtoupper($part_def_obj->subtype);
|
||||
// attachments, multipart types are 1-9
|
||||
|
||||
//p($part_def_obj);
|
||||
if ($part_def_obj->type)
|
||||
{
|
||||
// determine body type (more to do here)
|
||||
switch($part_def_obj->type)
|
||||
{
|
||||
case '5': // image, should put proper name here for the image
|
||||
//$this->parts_array[$part_number]['image'] = array('filename'=>'IMAGE', 'string'=>$part_string, 'part_no'=>$part_number);
|
||||
break;
|
||||
}
|
||||
|
||||
// get an attachment, set filename to dparameter value
|
||||
$filename='';
|
||||
if ($part_def_obj->ifdparameters && count($part_def_obj->dparameters))
|
||||
{
|
||||
foreach ($part_def_obj->dparameters as $dp)
|
||||
{
|
||||
if ((strtoupper($dp->attribute)=='NAME') || (strtoupper($dp->attribute)=='FILENAME')) $filename=$dp->value;
|
||||
}
|
||||
}
|
||||
// if no filename yet, set filename to parameter value
|
||||
if ($filename=='')
|
||||
{
|
||||
if ($part_def_obj->ifparameters && count($part_def_obj->parameters))
|
||||
{
|
||||
foreach ($part_def_obj->parameters as $p)
|
||||
{
|
||||
if ((strtoupper($p->attribute)=='NAME') || (strtoupper($p->attribute)=='FILENAME')) $filename=$p->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we've got to have a filename by now!
|
||||
if ($filename!='' )
|
||||
{
|
||||
$this->parts_array[$part_number]['attachment'] =
|
||||
array('filename'=>$filename,
|
||||
'string'=>$part_string,
|
||||
'encoding'=>$part_def_obj->encoding,
|
||||
'part_no'=>$part_number,
|
||||
'type'=>$part_def_obj->type,
|
||||
'subtype'=>$sub_type);
|
||||
}
|
||||
|
||||
// now write the attachments to the disk
|
||||
|
||||
// Get store dir, call this every message based on
|
||||
// the email data so it can hold all parts for a single email
|
||||
//$dir = $this->dir_name();
|
||||
$a_f = $this->decode_mime_text($filename);
|
||||
// replace crap with underscore, there is a CI function to do this
|
||||
$a_f = preg_replace('/[^a-z0-9_\-\.]/i', '_', $a_f);
|
||||
//$this->save_files($dir.$a_f, $part_string);
|
||||
|
||||
}
|
||||
// Text or HTML email, type is 0
|
||||
else
|
||||
{
|
||||
// creates an instance var for $this->HTML or $this->PLAIN
|
||||
// NOTE: only works for the last part or sub-part extracted
|
||||
$this->$sub_type = $part_string;
|
||||
$this->parts_array[$part_number]['text'] = array('type'=>$sub_type,'string'=>$part_string);
|
||||
|
||||
}
|
||||
|
||||
// if there are subparts call this function recursively
|
||||
if (isset($part_def_obj->parts) && count($part_def_obj->parts))
|
||||
{
|
||||
foreach ($part_def_obj->parts as $index => $sub_part_def_obj)
|
||||
{
|
||||
$this->extract_a_part($sub_part_def_obj, ($part_number.'.'.($index+1)));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// get mime meta data
|
||||
// this needs some attention
|
||||
function decode_mime_text($str)
|
||||
{
|
||||
|
||||
$txt = '';
|
||||
$str = htmlspecialchars(chop($str));
|
||||
|
||||
$elements = imap_mime_header_decode($str);
|
||||
if(is_array($elements))
|
||||
{
|
||||
for ($i=0; $i<count($elements); $i++)
|
||||
{
|
||||
$charset = $elements[$i]->charset;
|
||||
$txt .= $elements[$i]->text;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$txt = $str;
|
||||
}
|
||||
|
||||
if($txt == '')
|
||||
{
|
||||
$txt = 'NO DATA - value missing';
|
||||
}
|
||||
|
||||
return $txt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function
|
||||
* Extract an array listing from the header
|
||||
* This code makes sure this is done in a way that we get
|
||||
* all the possible headers (multi-line, domain keys,
|
||||
* repeated headers , etc...) tucked away nicely into
|
||||
* a nested array
|
||||
*
|
||||
* To Do: 20070904
|
||||
* These headers (resent) have to be plucked out
|
||||
* as they are not supported in the imap_headerinfo() function
|
||||
* used in the grab_email_as_array() function above
|
||||
*
|
||||
* resent-from = "Resent-From:" mailbox-list CRLF
|
||||
* resent-sender = "Resent-Sender:" mailbox CRLF
|
||||
* resent-to = "Resent-To:" address-list CRLF
|
||||
* resent-cc = "Resent-Cc:" address-list CRLF
|
||||
* resent-bcc = "Resent-Bcc:" (address-list / [CFWS]) CRLF
|
||||
*/
|
||||
function extract_headers_to_array($header)
|
||||
{
|
||||
//p($header);
|
||||
$header_array = explode("\n", rtrim($header));
|
||||
// drop off any empty, null or FALSE values
|
||||
$header_array=array_filter($header_array);
|
||||
//p($header_array);
|
||||
|
||||
$new_header_array = array();
|
||||
foreach ($header_array as $key => $line)
|
||||
{
|
||||
//p($new_header_array);
|
||||
// check if this line starts with a header name
|
||||
// if it does, build the new header item
|
||||
// if it doesn't, build the string out
|
||||
if (preg_match('/^([^:\s]+):\s(.+)/',$line,$m))
|
||||
{
|
||||
//p($m[1]);
|
||||
//p($m[2]);
|
||||
$current_header = $m[1];
|
||||
$current_data = $m[2];
|
||||
// if there is no header by this name yet
|
||||
// set the data, otherwise, append it as array item
|
||||
if (!isset($new_header_array[$current_header]))
|
||||
{
|
||||
// this is the normal branch, new header, one line of data
|
||||
$new_header_array[$current_header] = $current_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it is not an array, it is a string and we need
|
||||
// to convert the existing data to an array, and add the new
|
||||
if (!is_array($new_header_array[$current_header]))
|
||||
{
|
||||
// this runs when a header name is repeated (like Received often is)
|
||||
// runs the 1st time it is repeated (second occurance of the header)
|
||||
// converts the existing string and the incoming string to a 2-item sub-array
|
||||
$new_header_array[$current_header] = array($new_header_array[$current_header],$current_data);
|
||||
}
|
||||
else
|
||||
// if it is already an array then append an array item
|
||||
{
|
||||
// this runs when a header name is repeated (like Received often is)
|
||||
// runs 3rd and subsequent times
|
||||
$new_header_array[$current_header][] = $current_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it is already an array then append
|
||||
// the string to the last sub-array item
|
||||
// because we assume the lines with no header names
|
||||
// are part of the most recently added sub-array item
|
||||
if (is_array($new_header_array[$current_header]))
|
||||
{
|
||||
// this runs if there has already been a header of the same header name
|
||||
$new_header_array[$current_header][count($new_header_array[$current_header])-1] .= $line;
|
||||
}
|
||||
else
|
||||
// if it is not an array, it is still just a string and we need
|
||||
// to build the string out
|
||||
{
|
||||
// this runs if the line is part of the first header encountered
|
||||
// but is part of a long multiline string (like Received header)
|
||||
$new_header_array[$current_header] .= $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $new_header_array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to close the IMAP connection
|
||||
* Returns TRUE if closed with no errors
|
||||
*/
|
||||
function close()
|
||||
{
|
||||
if (is_resource($this->IMAP_resource))
|
||||
{
|
||||
$closed = imap_close($this->IMAP_resource);
|
||||
$this->IMAP_state = 'Connection closed.';
|
||||
}
|
||||
else
|
||||
{
|
||||
$closed = FALSE;
|
||||
$this->IMAP_state = 'Connection could not be closed - no resource.';
|
||||
}
|
||||
|
||||
return $closed;
|
||||
}
|
||||
|
||||
// This is a pretty lame function...
|
||||
// if it doesn't exist already
|
||||
// create a writeable directory
|
||||
// named for current month and year
|
||||
// where we can store attachments
|
||||
function make_dir($potential_name='')
|
||||
{
|
||||
//$dir_n = date('Y') . "_" . date('m');
|
||||
$dir_n = $potential_name;
|
||||
|
||||
if (!is_dir($this->IMAP_attachment_dir . $dir_n))
|
||||
mkdir($this->IMAP_attachment_dir . $dir_n, 0777);
|
||||
|
||||
return $dir_n . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Save messages on local disc, potential
|
||||
* name and file lock collision here
|
||||
*/
|
||||
function save_files($filename, $part)
|
||||
{
|
||||
$fp=fopen($this->IMAP_attachment_dir.$filename,"w+");
|
||||
fwrite($fp,$part);
|
||||
fclose($fp);
|
||||
chown($this->IMAP_attachment_dir.$filename, 'www');
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
// wrapper function to loop over the emails and get them all
|
||||
// in a nice tidy, decoded and converted format
|
||||
// accepts no param, one number (msgid), or range specified with x:y
|
||||
function grab_emails_as_nested_array($id_or_range = 0)
|
||||
{
|
||||
$a = array();
|
||||
|
||||
// deals with all the variations in msg_id spec
|
||||
list($id_start, $id_end) = $this->_prepare_msg_ids($id_or_range);
|
||||
|
||||
for ($id=$id_start; $id <= $id_end; $id++)
|
||||
{
|
||||
$a[] = $this->grab_email_as_array($id);
|
||||
}
|
||||
return $a;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
// This is the same function wrapper as the nested array fn above
|
||||
// but, this one writes attachments to files too
|
||||
// loop over the emails and get them all
|
||||
// in a nice tidy, decoded and converted format
|
||||
// also, write the attachments to files in the attachment folder
|
||||
// accepts no param, one number (msgid), or range specified with x:y
|
||||
function grab_emails_as_nested_array_and_store($id_or_range = 0)
|
||||
{
|
||||
$a = array();
|
||||
|
||||
// deals with all the variations in msg_id spec
|
||||
list($id_start, $id_end) = $this->_prepare_msg_ids($id_or_range);
|
||||
|
||||
for ($id=$id_start; $id <= $id_end; $id++)
|
||||
{
|
||||
$a[] = $this->grab_email_as_array_and_store($id);
|
||||
}
|
||||
return $a;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
// grab the email and save attachments to files
|
||||
// this is just like grab_email_as_array() but,
|
||||
// attachment files are written to disk
|
||||
// this is the function to use when you are
|
||||
// transferring to a db and you want the files
|
||||
// to be written to disk at the same time all the
|
||||
// email data is transferred and deleted from the mailserver
|
||||
//
|
||||
function grab_email_as_array_and_store($id = 0)
|
||||
{
|
||||
// make sure we've got a message there of that id
|
||||
// and it's not a bogus id like 0 or -1
|
||||
if (!$this->set_msg_id($id)) return FALSE;
|
||||
|
||||
// Get message header as array of pertinent values
|
||||
$email_array = $this->grab_email_as_array($this->msg_id);
|
||||
|
||||
// Get store dir, call this every message based on
|
||||
// the email data so it can hold all parts for a single email
|
||||
// make a new dir based on the fingerprint
|
||||
$dir = $this->make_dir($email_array['strings']['email_fingerprint_auto']);
|
||||
|
||||
// now write files into the new directory
|
||||
// named with the fingerprint hash
|
||||
|
||||
// I think this loop may be subject to missing some stuff
|
||||
// loop through the parts extracted in the grab_email_as_array() method
|
||||
// and either save them to disk (attachments) or write to db (HTML or text)
|
||||
// define the pattern that is going to make a nice filename
|
||||
// there is a CI function that does this
|
||||
$pattern_for_filename = '/[^a-z0-9_\-\.]/i';
|
||||
foreach ($this->parts_array as $part)
|
||||
{
|
||||
if (isset($part['text']['type']) AND ($part['text']['type'] == 'HTML' OR $part['text']['type'] == 'PLAIN'))
|
||||
{
|
||||
// handle PLAIN and HTML types elsewhere
|
||||
}
|
||||
elseif (isset($part['attachment']) AND $part['attachment'])
|
||||
{
|
||||
// Save file attachments to disk
|
||||
foreach (array($part['attachment']) as $attach)
|
||||
{
|
||||
$a_f = $this->decode_mime_text($attach['filename']);
|
||||
// replace crap with underscore, there is a CI function to do this
|
||||
$a_f = preg_replace($pattern_for_filename, '_', $a_f);
|
||||
$filename = $dir.$a_f;
|
||||
$this->save_files($filename, $attach['string']);
|
||||
}
|
||||
|
||||
}
|
||||
elseif (isset($part['image']) AND $part['image'])
|
||||
{
|
||||
// Save image attachments to disk
|
||||
foreach ($part['image'] as $image)
|
||||
{
|
||||
$i_f = $this->decode_mime_text($image['filename']);
|
||||
// replace crap with underscore, there is a CI function to do this
|
||||
$i_f = preg_replace($pattern_for_filename, '_', $i_f);
|
||||
$filename = $dir.$i_f;
|
||||
$this->save_files($filename, $image['string']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($email_array != '')
|
||||
{
|
||||
unset($this->parts_array);
|
||||
}
|
||||
|
||||
// return so the data can be used for a db insert
|
||||
return $email_array;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
[/code]
|
Loading…
x
Reference in New Issue
Block a user