-
-
Save pocki80/9fe56f034f204c78ffb7c377971679b3 to your computer and use it in GitHub Desktop.
Save attachments from imap messages to disk.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* imap-attachment.php | |
* | |
* @author hakre <hakre.wordpress.com> | |
* @link http://stackoverflow.com/questions/9974334/how-to-download-mails-attachment-to-a-specific-folder-using-imap-and-php | |
* | |
* mods by <pocki@pocki.at> to support content-type:attachment | |
*/ | |
/** | |
* Utility Class | |
*/ | |
class IMAP | |
{ | |
/** | |
* | |
* =?x-unknown?B? | |
* =?iso-8859-1?Q? | |
* =?windows-1252?B? | |
* | |
* @param string $stringQP | |
* @param string $base (optional) charset (IANA, lowercase) | |
* @return string UTF-8 | |
*/ | |
public static function decodeToUTF8($stringQP, $base = 'windows-1252') | |
{ | |
$pairs = array( | |
'?x-unknown?' => "?$base?" | |
); | |
$stringQP = strtr($stringQP, $pairs); | |
return imap_utf8($stringQP); | |
} | |
} | |
class IMAPMailbox implements IteratorAggregate, Countable | |
{ | |
private $stream; | |
public function __construct($hostname, $username, $password) | |
{ | |
$stream = imap_open($hostname, $username, $password); | |
if (FALSE === $stream) { | |
throw new Exception('Connect failed: ' . imap_last_error()); | |
} | |
$this->stream = $stream; | |
} | |
public function getStream() | |
{ | |
return $this->stream; | |
} | |
/** | |
* @return stdClass | |
*/ | |
public function check() | |
{ | |
$info = imap_check($this->stream); | |
if (FALSE === $info) { | |
throw new Exception('Check failed: ' . imap_last_error()); | |
} | |
return $info; | |
} | |
/** | |
* @param string $criteria | |
* @param int $options | |
* @param int $charset | |
* @return IMAPMessage[] | |
* @throws Exception | |
*/ | |
public function search($criteria, $options = NULL, $charset = NULL) | |
{ | |
$emails = imap_search($this->stream, $criteria, $options, $charset); | |
if (FALSE === $emails) { | |
throw new Exception('Search failed: ' . imap_last_error()); | |
} | |
foreach ($emails as &$email) { | |
$email = $this->getMessageByNumber($email); | |
} | |
return $emails; | |
} | |
/** | |
* @param int $number | |
* @return IMAPMessage | |
*/ | |
public function getMessageByNumber($number) | |
{ | |
return new IMAPMessage($this, $number); | |
} | |
public function getOverview($sequence = NULL) | |
{ | |
if (NULL === $sequence) { | |
$sequence = sprintf('1:%d', count($this)); | |
} | |
return new IMAPOverview($this, $sequence); | |
} | |
/** | |
* Retrieve an external iterator | |
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php | |
* @return Traversable An instance of an object implementing Iterator or | |
* Traversable | |
*/ | |
public function getIterator() | |
{ | |
return $this->getOverview()->getIterator(); | |
} | |
/** | |
* @return int | |
*/ | |
public function count() | |
{ | |
return $this->check()->Nmsgs; | |
} | |
} | |
class IMAPOverview extends ArrayObject | |
{ | |
private $mailbox; | |
public function __construct(IMAPMailbox $mailbox, $sequence) | |
{ | |
$result = imap_fetch_overview($mailbox->getStream(), $sequence); | |
if (FALSE === $result) { | |
throw new Exception('Overview failed: ' . imap_last_error()); | |
} | |
$this->mailbox = $mailbox; | |
foreach ($result as $overview) | |
{ | |
if (!isset($overview->subject)) { | |
$overview->subject = ''; | |
} else { | |
$overview->subject = IMAP::decodeToUTF8($overview->subject); | |
} | |
} | |
parent::__construct($result); | |
} | |
/** | |
* @return IMAPMailbox | |
*/ | |
public function getMailbox() | |
{ | |
return $this->mailbox; | |
} | |
} | |
class IMAPMessage | |
{ | |
private $mailbox; | |
private $number; | |
private $stream; | |
public function __construct(IMAPMailbox $mailbox, $number) | |
{ | |
$this->mailbox = $mailbox; | |
$this->number = $number; | |
$this->stream = $mailbox->getStream(); | |
} | |
public function getNumber() | |
{ | |
return $this->number; | |
} | |
/** | |
* @param int $number | |
* @return string | |
*/ | |
public function fetchBody($number) | |
{ | |
return imap_fetchbody($this->stream, $this->number, $number); | |
} | |
/** | |
* @return stdClass | |
* @throws Exception | |
*/ | |
public function fetchOverview() | |
{ | |
$result = imap_fetch_overview($this->stream, $this->number); | |
if (FALSE === $result) { | |
throw new Exception('FetchOverview failed: ' . imap_last_error()); | |
} | |
list($result) = $result; | |
foreach ($result as &$prop) { | |
$prop = imap_utf8($prop); | |
} | |
return $result; | |
} | |
public function fetchStructure() | |
{ | |
$structure = imap_fetchstructure($this->stream, $this->number); | |
if (FALSE === $structure) { | |
throw new Exception('FetchStructure failed: ' . imap_last_error()); | |
} | |
return $structure; | |
} | |
public function setSeenFlag() | |
{ | |
if ($this->fetchOverview()->seen) | |
return true; | |
else | |
return imap_setflag_full($this->stream, $this->number, '\\Seen'); | |
} | |
/** | |
* @return IMAPAttachments | |
*/ | |
public function getAttachments() | |
{ | |
return new IMAPAttachments($this); | |
} | |
public function __toString() | |
{ | |
return (string)$this->number; | |
} | |
} | |
class IMAPAttachment | |
{ | |
private $attachment; | |
private $message; | |
public function __construct(IMAPMessage $message, $attachment) | |
{ | |
$this->message = $message; | |
$this->attachment = $attachment; | |
} | |
/** | |
* @return string; | |
*/ | |
public function getBody() | |
{ | |
return $this->message->fetchBody($this->attachment->number); | |
} | |
/** | |
* @return int | |
*/ | |
public function getSize() | |
{ | |
return (int)$this->attachment->bytes; | |
} | |
/** | |
* @return string | |
*/ | |
public function getExtension() | |
{ | |
return pathinfo($this->getFilename(), PATHINFO_EXTENSION); | |
} | |
public function getFilename() | |
{ | |
$filename = $this->attachment->filename; | |
NULL === $filename && $filename = $this->attachment->name; | |
return $filename; | |
} | |
public function __toString() | |
{ | |
$encoding = $this->attachment->encoding; | |
switch ($encoding) { | |
case 0: // 7BIT | |
case 1: // 8BIT | |
case 2: // BINARY | |
return $this->getBody(); | |
case 3: // BASE-64 | |
return base64_decode($this->getBody()); | |
case 4: // QUOTED-PRINTABLE | |
return imap_qprint($this->getBody()); | |
} | |
throw new Exception(sprintf('Encoding failed: Unknown encoding %s (5: OTHER).', $encoding)); | |
} | |
} | |
class IMAPAttachments extends ArrayObject | |
{ | |
private $message; | |
public function __construct(IMAPMessage $message) | |
{ | |
$array = $this->setMessage($message); | |
parent::__construct($array); | |
} | |
private function setMessage(IMAPMessage $message) | |
{ | |
$this->message = $message; | |
return $this->parseStructure($message->fetchStructure()); | |
} | |
private function makePart($part, $index) | |
{ | |
$attachment = new stdClass; | |
$attachment->isAttachment = FALSE; | |
$attachment->number = $index + 1; | |
$attachment->bytes = $part->bytes; | |
$attachment->encoding = $part->encoding; | |
$attachment->filename = NULL; | |
$attachment->name = NULL; | |
$part->ifdparameters | |
&& ($attachment->filename = $this->getAttribute($part->dparameters, 'filename')) | |
&& $attachment->isAttachment = TRUE; | |
$part->ifparameters | |
&& ($attachment->name = $this->getAttribute($part->parameters, 'name')) | |
&& $attachment->isAttachment = TRUE; | |
return $attachment; | |
} | |
private function parseStructure($structure) | |
{ | |
$attachments = array(); | |
if (!isset($structure->parts)) | |
{ // this is a mail with no attachments, but wait: the body might be the file itself | |
if (($structure->ifdisposition) && ($structure->disposition == "attachment")) | |
{ | |
$attachment=$this->makePart($structure, 0); | |
$attachment->isAttachment | |
&& $attachments[] = new IMAPAttachment($this->message, $attachment); | |
} | |
return $attachments; | |
} | |
foreach ($structure->parts as $index => $part) | |
{ | |
if (!$part->ifdisposition) continue; | |
$attachment=$this->makePart($part, $index); | |
$attachment->isAttachment | |
&& $attachments[] = new IMAPAttachment($this->message, $attachment); | |
} | |
return $attachments; | |
} | |
private function getAttribute($params, $name) | |
{ | |
foreach ($params as $object) | |
{ | |
if ($object->attribute == $name) { | |
return IMAP::decodeToUTF8($object->value); | |
} | |
} | |
return NULL; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is how to use it: