Skip to content

Instantly share code, notes, and snippets.

@gaffling
Last active March 4, 2021 12:06
Show Gist options
  • Save gaffling/fb5a78165787449aab35704cf64fcd5f to your computer and use it in GitHub Desktop.
Save gaffling/fb5a78165787449aab35704cf64fcd5f to your computer and use it in GitHub Desktop.
[SAVE MAIL ATTACHMENTS/InlineImages] #php #class #function #attachments
<?php
/* ----------------------------------------------------------------------- */
/* [SAVE MAIL ATTACHMENTS/InlineImages] #php #class #function #attachments */
/* ----------------------------------------------------------------------- */
/**
* INSPIRED BY
* @see http://cantstopgeeking.blogspot.com/2018/12/php-email-message-class-for-extracting.html
* @see https://www.electrictoolbox.com/php-email-message-class-extracting-attachments/
* @see https://www.electrictoolbox.com/php-email-extract-inline-image-attachments/
* https://electrictoolbox.com/php-generate-thumbnails/
* https://gist.github.com/umidjons/11037635
*/
# DEBUG MODE
define('DEBUG', FALSE);
# SERVER CONNECTION STRING FOR GOOGLE MAIL
$SERVER = '{imap.gmail.com:993/ssl/novalidate-cert}Inbox';
# LOGIN (EMAIL-ADDRESS & PASSWORD)
$LOGIN = '';
$PASSWORD = '';
# SHOULD INLINE IMAGES ALSO BE SAVED
$SAVE_INLINE_IMAGES = false;
# HOW MANY MAILS SHOULD BE SCANNED (only the newest)
$LIMIT = 10;
// Build Folder
if (!file_exists("email_files/debug/") and DEBUG == true) {
$oldmask = umask(0);
mkdir("email_files/debug/", 0777, true);
umask($oldmask);
}
if (!file_exists("email_files/Inlineimages/") and $SAVE_INLINE_IMAGES == true) {
$oldmask = umask(0);
mkdir("email_files/Inlineimages/", 0777, true);
umask($oldmask);
}
if (!file_exists("email_files/attachments/")) {
$oldmask = umask(0);
mkdir("email_files/attachments/", 0777, true);
umask($oldmask);
}
// Connect to the mail server
$connection = imap_open($SERVER, $LOGIN, $PASSWORD);
// Get the number of messages in the mailbox
$MC = imap_check($connection);
// Get all headers from inbox
$result = imap_fetch_overview($connection, "1:{$MC->Nmsgs}", 0);
// Sort them from newest to oldest
usort($result, function($a, $b) { return($b->udate-$a->udate); });
// Set helper var counter to 1
$counter = 1;
// Loop each header
foreach ($result as $overview) {
// Stop if limit is reached
++$counter;
if ($counter > $LIMIT) break;
if (DEBUG == TRUE) echo $overview->date.' - '.$overview->from.'<br>'; // DEV ONLY
// Get UID
$emailUID = imap_uid($connection, $overview->msgno);
// Get Message
$emailMessage = new EmailMessage($connection, $emailUID);
// Set to true to get the message parts (or don't set to false, the default is true)
// $emailMessage->getAttachments = false;
$message = $emailMessage->fetch();
if ($SAVE_INLINE_IMAGES==TRUE) {
// Match inline images
preg_match_all('/src="cid:(.*)(["|@])/Uims', $emailMessage->bodyHTML, $matches);
// If there are any matches, loop through them and save to filesystem,
// change the src property of the image to an actual URL it can be viewed at
if(count($matches[1])) {
// Search and replace arrays will be used in str_replace function below
$search = array();
$replace = array();
foreach($matches[1] as $match) {
if (isset($emailMessage->attachments[$match]['filename'])) {
// Work out some unique filename for it and save to filesystem etc
$uniqueFilename = $emailMessage->attachments[$match]['filename'];
file_put_contents("email_files/Inlineimages/".$uniqueFilename, $emailMessage->attachments[$match]['data']);
$search[] = "src=\"cid:$match\"";
// change www.example.com etc to actual URL
$replace[] = "src=\"email_files/Inlineimages/$uniqueFilename\"";
}
}
// now do the replacements
$emailMessage->bodyHTML = str_replace($search, $replace, $emailMessage->bodyHTML);
$message = $emailMessage->bodyHTML;
}
}
if (DEBUG == TRUE) $emailMessage->saveAndShowAttachments(); // DEV ONLY
else $emailMessage->saveAttachments();
}
imap_close($connection);
class EmailMessage {
protected $connection;
protected $messageNumber;
protected $dates;
protected $emails;
protected $message;
public $bodyHTML = '';
public $bodyPlain = '';
public $attachments;
public $getAttachments = true;
public function __construct($connection, $messageNumber) {
$this->connection = $connection;
$this->messageNumber = imap_msgno($this->connection,$messageNumber);
}
public function fetch() {
$structure = imap_fetchstructure($this->connection, $this->messageNumber);
if (DEBUG == TRUE) file_put_contents("email_files/debug/structure.txt", "Structure \n" . date("Y-m-d h:i:sa") . "\n" . print_r($structure, true) . PHP_EOL); // DEV ONLY
if (!$structure) {
return false;
} else {
if (isset($structure->parts)) {
$this->message= $this->recurse($structure->parts);
} else{
$part=$structure;
$structure->parts = Array($part);
$this->message= $this->recurse($structure->parts);
}
return $this->message;
}
}
public function recurse($messageParts, $prefix = '', $index = 1, $fullPrefix = true) {
foreach ($messageParts as $part) {
$partNumber = $prefix . $index;
if ($part->type == 0) {
if (isset($part->ifdisposition)){
if ($part->ifdisposition == 1 && $part->disposition == 'ATTACHMENT') {
$this->attachments[] = array(
'type' => $part->type,
'subtype' => $part->subtype,
'filename' => $this->getFilenameFromPart($part),
'data' => $this->getAttachments ? $this->getPart($partNumber, $part->encoding) : '',
'inline' => false,
);
} else {
if ($part->subtype == 'PLAIN') {
$this->bodyPlain .= nl2br($this->getPart($partNumber, $part->encoding));
$this->message = $this->bodyPlain;
#echo $this->message;
} else {
$this->bodyHTML .= $this->getPart($partNumber, $part->encoding);
$this->message = $this->bodyHTML;
}
}
} else {
if ($part->subtype == 'PLAIN') {
$this->bodyPlain .= $this->getPart($partNumber, $part->encoding);
$this->message = $this->bodyPlain;
} else {
$this->bodyHTML .= $this->getPart($partNumber, $part->encoding);
$this->message = $this->bodyHTML;
}
}
} elseif ($part->type == 2) {
$msg = new EmailMessage($this->connection, $this->messageNumber);
$msg->getAttachments = $this->getAttachments;
$msg->recurse($part->parts, $partNumber.'.', 0, false);
$this->attachments[] = array(
'type' => $part->type,
'subtype' => $part->subtype,
'filename' => '',
'data' => $msg,
'inline' => false,
);
} elseif(isset($part->parts)) {
if ($fullPrefix) {
$this->recurse($part->parts, $prefix.$index.'.');
} else {
$this->recurse($part->parts, $prefix);
}
} elseif($part->type > 2) {
if (isset($part->disposition)) {
if ($part->disposition == 'INLINE' && $part->type == 5) {
$id = str_replace(array('<', '>'), '', $part->id);
$this->attachments[$id] = array(
'type' => $part->type,
'subtype' => $part->subtype,
'filename' => $this->getFilenameFromPart($part),
'data' => $this->getAttachments ? $this->getPart($partNumber, $part->encoding) : '',
'inline' => true,
);
} else {
$this->attachments[] = array(
'type' => $part->type,
'subtype' => $part->subtype,
'filename' => $this->getFilenameFromPart($part),
'data' => $this->getAttachments ? $this->getPart($partNumber, $part->encoding) : '',
'inline' => false,
);
}
} else{
$id = str_replace(array('<', '>'), '', $part->id);
$this->attachments[$id] = array(
'type' => $part->type,
'subtype' => $part->subtype,
'filename' => $this->getFilenameFromPart($part),
'data' => $this->getAttachments ? $this->getPart($partNumber, $part->encoding) : '',
'inline' => true,
);
}
}
$index++;
}
return $this->message;
}
function getPart($partNumber, $encoding) {
$data = imap_fetchbody($this->connection, $this->messageNumber, $partNumber);
switch($encoding) {
case 0: return $data; # 7BIT
case 1: return $data; # 8BIT
case 2: return $data; # BINARY
case 3: return base64_decode($data); # BASE64
case 4: return quoted_printable_decode($data); # QUOTED_PRINTABLE
case 5: return $data; # OTHER
}
}
function getFilenameFromPart($part) {
$filename = '';
if ($part->ifdparameters) {
foreach ($part->dparameters as $object) {
if (strtolower($object->attribute) == 'filename') {
$filename = $object->value;
}
}
}
if (!$filename && $part->ifparameters) {
foreach ($part->parameters as $object) {
if (strtolower($object->attribute) == 'name') {
$filename = $object->value;
}
}
}
return $filename;
}
public function saveAndShowAttachments(){
if (isset($this->attachments)) {
foreach ($this->attachments as $attachment) {
if ($attachment['inline'] != true) {
file_put_contents('email_files/attachments/' . $attachment['filename'], $attachment['data']);
echo '<embed style="width:200px;" src="email_files/attachments/' . $attachment['filename'] .'"/><br>';
}
}
}
}
public function saveAttachments() {
if (isset($this->attachments)) {
foreach ($this->attachments as $attachment) {
if ($attachment['inline'] == false) {
file_put_contents('email_files/attachments/' . $attachment['filename'], $attachment['data']);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment