Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1102220
mailbox_helper.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Aug 18, 7:35 AM
Size
16 KB
Mime Type
text/x-php
Expires
Wed, Aug 20, 7:35 AM (1 d, 2 h)
Engine
blob
Format
Raw Data
Handle
714010
Attached To
rINP In-Portal
mailbox_helper.php
View Options
<?php
/**
* @version $Id: mailbox_helper.php 16513 2017-01-20 14:10:53Z alex $
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined
(
'FULL_PATH'
)
or
die
(
'restricted access!'
);
class
MailboxHelper
extends
kHelper
{
var
$headers
=
Array
();
var
$parsedMessage
=
Array
();
/**
* Maximal megabytes of data to process
*
* @var int
*/
var
$maxMegabytes
=
2
;
/**
* Maximal message count to process
*
* @var int
*/
var
$maxMessages
=
50
;
/**
* Reads mailbox and gives messages to processing callback
*
* @param Array $connection_info
* @param Array $verify_callback
* @param Array $process_callback
* @param Array $callback_params
* @param bool $include_attachment_contents
* @return string
*/
function
process
(
$connection_info
,
$verify_callback
,
$process_callback
,
$callback_params
=
Array
(),
$include_attachment_contents
=
true
)
{
/** @var POP3Helper $pop3_helper */
$pop3_helper
=
$this
->
Application
->
makeClass
(
'POP3Helper'
,
Array
(
$connection_info
));
$connection_status
=
$pop3_helper
->
initMailbox
();
if
(
is_string
(
$connection_status
))
{
return
$connection_status
;
}
if
(
defined
(
'DEBUG_MODE'
)
&&
DEBUG_MODE
&&
$this
->
Application
->
isDebugMode
())
{
$this
->
Application
->
Debugger
->
appendHTML
(
'Reading MAILBOX: '
.
$connection_info
[
'username'
]);
}
// Figure out if all messages are huge
$only_big_messages
=
true
;
$max_message_size
=
$this
->
maxMegabytes
*
(
1024
*
1024
);
foreach
(
$pop3_helper
->
messageSizes
as
$message_size
)
{
if
((
$message_size
<=
$max_message_size
)
&&
(
$max_message_size
>
0
))
{
$only_big_messages
=
false
;
break
;
}
}
$count
=
$total_size
=
0
;
foreach
(
$pop3_helper
->
messageSizes
as
$message_number
=>
$message_size
)
{
// Too many messages?
if
((
$count
++
>
$this
->
maxMessages
)
&&
(
$this
->
maxMessages
>
0
))
{
break
;
}
// Message too big?
if
(!
$only_big_messages
&&
(
$message_size
>
$max_message_size
)
&&
(
$max_message_size
>
0
))
{
$this
->
_displayLogMessage
(
'message <strong>#'
.
$message_number
.
'</strong> too big, skipped'
);
continue
;
}
// Processed enough for today?
if
((
$total_size
>
$max_message_size
)
&&
(
$max_message_size
>
0
))
{
break
;
}
$total_size
+=
$message_size
;
$pop3_helper
->
getEmail
(
$message_number
,
$message_source
);
$processed
=
$this
->
normalize
(
$message_source
,
$verify_callback
,
$process_callback
,
$callback_params
,
$include_attachment_contents
);
if
(
$processed
)
{
// delete message from server immediatly after retrieving & processing
$pop3_helper
->
deleteEmail
(
$message_number
);
$this
->
_displayLogMessage
(
'message <strong>#'
.
$message_number
.
'</strong>: processed'
);
}
else
{
$this
->
_displayLogMessage
(
'message <strong>#'
.
$message_number
.
'</strong>: skipped'
);
}
}
$pop3_helper
->
close
();
return
'success'
;
}
/**
* Displays log message
*
* @param string $text
*/
function
_displayLogMessage
(
$text
)
{
if
(
defined
(
'DEBUG_MODE'
)
&&
DEBUG_MODE
&&
$this
->
Application
->
isDebugMode
())
{
$this
->
Application
->
Debugger
->
appendHTML
(
$text
);
}
}
/**
* Takes an RFC822 formatted date, returns a unix timestamp (allowing for zone)
*
* @param string $rfcdate
* @return int
*/
function
rfcToTime
(
$rfcdate
)
{
$date
=
strtotime
(
$rfcdate
);
if
(
$date
==
-
1
)
{
return
false
;
}
return
$date
;
}
/**
* Gets recipients from all possible headers
*
* @return string
*/
function
getRecipients
()
{
$ret
=
''
;
// headers that could contain recipients
$recipient_headers
=
Array
(
'to'
,
'cc'
,
'envelope-to'
,
'resent-to'
,
'delivered-to'
,
'apparently-to'
,
'envelope-to'
,
'x-envelope-to'
,
'received'
,
);
foreach
(
$recipient_headers
as
$recipient_header
)
{
if
(!
array_key_exists
(
$recipient_header
,
$this
->
headers
))
{
continue
;
}
if
(!
is_array
(
$this
->
headers
[
"$recipient_header"
]))
{
$ret
.=
' '
.
$this
->
headers
[
"$recipient_header"
];
}
else
{
$ret
.=
' '
.
implode
(
' '
,
$this
->
headers
[
"$recipient_header"
]);
}
}
return
$ret
;
}
/**
* "Flattens" the multi-demensinal headers array into a single dimension one
*
* @param Array $input
* @param string $add
* @return Array
*/
function
flattenHeadersArray
(
$input
,
$add
=
''
)
{
$output
=
Array
();
foreach
(
$input
as
$key
=>
$value
)
{
if
(!
empty
(
$add
))
{
$newkey
=
ucfirst
(
strtolower
(
$add
)
);
}
elseif
(
is_numeric
(
$key
))
{
$newkey
=
''
;
}
else
{
$newkey
=
ucfirst
(
strtolower
(
$key
)
);
}
if
(
is_array
(
$value
))
{
$output
=
array_merge
(
$output
,
$this
->
flattenHeadersArray
(
$value
,
$newkey
));
}
else
{
$output
[]
=
(!
empty
(
$newkey
)
?
$newkey
.
': '
:
''
)
.
$value
;
}
}
return
$output
;
}
/**
* Processes given message using given callbacks
*
* @param string $message
* @param Array $verify_callback
* @param Array $process_callback
* @param Array $callback_params
* @param bool $include_attachment_contents
* @return bool
* @access protected
*/
protected
function
normalize
(
$message
,
$verify_callback
,
$process_callback
,
$callback_params
,
$include_attachment_contents
=
true
)
{
// Decode message
$this
->
decodeMime
(
$message
,
$include_attachment_contents
);
// Init vars; $good will hold all the correct infomation from now on
$good
=
Array
();
// trim() some stuff now instead of later
$this
->
headers
[
'from'
]
=
trim
(
$this
->
headers
[
'from'
]);
$this
->
headers
[
'to'
]
=
trim
(
$this
->
headers
[
'to'
]);
$this
->
headers
[
'cc'
]
=
array_key_exists
(
'cc'
,
$this
->
headers
)
?
trim
(
$this
->
headers
[
'cc'
])
:
''
;
$this
->
headers
[
'x-forward-to'
]
=
array_key_exists
(
'x-forward-to'
,
$this
->
headers
)
?
$this
->
headers
[
'x-forward-to'
]
:
''
;
$this
->
headers
[
'subject'
]
=
trim
(
$this
->
headers
[
'subject'
]);
$this
->
headers
[
'received'
]
=
is_array
(
$this
->
headers
[
'received'
])
?
$this
->
headers
[
'received'
]
:
Array
(
$this
->
headers
[
'received'
]);
if
(
array_key_exists
(
'return-path'
,
$this
->
headers
)
&&
is_array
(
$this
->
headers
[
'return-path'
]))
{
$this
->
headers
[
'return-path'
]
=
implode
(
' '
,
$this
->
flattenHeadersArray
(
$this
->
headers
[
'return-path'
]));
}
// Create our own message-ID if it's missing
$message_id
=
array_key_exists
(
'message-id'
,
$this
->
headers
)
?
trim
(
$this
->
headers
[
'message-id'
])
:
''
;
$good
[
'emailid'
]
=
$message_id
?
$message_id
:
md5
(
$message
)
.
"@in-portal"
;
// Stops us looping in stupid conversations with other mail software
if
(
isset
(
$this
->
headers
[
'x-loop-detect'
])
&&
$this
->
headers
[
'x-loop-detect'
]
>
2
)
{
return
false
;
}
/** @var kEmailSendingHelper $esender */
$esender
=
$this
->
Application
->
recallObject
(
'EmailSender'
);
// Get the return address
$return_path
=
''
;
if
(
array_key_exists
(
'return-path'
,
$this
->
headers
))
{
$return_path
=
$esender
->
ExtractRecipientEmail
(
$this
->
headers
[
'return-path'
]);
}
if
(!
$return_path
)
{
if
(
array_key_exists
(
'reply-to'
,
$this
->
headers
))
{
$return_path
=
$esender
->
ExtractRecipientEmail
(
$this
->
headers
[
'reply-to'
]
);
}
else
{
$return_path
=
$esender
->
ExtractRecipientEmail
(
$this
->
headers
[
'from'
]
);
}
}
// Get the sender's name & email
$good
[
'fromemail'
]
=
$esender
->
ExtractRecipientEmail
(
$this
->
headers
[
'from'
]);
$good
[
'fromname'
]
=
$esender
->
ExtractRecipientName
(
$this
->
headers
[
'from'
],
$good
[
'fromemail'
]);
// Get the list of recipients.
if
(
!
call_user_func
(
$verify_callback
,
$callback_params
)
)
{
// Error: mail is probably spam.
return
false
;
}
// Handle the subject
$good
[
'subject'
]
=
$this
->
headers
[
'subject'
];
// Priorities rock
$good
[
'priority'
]
=
array_key_exists
(
'x-priority'
,
$this
->
headers
)
?
(
int
)
$this
->
headers
[
'x-priority'
]
:
0
;
switch
(
$good
[
'priority'
])
{
case
1
:
case
5
:
break
;
default
:
$good
[
'priority'
]
=
3
;
}
// If we have attachments it's about time we tell the user about it
if
(
array_key_exists
(
'attachments'
,
$this
->
parsedMessage
)
&&
is_array
(
$this
->
parsedMessage
[
'attachments'
]))
{
$good
[
'attach'
]
=
count
(
$this
->
parsedMessage
[
'attachments'
]
);
}
else
{
$good
[
'attach'
]
=
0
;
}
// prepare message text (for replies, etc)
if
(
isset
(
$this
->
parsedMessage
[
'text'
][
0
])
&&
trim
(
$this
->
parsedMessage
[
'text'
][
0
][
'body'
])
!=
''
)
{
$message_body
=
trim
(
$this
->
parsedMessage
[
'text'
][
0
][
'body'
]);
$message_type
=
'text'
;
}
elseif
(
isset
(
$this
->
parsedMessage
[
'html'
])
&&
trim
(
$this
->
parsedMessage
[
'html'
][
0
][
'body'
])
!=
''
)
{
$message_body
=
trim
(
$this
->
parsedMessage
[
'html'
][
0
][
'body'
]);
$message_type
=
'html'
;
}
else
{
$message_body
=
'[no message]'
;
$message_type
=
'text'
;
}
// remove scripts
$message_body
=
preg_replace
(
"/<script[^>]*>[^<]+<
\/
script[^>]*>/is"
,
''
,
$message_body
);
$message_body
=
preg_replace
(
"/<iframe[^>]*>[^<]*<
\/
iframe[^>]*>/is"
,
''
,
$message_body
);
if
(
$message_type
==
'html'
)
{
$message_body
=
$esender
->
ConvertToText
(
$message_body
);
}
/** @var MimeDecodeHelper $mime_decode_helper */
$mime_decode_helper
=
$this
->
Application
->
recallObject
(
'MimeDecodeHelper'
);
// convert to site encoding
$message_charset
=
$this
->
parsedMessage
[
$message_type
][
0
][
'charset'
];
if
(
$message_charset
)
{
$good
[
'message'
]
=
$mime_decode_helper
->
convertEncoding
(
$message_charset
,
$message_body
);
}
if
(
array_key_exists
(
'delivery-date'
,
$this
->
headers
))
{
// We found the Delivery-Date header (and it's not too far in the future)
$dateline
=
$this
->
rfcToTime
(
$this
->
headers
[
'delivery-date'
]);
if
(
$dateline
>
TIMENOW
+
86400
)
{
unset
(
$dateline
);
}
}
// We found the latest date from the received headers
$received_timestamp
=
$this
->
headers
[
'received'
][
0
];
$dateline
=
$this
->
rfcToTime
(
trim
(
substr
(
$received_timestamp
,
strrpos
(
$received_timestamp
,
';'
)
+
1
)
));
if
(
$dateline
==
$this
->
rfcToTime
(
0
))
{
unset
(
$dateline
);
}
if
(!
isset
(
$dateline
))
{
$dateline
=
TIMENOW
;
}
// save collected data to database
$fields_hash
=
Array
(
'DeliveryDate'
=>
$dateline
,
// date, when SMTP server received the message
'ReceivedDate'
=>
TIMENOW
,
// date, when message was retrieved from POP3 server
'CreatedOn'
=>
$this
->
rfcToTime
(
$this
->
headers
[
'date'
]),
// date, when created on sender's computer
'ReturnPath'
=>
$return_path
,
'FromEmail'
=>
$good
[
'fromemail'
],
'FromName'
=>
$good
[
'fromname'
],
'To'
=>
$this
->
headers
[
'to'
],
'Subject'
=>
$good
[
'subject'
],
'Message'
=>
$good
[
'message'
],
'MessageType'
=>
$message_type
,
'AttachmentCount'
=>
$good
[
'attach'
],
'MessageId'
=>
$good
[
'emailid'
],
'Source'
=>
$message
,
'Priority'
=>
$good
[
'priority'
],
'Size'
=>
strlen
(
$message
),
);
return
call_user_func
(
$process_callback
,
$callback_params
,
$fields_hash
);
}
/**
* Function that decodes the MIME message and creates the $this->headers and $this->parsedMessage data arrays
*
* @param string $message
* @param bool $include_attachments
*
*/
function
decodeMime
(
$message
,
$include_attachments
=
true
)
{
$message
=
preg_replace
(
"/
\r
?
\n
/"
,
"
\r\n
"
,
trim
(
$message
));
/** @var MimeDecodeHelper $mime_decode_helper */
$mime_decode_helper
=
$this
->
Application
->
recallObject
(
'MimeDecodeHelper'
);
// 1. separate headers from bodies
$mime_decode_helper
->
InitHelper
(
$message
);
$decoded_message
=
$mime_decode_helper
->
decode
(
true
,
true
,
true
);
// 2. extract attachments
$this
->
parsedMessage
=
Array
();
// ! reset value
$this
->
parseOutput
(
$decoded_message
,
$this
->
parsedMessage
,
$include_attachments
);
// 3. add "other" attachments (text part, that is not maked as attachment)
if
(
array_key_exists
(
'text'
,
$this
->
parsedMessage
)
&&
count
(
$this
->
parsedMessage
[
'text'
])
>
1
)
{
for
(
$attach
=
1
;
$attach
<
count
(
$this
->
parsedMessage
[
'text'
]);
$attach
++)
{
$this
->
parsedMessage
[
'attachments'
][]
=
Array
(
'data'
=>
$this
->
parsedMessage
[
'text'
][
"$attach"
][
'body'
],
);
}
}
$this
->
headers
=
$this
->
parsedMessage
[
'headers'
];
// ! reset value
if
(
empty
(
$decoded_message
->
ctype_parameters
[
'boundary'
]))
{
// when no boundary, then assume all message is it's text
$this
->
parsedMessage
[
'text'
][
0
][
'body'
]
=
$decoded_message
->
body
;
}
}
/**
* Returns content-id's from inline attachments in message
*
* @return Array
*/
function
getContentIds
()
{
$cids
=
Array
();
if
(
array_key_exists
(
'attachments'
,
$this
->
parsedMessage
)
&&
is_array
(
$this
->
parsedMessage
[
'attachments'
]))
{
foreach
(
$this
->
parsedMessage
[
'attachments'
]
as
$attachnum
=>
$attachment
)
{
if
(!
isset
(
$attachment
[
'headers'
][
'content-id'
]))
{
continue
;
}
$cid
=
$attachment
[
'headers'
][
'content-id'
];
if
(
substr
(
$cid
,
0
,
1
)
==
'<'
&&
substr
(
$cid
,
-
1
)
==
'>'
)
{
$cid
=
substr
(
$cid
,
1
,
-
1
);
}
$cids
[
"$attachnum"
]
=
$cid
;
}
}
return
$cids
;
}
/**
* Get more detailed information about attachments
*
* @param stdClass $decoded parsed headers & body as object
* @param Array $parts parsed parts
* @param bool $include_attachments
*/
function
parseOutput
(&
$decoded
,
&
$parts
,
$include_attachments
=
true
)
{
$ctype
=
strtolower
(
$decoded
->
ctype_primary
.
'/'
.
$decoded
->
ctype_secondary
);
// don't parse attached messages recursevely
if
(!
empty
(
$decoded
->
parts
)
&&
$ctype
!=
'message/rfc822'
)
{
for
(
$i
=
0
;
$i
<
count
(
$decoded
->
parts
);
$i
++)
{
$this
->
parseOutput
(
$decoded
->
parts
[
"$i"
],
$parts
,
$include_attachments
);
}
}
else
/*if (!empty($decoded->disposition) && $decoded->disposition != 'inline' or 1)*/
{
switch
(
$ctype
)
{
case
'text/plain'
:
case
'text/html'
:
if
(!
empty
(
$decoded
->
disposition
)
&&
(
$decoded
->
disposition
==
'attachment'
))
{
$parts
[
'attachments'
][]
=
Array
(
'data'
=>
$include_attachments
?
$decoded
->
body
:
''
,
'filename'
=>
array_key_exists
(
'filename'
,
$decoded
->
d_parameters
)
?
$decoded
->
d_parameters
[
'filename'
]
:
''
,
// from content-disposition
'filename2'
=>
$decoded
->
ctype_parameters
[
'name'
],
// from content-type
'type'
=>
$decoded
->
ctype_primary
,
// "text"
'encoding'
=>
$decoded
->
headers
[
'content-transfer-encoding'
]
);
}
else
{
$body_type
=
$decoded
->
ctype_secondary
==
'plain'
?
'text'
:
'html'
;
$parts
[
$body_type
][]
=
Array
(
'content-type'
=>
$ctype
,
'charset'
=>
array_key_exists
(
'charset'
,
$decoded
->
ctype_parameters
)
?
$decoded
->
ctype_parameters
[
'charset'
]
:
'ISO-8859-1'
,
'body'
=>
$decoded
->
body
);
}
break
;
case
'message/rfc822'
:
// another e-mail as attachment
$parts
[
'attachments'
][]
=
Array
(
'data'
=>
$include_attachments
?
$decoded
->
body
:
''
,
'filename'
=>
array_key_exists
(
'filename'
,
$decoded
->
d_parameters
)
?
$decoded
->
d_parameters
[
'filename'
]
:
''
,
'filename2'
=>
array_key_exists
(
'name'
,
$decoded
->
ctype_parameters
)
?
$decoded
->
ctype_parameters
[
'name'
]
:
$decoded
->
parts
[
0
]->
headers
[
'subject'
],
'type'
=>
$decoded
->
ctype_primary
,
// "message"
'headers'
=>
$decoded
->
headers
// individual copy of headers with each attachment
);
break
;
default
:
if
(!
stristr
(
$decoded
->
headers
[
'content-type'
],
'signature'
))
{
$parts
[
'attachments'
][]
=
Array
(
'data'
=>
$include_attachments
?
$decoded
->
body
:
''
,
'filename'
=>
array_key_exists
(
'filename'
,
$decoded
->
d_parameters
)
?
$decoded
->
d_parameters
[
'filename'
]
:
''
,
// from content-disposition
'filename2'
=>
$decoded
->
ctype_parameters
[
'name'
],
// from content-type
'type'
=>
$decoded
->
ctype_primary
,
'headers'
=>
$decoded
->
headers
// individual copy of headers with each attachment
);
}
}
}
$parts
[
'headers'
]
=
$decoded
->
headers
;
// headers of next parts overwrite previous part headers
}
}
Event Timeline
Log In to Comment