diff --git a/composer.json b/composer.json index ed63ece..22892d7 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "eden/mail", + "name": "leonardoweslei/eden-mail", "type": "library", "description": "Eden POP3, IMAP and SMTP component", "keywords": ["eden", "library"], diff --git a/src/Imap.php b/src/Imap.php index b652865..53813c6 100644 --- a/src/Imap.php +++ b/src/Imap.php @@ -29,72 +29,72 @@ class Imap extends Base * @const string NO_SUBJECT Default subject */ const NO_SUBJECT = '(no subject)'; - + /** * @var string $host The IMAP Host */ protected $host = null; - + /** * @var string|null $port The IMAP port */ protected $port = null; - + /** * @var bool $ssl Whether to use SSL */ protected $ssl = false; - + /** * @var bool $tls Whether to use TLS */ protected $tls = false; - + /** * @var string|null $username The mailbox user name */ protected $username = null; - + /** * @var string|null $password The mailbox password */ protected $password = null; - + /** * @var int $tag The tag number */ protected $tag = 0; - + /** * @var int $total The total main in mailbox */ protected $total = 0; - + /** * @var int $next for pagination */ protected $next = 0; - + /** * @var string|null $buffer Mail body */ protected $buffer = null; - + /** * @var [RESOURCE] $socket The socket connection */ protected $socket = null; - + /** * @var string|null $mailbox The mailbox name */ protected $mailbox = null; - + /** * @var array $mailboxes The list of mailboxes */ protected $mailboxes = array(); - + /** * @var bool $debugging If true outputs the logs */ @@ -371,7 +371,18 @@ public function getMailboxes() continue; } - $mailboxes[] = $line[count($line)-2]; + $mailbox = trim($line[count($line) - 2]); + + if ($mailbox == "/" || $mailbox == "") { + $mailbox = $line[count($line) - 1]; + } + + //Fix mailbox name encoded with utf7 + $mailbox = ImapUtf7::decode(trim($mailbox)); + //Decoding utf8 string result + $mailbox = utf8_decode($mailbox); + + $mailboxes[] = $mailbox; } return $mailboxes; @@ -462,7 +473,7 @@ public function remove($uid) return $this; } - + /** * Remove an email from a mailbox * @@ -470,9 +481,8 @@ public function remove($uid) */ public function expunge() { - $this->call('expunge'); - + return $this; } @@ -673,7 +683,16 @@ public function setActiveMailbox($mailbox) if (strpos($line, 'EXISTS') !== false) { list($star, $this->total, $type) = explode(' ', $line, 3); } else if (strpos($line, 'UIDNEXT') !== false) { - list($star, $ok, $next, $this->next, $type) = explode(' ', $line, 5); + $explode = explode(' ', $line, 5); + // the list function expects 5 items + if (count($explode) < 5) { + array_push($explode, "The next unique identifier value"); + list($star, $ok, $next, $this->next, $type) = $explode; + } + else { + list($star, $ok, $next, $this->next, $type) = explode(' ', $line, + 5); + } $this->next = substr($this->next, 0, -1); } @@ -740,7 +759,11 @@ protected function receive($sentTag) $start = time(); while (time() < ($start + self::TIMEOUT)) { - list($receivedTag, $line) = explode(' ', $this->getLine(), 2); + $explode = explode(' ', $this->getLine(), 2); + if (count($explode) < 2) { + array_push($explode, "OK []\r\n"); + } + list($receivedTag, $line) = $explode; $this->buffer[] = trim($receivedTag . ' ' . $line); if ($receivedTag == 'TAG'.$sentTag) { return $this->buffer; @@ -890,7 +913,12 @@ private function getEmailFormat($email, $uniqueId = null, array $flags = array() } } - $sender['email'] = $headers1->from[0]->mailbox . '@' . $headers1->from[0]->host; + if (isset($headers1->from[0]) && is_object($headers1->from[0])) { + $sender['email'] = $headers1->from[0]->mailbox . '@' . $headers1->from[0]->host; + } + else { + $sender['email'] = 'unknown@localhost.localdomain'; + } //set the to if (isset($headers1->to)) { @@ -1059,6 +1087,16 @@ private function getEmailResponse($command, $parameters = array(), $first = fals //if the line starts with a fetch //it means it's the end of getting an email if (strpos($line, 'FETCH') !== false && strpos($line, 'TAG'.$this->tag) === false) { + $flags = []; + /** + * simple regex to match the FETCH line + * used to skip over a $line that hasn't got to do with the FETCH or TAG line + */ + preg_match('/^\* [0-9]+/', $line, $arr); + // no match, there isn't sufficient information to parse the email. Skipping. + // potential exception: undefined $flags + if (empty($arr)) continue; + //if there is email data if (!empty($email)) { //create the email format and add it to emails @@ -1359,7 +1397,12 @@ function imap_rfc822_parse_headers($header) $headers->to = $headers->cc = $headers->bcc = array(); preg_match('#Message\-(ID|id|Id)\:([^\n]*)#', $header, $ID); - $headers->ID = trim($ID[2]); + if (isset($ID[2])){ + $headers->ID = trim($ID[2]); + } + else { + $headers->ID = 'Message-ID: '; + } unset($ID); preg_match('#\nTo\:([^\n]*)#', $header, $to); @@ -1372,7 +1415,12 @@ function imap_rfc822_parse_headers($header) $headers->from = array(new \stdClass()); preg_match('#\nFrom\:([^\n]*)#', $header, $from); - $headers->from[0] = imap_rfc822_parse_headers_decode(trim($from[1])); + if (isset($from[1])) { + $headers->from[0] = imap_rfc822_parse_headers_decode(trim($from[1])); + } + else { + $headers->from[0] = 'unknown@localhost.localdomain'; + } preg_match('#\nCc\:([^\n]*)#', $header, $cc); if (isset($cc[1])) { @@ -1391,15 +1439,23 @@ function imap_rfc822_parse_headers($header) } preg_match('#\nSubject\:([^\n]*)#', $header, $subject); - $headers->subject = trim($subject[1]); - unset($subject); + if (isset($subject[1])) { + $headers->subject = trim($subject[1]); + unset($subject); + } preg_match('#\nDate\:([^\n]*)#', $header, $date); - $date = substr(trim($date[0]), 6); + if (isset($date[0])) { + $date = substr(trim($date[0]), 6); + - $date = preg_replace('/\(.*\)/', '', $date); + $date = preg_replace('/\(.*\)/', '', $date); - $headers->date = trim($date); + $headers->date = trim($date); + } + else { + $headers->date = date('d-m-Y H:i:s'); + } unset($date); foreach ($ccs as $k => $cc) { diff --git a/src/ImapUtf7.php b/src/ImapUtf7.php new file mode 100644 index 0000000..002530c --- /dev/null +++ b/src/ImapUtf7.php @@ -0,0 +1,209 @@ += 6; $al -= 6) { + $res .= self::$imap_base64[($a >> ($al - 6)) & 0x3F]; + } + } + + if ($al > 0) { + $res .= self::$imap_base64[($a << (6 - $al)) & 0x3F]; + } + + return $res; + } + + private static function encodeUtf8Char($w) + { + if ($w & 0x80000000) { + return ''; + } + + if ($w & 0xFC000000) { + $n = 5; + } elseif ($w & 0xFFE00000) { + $n = 4; + } elseif ($w & 0xFFFF0000) { + $n = 3; + } elseif ($w & 0xFFFFF800) { + $n = 2; + } elseif ($w & 0xFFFFFF80) { + $n = 1; + } else { + return chr($w); + } + + $res = chr(((255 << (7 - $n)) | ($w >> ($n * 6))) & 255); + + while (--$n >= 0) { + $res .= chr((($w >> ($n * 6)) & 0x3F) | 0x80); + } + + return $res; + } + + private static function decodeB64Imap($s) + { + $a = 0; + $al = 0; + $res = ''; + $n = strlen($s); + + for ($i = 0; $i < $n; $i++) { + $k = strpos(self::$imap_base64, $s[$i]); + if ($k === false) { + continue; + } + $a = ($a << 6) | $k; + $al += 6; + + if ($al >= 8) { + $res .= chr(($a >> ($al - 8)) & 255); + $al -= 8; + } + } + + $r2 = ''; + $n = strlen($res); + + for ($i = 0; $i < $n; $i++) { + $c = ord($res[$i]); + $i++; + + if ($i < $n) { + $c = ($c << 8) | ord($res[$i]); + } + + $r2 .= self::encodeUtf8Char($c); + } + + return $r2; + } + + public static function encode($s) + { + $n = strlen($s); + $err = 0; + $buf = ''; + $res = ''; + + for ($i = 0; $i < $n;) { + $x = ord($s[$i++]); + + if (($x & 0x80) == 0x00) { + $r = $x; + $w = 0; + } elseif (($x & 0xE0) == 0xC0) { + $w = 1; + $r = $x & 0x1F; + } elseif (($x & 0xF0) == 0xE0) { + $w = 2; + $r = $x & 0x0F; + } elseif (($x & 0xF8) == 0xF0) { + $w = 3; + $r = $x & 0x07; + } elseif (($x & 0xFC) == 0xF8) { + $w = 4; + $r = $x & 0x03; + } elseif (($x & 0xFE) == 0xFC) { + $w = 5; + $r = $x & 0x01; + } elseif (($x & 0xC0) == 0x80) { + $w = 0; + $r = -1; + $err++; + } else { + $w = 0; + $r = -2; + $err++; + } + + for ($k = 0; $k < $w && $i < $n; $k++) { + $x = ord($s[$i++]); + if ($x & 0xE0 != 0x80) { + $err++; + } + $r = ($r << 6) | ($x & 0x3F); + } + + if ($r < 0x20 || $r > 0x7E) { + $buf .= chr(($r >> 8) & 0xFF); + $buf .= chr($r & 0xFF); + } else { + if (strlen($buf)) { + $res .= '&' . self::encodeB64Imap($buf) . '-'; + $buf = ''; + } + if ($r == 0x26) { + $res .= '&-'; + } else { + $res .= chr($r); + } + } + } + + if (strlen($buf)) { + $res .= '&' . self::encodeB64Imap($buf) . '-'; + } + + return $res; + } + + public static function decode($s) + { + $res = ''; + $n = strlen($s); + $h = 0; + + while ($h < $n) { + $t = strpos($s, '&', $h); + + if ($t === false) { + $t = $n; + } + + $res .= substr($s, $h, $t - $h); + $h = $t + 1; + + if ($h >= $n) { + break; + } + + $t = strpos($s, '-', $h); + + if ($t === false) { + $t = $n; + } + + $k = $t - $h; + + if ($k == 0) { + $res .= '&'; + } else { + $res .= self::decodeB64Imap(substr($s, $h, $k)); + } + + $h = $t + 1; + } + + return $res; + } +}