From 448e3911af58983782f3dcef70b7b5b105a47454 Mon Sep 17 00:00:00 2001 From: Rogov Viktor Date: Fri, 19 Dec 2014 09:17:45 +0600 Subject: [PATCH 1/6] Lay out sources as composer package with PSR-4 autoloader --- .gitignore | 2 + composer.json | 13 ++ composer.lock | 430 +++++++++++++++++++++++++++++++++++++++ phpunit.xml | 20 ++ smtp.php => src/Smtp.php | 5 +- test/AutoloadTest.php | 15 ++ 6 files changed, 484 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 phpunit.xml rename smtp.php => src/Smtp.php (99%) create mode 100644 test/AutoloadTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..45832c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea/ +/vendor/ \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..30cbb01 --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "name": "hujuice/smtp", + "version": "1.1", + "autoload": { + "psr-4": { + "Hujuice\\Smtp\\": "src/" + } + }, + "require": {}, + "require-dev": { + "phpunit/phpunit": "3.7.*" + } +} \ No newline at end of file diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3d27f27 --- /dev/null +++ b/composer.lock @@ -0,0 +1,430 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "d19d86fd2d5932d5527bbc7fd37f9a64", + "packages": [], + "packages-dev": [ + { + "name": "phpunit/php-code-coverage", + "version": "1.2.18", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", + "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-text-template": ">=1.2.0@stable", + "phpunit/php-token-stream": ">=1.1.3,<1.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*@dev" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2014-09-02 10:13:14" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2014-01-30 17:20:04" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2014-03-03 05:10:30" + }, + { + "name": "phpunit/phpunit", + "version": "3.7.38", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6", + "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~1.2", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.1", + "phpunit/php-timer": "~1.0", + "phpunit/phpunit-mock-objects": "~1.2", + "symfony/yaml": "~2.0" + }, + "require-dev": { + "pear-pear.php.net/pear": "1.9.4" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2014-10-17 09:04:17" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2013-01-13 10:24:48" + }, + { + "name": "symfony/yaml", + "version": "v2.6.1", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20", + "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-12-02 20:19:20" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "platform": [], + "platform-dev": [] +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e2ec3a4 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ + + + + + test + + + + .:./test + + \ No newline at end of file diff --git a/smtp.php b/src/Smtp.php similarity index 99% rename from smtp.php rename to src/Smtp.php index 33f6847..e555761 100644 --- a/smtp.php +++ b/src/Smtp.php @@ -24,13 +24,16 @@ * @version 1.1 */ +namespace Hujuice\Smtp; +use Exception; + /** * Rich SMTP client * * @package SMTP * @link http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol Documentation */ -class smtp +class Smtp { /** * New line character diff --git a/test/AutoloadTest.php b/test/AutoloadTest.php new file mode 100644 index 0000000..2db1194 --- /dev/null +++ b/test/AutoloadTest.php @@ -0,0 +1,15 @@ +assertInstanceOf(Smtp::class, $smtp); + } +} From c0473b478a1d23209440085e60517b92b826c694 Mon Sep 17 00:00:00 2001 From: Rogov Viktor Date: Sat, 20 Dec 2014 10:12:35 +0600 Subject: [PATCH 2/6] Ability to send raw body formatted by external code --- src/Smtp.php | 249 +++++++++++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 118 deletions(-) diff --git a/src/Smtp.php b/src/Smtp.php index e555761..cf28c87 100644 --- a/src/Smtp.php +++ b/src/Smtp.php @@ -210,6 +210,8 @@ class Smtp 'charset' => 'UTF-8' ); + protected $_body = null; + /** * File attachments * @@ -546,14 +548,19 @@ public function text($text = null, $content_type = 'text/plain', $charset = 'utf if (null !== $text) { $this->_text = array( - 'body' => str_replace("\n", self::NL, (string) $text), - 'Content-Type' => $content_type, - 'charset' => $charset - ); + 'body' => str_replace("\n", self::NL, (string) $text), + 'Content-Type' => $content_type, + 'charset' => $charset + ); } return $this->_text; } + public function body($body) + { + $this->_body = $body; + } + /** * Attachment from file * @@ -570,10 +577,10 @@ public function attachment($path = null, $name = '', $content_type = 'applicatio if (is_readable($path)) { $attachment = array( - 'path' => (string) $path, - 'Content-Type' => (string) $content_type, - 'charset' => (string) $charset - ); + 'path' => (string) $path, + 'Content-Type' => (string) $content_type, + 'charset' => (string) $charset + ); $name || ($name = pathinfo($path, PATHINFO_BASENAME)); @@ -602,10 +609,10 @@ public function raw($content = null, $name = '', $content_type = 'text/plain', $ if ($content) { $attachment = array( - 'content' => (string) $content, - 'Content-Type' => (string) $content_type, - 'charset' => (string) $charset - ); + 'content' => (string) $content, + 'Content-Type' => (string) $content_type, + 'charset' => (string) $charset + ); if (empty($name)) $name = time() . '-' . mt_rand(); @@ -644,11 +651,13 @@ public function send() if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) throw new Exception('No recipients'); - if (empty($this->_subject)) // Net Ecology - throw new Exception('No subject'); + if (empty($this->_body)) { + if (empty($this->_subject)) // Net Ecology + throw new Exception('No subject'); - if (empty($this->_text)) - throw new Exception('No message text'); + if (empty($this->_text)) + throw new Exception('No message text'); + } // Connection $this->_connect(); @@ -671,128 +680,132 @@ public function send() // Data $this->_dialog('DATA', self::DATAOK); - // Message - $message = ''; - - // From - if (empty($this->_from['name'])) - $message .= 'From: <' . $this->_from['address'] . '>' . self::NL; - else - $message .= 'From: "' . $this->_encode($this->_from['name']) . '"<' . $this->_from['address'] . '>' . self::NL; + if ($this->_body) { + $message = $this->_body . self::NL; + } else { + // Message + $message = ''; - // Reply to - if (!empty($this->_replyTo)) - { - if (empty($this->_replyTo['name'])) - $message .= 'Reply-To: <' . $this->_replyTo['address'] . '>' . self::NL; + // From + if (empty($this->_from['name'])) + $message .= 'From: <' . $this->_from['address'] . '>' . self::NL; else - $message .= 'Reply-To: "' . $this->_encode($this->_replyTo['name']) . '"<' . $this->_replyTo['address'] . '>' . self::NL; - } + $message .= 'From: "' . $this->_encode($this->_from['name']) . '"<' . $this->_from['address'] . '>' . self::NL; - // To - foreach ($this->_to as $name => $rcpt) - { - if (is_integer($name)) - $message .= 'To: <' . $rcpt . '>' . self::NL; - else - $message .= 'To: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL; - } + // Reply to + if (!empty($this->_replyTo)) + { + if (empty($this->_replyTo['name'])) + $message .= 'Reply-To: <' . $this->_replyTo['address'] . '>' . self::NL; + else + $message .= 'Reply-To: "' . $this->_encode($this->_replyTo['name']) . '"<' . $this->_replyTo['address'] . '>' . self::NL; + } - // Cc - foreach ($this->_cc as $name => $rcpt) - { - if (is_integer($name)) - $message .= 'Cc: <' . $rcpt . '>' . self::NL; - else - $message .= 'Cc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL; - } + // To + foreach ($this->_to as $name => $rcpt) + { + if (is_integer($name)) + $message .= 'To: <' . $rcpt . '>' . self::NL; + else + $message .= 'To: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL; + } - // Bcc - foreach ($this->_bcc as $name => $rcpt) - { - if (is_integer($name)) - $message .= 'Bcc: <' . $rcpt . '>' . self::NL; - else - $message .= 'Bcc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL; - } + // Cc + foreach ($this->_cc as $name => $rcpt) + { + if (is_integer($name)) + $message .= 'Cc: <' . $rcpt . '>' . self::NL; + else + $message .= 'Cc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL; + } - // Priority - if ($this->_priority) - $message .= 'X-Priority: ' . $this->_priority . self::NL; + // Bcc + foreach ($this->_bcc as $name => $rcpt) + { + if (is_integer($name)) + $message .= 'Bcc: <' . $rcpt . '>' . self::NL; + else + $message .= 'Bcc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL; + } - // Mailer - $message .= 'X-mailer: ' . self::MAILER . self::NL; - $message .= 'X-mailer-author: ' . self::MAILER_AUTHOR . self::NL; + // Priority + if ($this->_priority) + $message .= 'X-Priority: ' . $this->_priority . self::NL; - // Custom headers - foreach ($this->_headers as $name => $value) - $message .= $name . ': ' . $value. self::NL; + // Mailer + $message .= 'X-mailer: ' . self::MAILER . self::NL; + $message .= 'X-mailer-author: ' . self::MAILER_AUTHOR . self::NL; - // Date - $message .= 'Date: ' . date('r') . self::NL; + // Custom headers + foreach ($this->_headers as $name => $value) + $message .= $name . ': ' . $value. self::NL; - // Subject - $message .= 'Subject: ' . $this->_encode($this->_subject) . self::NL; + // Date + $message .= 'Date: ' . date('r') . self::NL; - // Message - /* - The message will containt text and attachments. - This implementation consider the multipart/mixed method only. - http://en.wikipedia.org/wiki/MIME#Multipart_messages - */ - if ($this->_attachments || $this->_raw) - { - $separator = hash('sha256', time()); - $message .= 'MIME-Version: 1.0' . self::NL; - $message .= 'Content-Type: multipart/mixed; boundary=' . $separator . self::NL; - $message .= self::NL; - $message .= 'This is a message with multiple parts in MIME format.' . self::NL; - $message .= '--' . $separator . self::NL; - $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL; - $message .= self::NL; - $message .= $this->_text['body'] . self::NL; - foreach ($this->_attachments as $name => $attach) + // Subject + $message .= 'Subject: ' . $this->_encode($this->_subject) . self::NL; + + // Message + /* + The message will containt text and attachments. + This implementation consider the multipart/mixed method only. + http://en.wikipedia.org/wiki/MIME#Multipart_messages + */ + if ($this->_attachments || $this->_raw) { + $separator = hash('sha256', time()); + $message .= 'MIME-Version: 1.0' . self::NL; + $message .= 'Content-Type: multipart/mixed; boundary=' . $separator . self::NL; + $message .= self::NL; + $message .= 'This is a message with multiple parts in MIME format.' . self::NL; $message .= '--' . $separator . self::NL; - $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r', filemtime($attach['path'])) . '"' . self::NL; - if (substr($attach['Content-Type'], 0, 5) == 'text/') + $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL; + $message .= self::NL; + $message .= $this->_text['body'] . self::NL; + foreach ($this->_attachments as $name => $attach) { - $message .= 'Content-Type: ' . $attach['Content-Type'] . '; charset=' . $attach['charset'] . self::NL; - $message .= self::NL; - $message .= file_get_contents($attach['path']) . self::NL; + $message .= '--' . $separator . self::NL; + $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r', filemtime($attach['path'])) . '"' . self::NL; + if (substr($attach['Content-Type'], 0, 5) == 'text/') + { + $message .= 'Content-Type: ' . $attach['Content-Type'] . '; charset=' . $attach['charset'] . self::NL; + $message .= self::NL; + $message .= file_get_contents($attach['path']) . self::NL; + } + else + { + $message .= 'Content-Type: ' . $attach['Content-Type'] . self::NL; + $message .= 'Content-Transfer-Encoding: base64' . self::NL; + $message .= self::NL; + $message .= base64_encode(file_get_contents($attach['path'])) . self::NL; + } } - else + foreach ($this->_raw as $name => $raw) { - $message .= 'Content-Type: ' . $attach['Content-Type'] . self::NL; - $message .= 'Content-Transfer-Encoding: base64' . self::NL; - $message .= self::NL; - $message .= base64_encode(file_get_contents($attach['path'])) . self::NL; + $message .= '--' . $separator . self::NL; + $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r') . '"' . self::NL; + if (substr($raw['Content-Type'], 0, 5) == 'text/') + { + $message .= 'Content-Type: ' . $raw['Content-Type'] . '; charset=' . $raw['charset'] . self::NL; + $message .= self::NL; + $message .= $raw['content'] . self::NL; + } + else + { + $message .= 'Content-Type: ' . $raw['Content-Type'] . self::NL; + $message .= 'Content-Transfer-Encoding: base64' . self::NL; + $message .= self::NL; + $message .= base64_encode($raw['content']) . self::NL; + } } + $message .= '--' . $separator . '--' . self::NL; } - foreach ($this->_raw as $name => $raw) + else { - $message .= '--' . $separator . self::NL; - $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r') . '"' . self::NL; - if (substr($raw['Content-Type'], 0, 5) == 'text/') - { - $message .= 'Content-Type: ' . $raw['Content-Type'] . '; charset=' . $raw['charset'] . self::NL; - $message .= self::NL; - $message .= $raw['content'] . self::NL; - } - else - { - $message .= 'Content-Type: ' . $raw['Content-Type'] . self::NL; - $message .= 'Content-Transfer-Encoding: base64' . self::NL; - $message .= self::NL; - $message .= base64_encode($raw['content']) . self::NL; - } + $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL; + $message .= self::NL . $this->_text['body'] . self::NL; } - $message .= '--' . $separator . '--' . self::NL; - } - else - { - $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL; - $message .= self::NL . $this->_text['body'] . self::NL; } $message .= '.'; // The _dialog function below will add self::NL; From 301ffd556fab28d4783242bfb964cf263ce354a9 Mon Sep 17 00:00:00 2001 From: Rogov Viktor Date: Sat, 20 Dec 2014 10:17:05 +0600 Subject: [PATCH 3/6] Version bump --- composer.json | 2 +- src/Smtp.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 30cbb01..69202a3 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "hujuice/smtp", - "version": "1.1", + "version": "1.3", "autoload": { "psr-4": { "Hujuice\\Smtp\\": "src/" diff --git a/src/Smtp.php b/src/Smtp.php index cf28c87..7025c2c 100644 --- a/src/Smtp.php +++ b/src/Smtp.php @@ -21,7 +21,7 @@ * @author Sergio Vaccaro * @copyright Copyright (c) Sergio Vaccaro * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 - * @version 1.1 + * @version 1.3 */ namespace Hujuice\Smtp; From 95cbe021072fdf9b3a8f13881710cedb741cd818 Mon Sep 17 00:00:00 2001 From: Rogov Viktor Date: Thu, 25 Dec 2014 14:36:42 +0600 Subject: [PATCH 4/6] Pipelining support --- composer.json | 2 +- composer.lock | 2 +- src/Smtp.php | 69 +++++++++++++++++++++++++++++++++---------- test/AutoloadTest.php | 15 ---------- test/SmtpTest.php | 27 +++++++++++++++++ 5 files changed, 82 insertions(+), 33 deletions(-) delete mode 100644 test/AutoloadTest.php create mode 100644 test/SmtpTest.php diff --git a/composer.json b/composer.json index 69202a3..18c2ff8 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "hujuice/smtp", - "version": "1.3", + "version": "1.4", "autoload": { "psr-4": { "Hujuice\\Smtp\\": "src/" diff --git a/composer.lock b/composer.lock index 3d27f27..8cde319 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "d19d86fd2d5932d5527bbc7fd37f9a64", + "hash": "1873393e3bd3ecf6579868921cb07339", "packages": [], "packages-dev": [ { diff --git a/src/Smtp.php b/src/Smtp.php index 7025c2c..8daa357 100644 --- a/src/Smtp.php +++ b/src/Smtp.php @@ -233,11 +233,16 @@ class Smtp */ protected $_log = ''; + protected $_pipelining = true; + + protected $_pipelinedCommands = array(); + /** * Charset encoding * * @see http://www.pcvr.nl/tcpip/smtp_sim.htm * @param string $string + * @return string */ protected function _encode($string) { @@ -254,7 +259,7 @@ protected function _encode($string) * @param string $dest * @param string $destName * @param array $class - * @throw Exception + * @throws Exception */ protected function _recipients($dest, $destName, $class) { @@ -280,32 +285,57 @@ protected function _recipients($dest, $destName, $class) throw new Exception('Wrong recipient'); } + protected function _readResponse($expected) + { + $response = ''; + while (($line = fgets($this->_smtp)) !== false) { + $response .= $line; + if ($line[3] != '-') { + break; + } + } + $this->_log .= $response; + + if (substr($response, 0, 3) != $expected) { + throw new Exception("Unexpected response. Expected {$expected}. Here is the dialog dump:\n{$this->_log}"); + } + + return $response; + } + /** * Perform a request/response exchange * * @param string $request * @param string $expect The expected status code * @return string - * @throw Exception + * @throws Exception */ protected function _dialog($request, $expect) { $this->_log .= $request . PHP_EOL; - fwrite ($this->_smtp, $request . self::NL); - $response = fgets($this->_smtp); - - $this->_log .= $response . PHP_EOL; - - if (substr($response, 0, 3) != $expect) - throw new Exception('Message "' . $request . '" NOT accepted! Here is the dialog dump:' . PHP_EOL . $this->_log); + fwrite($this->_smtp, $request . self::NL); + + if ($this->_pipelining) { + // is pipelinable command? + if (in_array(substr($request, 0, 4), array('RSET', 'MAIL', 'SEND', 'SOML', 'SAML', 'RCPT'))) { + $this->_pipelinedCommands[] = $expect; + return null; + } else { + while ($this->_pipelinedCommands) { + $_expected = array_shift($this->_pipelinedCommands); + $this->_readResponse($_expected); + } + } + } - return $response; + return $this->_readResponse($expect); } /** * Connection to the SMTP server - * @throw Exception + * @throws Exception */ public function _connect() { @@ -321,7 +351,8 @@ public function _connect() // HELO $sender = explode('@', $this->_from['address']); - $this->_dialog('HELO ' . $sender[1], self::OK); + $ehlo = $this->_dialog('EHLO ' . $sender[1], self::OK); + $this->_pipelining = preg_match('~250[\s-]pipelining~i', $ehlo); // Auth if ($this->_user && $this->_pass) @@ -348,7 +379,7 @@ public function _connect() * @param string $host * @param integer $port * @param integer $timeout - * @throw Exception + * @throws Exception */ public function __construct($host, $port = 25 , $timeout = 3) { @@ -496,6 +527,7 @@ public function bcc($bcc = null, $bccName = '') * Priority * * @param integer $priority + * @throws Exception * @return integer */ public function priority($priority = null) @@ -514,7 +546,8 @@ public function priority($priority = null) /** * Custom header * - * @param string $header + * @param string $name + * @param string $value * @return array */ public function header($name = null, $value = null) @@ -541,6 +574,8 @@ public function subject($subject = null) * Text * * @param string $text + * @param string $content_type + * @param string $charset * @return string */ public function text($text = null, $content_type = 'text/plain', $charset = 'utf-8') @@ -568,8 +603,10 @@ public function body($body) * @link http://en.wikipedia.org/wiki/MIME#Multipart_messages * @link http://support.mozilla.org/it/questions/746116 * @param string $path + * @param string $name * @param string $content_type * @param string $charset Will be used for text/* only + * @throws Exception * @return array */ public function attachment($path = null, $name = '', $content_type = 'application/octet-stream', $charset = 'utf-8') @@ -640,7 +677,7 @@ public function clear() * * @see http://www.pcvr.nl/tcpip/smtp_sim.htm * @return string - * @throw Exception + * @throws Exception */ public function send() { @@ -748,7 +785,7 @@ public function send() // Message /* - The message will containt text and attachments. + The message will contain text and attachments. This implementation consider the multipart/mixed method only. http://en.wikipedia.org/wiki/MIME#Multipart_messages */ diff --git a/test/AutoloadTest.php b/test/AutoloadTest.php deleted file mode 100644 index 2db1194..0000000 --- a/test/AutoloadTest.php +++ /dev/null @@ -1,15 +0,0 @@ -assertInstanceOf(Smtp::class, $smtp); - } -} diff --git a/test/SmtpTest.php b/test/SmtpTest.php new file mode 100644 index 0000000..895c54d --- /dev/null +++ b/test/SmtpTest.php @@ -0,0 +1,27 @@ +assertInstanceOf(Smtp::class, $smtp); + } + + public function testSend() + { + $smtp = new Smtp('localhost'); + $smtp->from('foo@example.com'); + $smtp->to('bar@example.com'); + $smtp->subject('Test'); + $smtp->text('Test'); + $res = $smtp->send(); + $this->assertStringStartsWith('Message queued for delivery', $res); + echo $smtp->dump(); + } +} From d379a546a43c3d24ba35e8c361754ad92977df5e Mon Sep 17 00:00:00 2001 From: Rogov Viktor Date: Thu, 25 Dec 2014 14:48:08 +0600 Subject: [PATCH 5/6] Fix clear(). Clear _body also --- composer.json | 2 +- src/Smtp.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 18c2ff8..3113837 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "hujuice/smtp", - "version": "1.4", + "version": "1.5", "autoload": { "psr-4": { "Hujuice\\Smtp\\": "src/" diff --git a/src/Smtp.php b/src/Smtp.php index 8daa357..b4a7a24 100644 --- a/src/Smtp.php +++ b/src/Smtp.php @@ -670,6 +670,7 @@ public function clear() $this->_headers = array(); $this->_attachments = array(); $this->_raw = array(); + $this->_body = null; } /** From 63e36b4366de149ab1859c9cd48ad78987dcb9b4 Mon Sep 17 00:00:00 2001 From: Rogov Viktor Date: Thu, 25 Dec 2014 16:00:23 +0600 Subject: [PATCH 6/6] Send QUIT command only when connected --- composer.json | 2 +- src/Smtp.php | 7 ++++--- test/SmtpTest.php | 5 ----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 3113837..baa5e0e 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "hujuice/smtp", - "version": "1.5", + "version": "1.6", "autoload": { "psr-4": { "Hujuice\\Smtp\\": "src/" diff --git a/src/Smtp.php b/src/Smtp.php index b4a7a24..68cb6d8 100644 --- a/src/Smtp.php +++ b/src/Smtp.php @@ -400,11 +400,12 @@ public function __construct($host, $port = 25 , $timeout = 3) */ public function __destruct() { - // Quit - $this->_dialog('QUIT', self::BYE); - if ($this->_smtp) + if ($this->_smtp) { + // Quit + $this->_dialog('QUIT', self::BYE); fclose($this->_smtp); + } } /** diff --git a/test/SmtpTest.php b/test/SmtpTest.php index 895c54d..8a39266 100644 --- a/test/SmtpTest.php +++ b/test/SmtpTest.php @@ -3,10 +3,6 @@ class SmtpTestClass extends PHPUnit_Framework_TestCase { - /** - * @expectedException Exception - * @expectedExceptionMessage Unexpected response - */ public function testSmtpClassLoading() { $smtp = new Smtp('localhost'); @@ -22,6 +18,5 @@ public function testSend() $smtp->text('Test'); $res = $smtp->send(); $this->assertStringStartsWith('Message queued for delivery', $res); - echo $smtp->dump(); } }