substr() uses the string length, not the position as third argument. The corrected version of the following code line:
<?php
$parts[$s] = substr($file_txt, $starting_pos_body, $ending_pos_body-$starting_pos_body);
?>
(PECL mailparse >= 0.9.0)
mailparse_msg_extract_part — Extrait et décode une section de message
Cette fonction est actuellement non documentée ; seule la liste des arguments est disponible.
mimemail
Une ressource MIME
valide.
msgbody
callbackfunc
Aucune valeur n'est retournée.
substr() uses the string length, not the position as third argument. The corrected version of the following code line:
<?php
$parts[$s] = substr($file_txt, $starting_pos_body, $ending_pos_body-$starting_pos_body);
?>
Here a full example to save ZIP files attachments in their original name.
<?php
$email_raw = '(raw e-mail contents buffer)';
$parser = mailparse_msg_create(); // MUST be destroyed at the end of the script
mailparse_msg_parse($parser, $email_raw);
$structure = mailparse_msg_get_structure($parser); // Ex. ["1", "1.1", "1.2"]
foreach ($structure as $part_label) { // Search among each e-mail part
$part = mailparse_msg_get_part($parser, $part_label); // Parse a specified part
$part_data = mailparse_msg_get_part_data($part); // Get parsed part data, header and meta values
if ($part_data['content-type'] ?? null === 'application/zip') {
$name = $part_data['disposition-filename'] ?? $part_data['content-name'] ?? 'unknow.zip';
$contents = mailparse_msg_extract_part($part, $email_raw, null); // null for returning content
file_put_contents($name, $contents);
}
}
mailparse_msg_free($parser); // Important
The callback argument does not support closures... :( It will complains with "PHP Catchable fatal error: Object of class Closure could not be converted to string".
With ref to previous comment re: callback:
If you explicitly specify NULL as the callback parameter, the complete section is extracted, decoded and returned, without the need for a callback.
In mailparse version 2.1.1 (and perhaps earlier), when using mailparse_msg_extract_part() with a callback function, it breaks the data it passes to it into 4kB chunks and calls the callback function for each chunk. So, for example, if it's extracting a 41kB MIME part, the callback function you define will be called 11 times, each time with the next chunk of data. Here's some quick-and-dirty code that shows one way to handle this:
<?php
$message = file_get_contents ("email.txt"); // Pull in the e-mail.
function catch_part ($part)
{
$GLOBALS["part_data"] .= $part; // Append the data onto any previously extracted data.
}
mailparse_msg_extract_part ("1.1", $message, "catch_part"); // Extract MIME part 1.1
echo $GLOBALS["part_data"]; // Print out the extracted part.
?>
There's probably a much better way of dealing with this, but hey. It's what I got.
Unless I've missed something obvious:
get_structure returns array(1,1.1,1.1.2) etc but its not easy to get the contents of each part as mailparse_msg_extract_part() and mailparse_msg_extract_part_file() just return the lot. However get_part_data will return the string offsets so you know where to chop the message so you can get the contents of the parts.
Only issue is get_part_data returns:
[starting-pos] => 0
[starting-pos-body] => 1412
[ending-pos] => 14989
[ending-pos-body] => 14989
Unless I'm missed something else, theres a bug here as ending-pos is the same as ending-pos-body so it won't chop the contents cleanly, leaving the:
------=_NextPart_000_0069_01C659A6.9072E590--
...as supposedly part of the section contents.
$file = "..../mail"; // path of your mail
$file_txt = implode("",file($file));
$parse = mailparse_msg_parse_file($file);
$structure = mailparse_msg_get_structure($parse);
// chop message parts into array
$parts = array();
foreach ($structure as $s){
print "Part $s\n";
print "--------------------------------------\n";
$part = mailparse_msg_get_part($parse, $s);
$part_data = mailparse_msg_get_part_data($part);
print_r($part_data);
$starting_pos_body = $part_data['starting-pos-body'];
$ending_pos_body = $part_data['ending-pos-body'];
$parts[$s] = substr($file_txt,$starting_pos_body,$ending_pos_body); // copy data into array
print "[".$parts[$s]."]";
print "\n------------------------------------\n";
}