aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Enderlin <ivan.enderlin@hoa-project.net>2015-05-26 16:58:56 +0200
committerIvan Enderlin <ivan.enderlin@hoa-project.net>2015-05-26 17:33:47 +0200
commit22fa3e6dd9daa375c6599f85163967953245dfa2 (patch)
tree7e21f8786ffe08f3f2665ac5989592df1bdb32f3
parent2255f3ad03bfd2eee3146f1a74dffd414a39a456 (diff)
downloadMail-22fa3e6dd9daa375c6599f85163967953245dfa2.zip
Mail-22fa3e6dd9daa375c6599f85163967953245dfa2.tar.gz
Mail-22fa3e6dd9daa375c6599f85163967953245dfa2.tar.bz2
Start implementing the QuotedPrintable encoder.
-rw-r--r--Content/Encoder/Encoder.php12
-rw-r--r--Content/Encoder/QuotedPrintable.php73
-rw-r--r--Test/Unit/Content/Encoder/QuotedPrintable.php163
3 files changed, 229 insertions, 19 deletions
diff --git a/Content/Encoder/Encoder.php b/Content/Encoder/Encoder.php
index b97edab..8d63148 100644
--- a/Content/Encoder/Encoder.php
+++ b/Content/Encoder/Encoder.php
@@ -48,8 +48,20 @@ use Hoa\Core;
*/
interface Encoder
{
+ /**
+ * Encode a string.
+ *
+ * @param string $string String to encode.
+ * @return string
+ */
public static function encode($string);
+ /**
+ * Decode a string.
+ *
+ * @param string $string String to decode.
+ * @return string
+ */
public static function decode($string);
}
diff --git a/Content/Encoder/QuotedPrintable.php b/Content/Encoder/QuotedPrintable.php
index 176cb99..34de25c 100644
--- a/Content/Encoder/QuotedPrintable.php
+++ b/Content/Encoder/QuotedPrintable.php
@@ -36,6 +36,8 @@
namespace Hoa\Mail\Content\Encoder;
+use Hoa\Mail;
+
/**
* Class \Hoa\Mail\Content\Encoder\QuotedPrintable.
*
@@ -48,37 +50,70 @@ namespace Hoa\Mail\Content\Encoder;
class QuotedPrintable implements Encoder
{
/**
+ * Encode into quoted-printable format.
*
* @param string $string String to encode.
* @return string
*/
public static function encode($string)
{
- if (0 === preg_match('#[\x80-\xff]+#', $string)) {
- return $string;
- }
+ // RFC2045, Section 6.7, rules 1 and 2.
+ $string = preg_replace_callback(
+ // 0x00 to 0xff minus:
+ // (from rule 1)
+ // * 0x0a,
+ // * 0x0d,
+ // (from rule 2)
+ // * 0x21 to 0x3c,
+ // * 0x3e to 0x7e,
+ // (from rule 3)
+ // * 0x09,
+ // * 0x20.
+ '#[\x00-\x08\x0b\x0c\x0e-\x1f\x3d-\x3d\x7f-\xff]#',
+ function ($matches) {
+ $substring = $matches[0];
+ $out = null;
+
+ for ($i = 0, $max = strlen($substring); $i < $max; ++$i) {
+ $out .= vsprintf('=%02X', ord($substring[$i]));
+ }
+
+ return $out;
+ },
+ $string
+ );
+
+ // RFC2045, Section 6.7, rule 3.
+ $string = preg_replace_callback(
+ '#([\x09\x20])' . CRLF . '#',
+ function ($matches) {
+ return vsprintf('=%02X', ord($matches[1])) . CRLF;
+ },
+ $string
+ );
- // RFC2045, Section 6.7, rule 1.
- return
- '=?utf-8?Q?' .
- preg_replace_callback(
- '#[\x80-\xff]+#',
- function ($matches) {
- $substring = $matches[0];
- $out = null;
+ // RFC2045, Section 6.7, rule 4.
+ // CRLF is not encoded.
- for ($i = 0, $max = strlen($substring); $i < $max; ++$i) {
- $out .= '=' . strtoupper(dechex(ord($substring[$i])));
- }
+ // RFC2045, Section 6.7, rule 5.
+ $string = wordwrap(
+ $string,
+ 75,
+ ' =' . CRLF,
+ false
+ );
- return strtoupper($out);
- },
- $string
- ) .
- '?=';
+ return $string;
}
+ /**
+ * Decode from quoted-printable format.
+ *
+ * @param string $string String to decode.
+ * @return string
+ */
public static function decode($string)
{
+ throw new Mail\Exception('Not implemented.');
}
}
diff --git a/Test/Unit/Content/Encoder/QuotedPrintable.php b/Test/Unit/Content/Encoder/QuotedPrintable.php
new file mode 100644
index 0000000..9863a0b
--- /dev/null
+++ b/Test/Unit/Content/Encoder/QuotedPrintable.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * Hoa
+ *
+ *
+ * @license
+ *
+ * New BSD License
+ *
+ * Copyright © 2007-2015, Hoa community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the Hoa nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+namespace Hoa\Mail\Test\Unit\Content\Encoder;
+
+use Hoa\Mail\Content\Encoder\QuotedPrintable as SUT;
+use Hoa\Test;
+
+/**
+ * Class \Hoa\Mail\Test\Unit\Content\Encoder\QuotedPrintable.
+ *
+ * Test suite of the quoted-printable encoder.
+ *
+ * @copyright Copyright © 2007-2015 Hoa community
+ * @license New BSD License
+ */
+class QuotedPrintable extends Test\Unit\Suite
+{
+ public function case_rfc2045_section_6_7_rule1()
+ {
+ $this
+ ->given($datum = 'abc')
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc')
+
+ // 8bits.
+ ->given($datum = 'fôöbàr')
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo('f=C3=B4=C3=B6b=C3=A0r')
+
+ ->given($datum = '😄!')
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo('=F0=9F=98=84!')
+
+ ->given($datum = 'abc' . CRLF . 'def')
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc' . CRLF . 'def');
+ }
+
+ public function case_rfc2045_section6_7_rule2()
+ {
+ $this
+ ->given(
+ $notEncoded = array_map(
+ 'chr',
+ array_merge(
+ range(33, 60),
+ range(62, 126)
+ )
+ )
+ )
+ ->when(function () use ($notEncoded) {
+ foreach ($notEncoded as $char) {
+ $this
+ ->string($char)
+ ->isEqualTo(SUT::encode($char));
+ }
+ });
+ }
+
+ public function case_rfc2045_section6_7_rule3()
+ {
+ $this
+ ->given($tab = 'abc' . "\t\t" . 'def')
+ ->when($result = SUT::encode($tab))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc' . "\t\t" . 'def')
+
+ ->given($tab = 'abc' . "\t\t" . CRLF)
+ ->when($result = SUT::encode($tab))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc' . "\t" . '=09' . CRLF)
+
+ ->given($space = 'abc def')
+ ->when($result = SUT::encode($space))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc def')
+
+ ->given($space = 'abc ' . CRLF)
+ ->when($result = SUT::encode($space))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc =20' . CRLF);
+ }
+
+ public function case_rfc2045_section_6_7_rule4()
+ {
+ $this
+ ->given($datum = 'abc' . CRLF . 'def' . CRLF . 'ghi' . CRLF)
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo($datum);
+ }
+
+ public function case_rfc2045_section_6_7_rule5()
+ {
+ $this
+ ->given($datum = 'abc')
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo('abc')
+
+ ->given(
+ $datum =
+ 'abc def ghi jkl mno pqr stu vwx yz ' .
+ 'abc def ghi jkl mno pqr stu vwx yz ' .
+ 'abc def ghi jkl mno pqr stu vwx yz' . CRLF
+ )
+ ->when($result = SUT::encode($datum))
+ ->then
+ ->string($result)
+ ->isEqualTo(
+ 'abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz abc =' . CRLF .
+ 'def ghi jkl mno pqr stu vwx yz' . CRLF
+ );
+ }
+}