Browse Source

Merge pull request #2565 from Howaner/mailpreview-fix

Updated php-mime-mail-parser library to 5.0 to fix webui html preview
André Peters 6 years ago
parent
commit
5cc6f68928

+ 1 - 1
data/web/inc/lib/composer.json

@@ -3,7 +3,7 @@
         "robthree/twofactorauth": "^1.6",
         "yubico/u2flib-server": "^1.0",
         "phpmailer/phpmailer": "^5.2",
-        "php-mime-mail-parser/php-mime-mail-parser": "^2.9",
+        "php-mime-mail-parser/php-mime-mail-parser": "^5.0",
         "soundasleep/html2text": "^0.5.0",
         "ddeboer/imap": "^1.5",
         "matthiasmullie/minify": "^1.3"

+ 149 - 63
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/README.md

@@ -1,6 +1,9 @@
 # php-mime-mail-parser
 
-A fully tested mailparse extension wrapper for PHP 5.4+
+A fully tested email parser for PHP 7.1+ (mailparse extension wrapper).
+
+It's the most effective php email parser around in terms of performance, foreign character encoding, attachment handling, and ease of use.
+Internet Message Format RFC [822](https://tools.ietf.org/html/rfc822), [2822](https://tools.ietf.org/html/rfc2822), [5322](https://tools.ietf.org/html/rfc5322).
 
 [![Latest Version](https://img.shields.io/packagist/v/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases)
 [![Total Downloads](https://img.shields.io/packagist/dt/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://packagist.org/packages/php-mime-mail-parser/php-mime-mail-parser)
@@ -10,6 +13,7 @@ A fully tested mailparse extension wrapper for PHP 5.4+
 
 This extension can be used to...
  * Parse and read email from Postfix
+ * For reading messages (Filename extension: eml)
  * Create webmail 
  * Store email information such a subject, HTML body, attachments, and etc. into a database
 
@@ -35,105 +39,185 @@ To install the latest version of PHP MIME Mail Parser, run the command below:
 
 The following versions of PHP are supported:
 
-* PHP 5.4
-* PHP 5.5
-* PHP 5.6
-* PHP 7
-* HHVM
+* PHP 7.1
+* PHP 7.2
+* PHP 7.3
+
+Previous Versions:
+
+| PHP Compatibility  | Version |
+| ------------- | ------------- |
+| HHVM  | php-mime-mail-parser 2.11.1  |
+| PHP 5.4  | php-mime-mail-parser 2.11.1  |
+| PHP 5.5  | php-mime-mail-parser 2.11.1  |
+| PHP 5.6  | php-mime-mail-parser 3.0.4  |
+| PHP 7.0  | php-mime-mail-parser 3.0.4  |
+
+Make sure you have the mailparse extension (http://php.net/manual/en/book.mailparse.php) properly installed. The command line `php -m | grep mailparse` need to return "mailparse".
 
+
+### Install mailparse extension
+
+#### Ubuntu, Debian & derivatives
 ```
-sudo apt install php-cli php-pear php-dev php-mbstring
+sudo apt install php-cli php-mailparse
 ```
 
-Make sure you have the mailparse extension (http://php.net/manual/en/book.mailparse.php) properly installed. The command line `php -m | grep mailparse` need to return "mailparse" else install it:
-* PHP version > 7.0: mailparse
-* PHP version < 7.0: mailparse 2.1.6
+#### Others platforms
+```
+sudo apt install php-cli php-pear php-dev php-mbstring
+pecl install mailparse
+```
 
-Follow this steps to install mailparse:
+#### From source
 
-* Compile in the temp folder the extension mailparse or mailparse-2.1.6 (workaround because pecl install doesn't work yet)
+AAAAMMDD should be `php-config --extension-dir`
 ```
-cd
-pecl download mailparse
-tar -xvf mailparse-3.0.2.tgz 
-cd mailparse-3.0.2/
+git clone https://github.com/php/pecl-mail-mailparse.git
+cd pecl-mail-mailparse
 phpize
 ./configure
 sed -i 's/#if\s!HAVE_MBSTRING/#ifndef MBFL_MBFILTER_H/' ./mailparse.c
 make
-sudo mv modules/mailparse.so /usr/lib/php/20160303/
-```
-* Add the extension mailparse and activate it
-```
+sudo mv modules/mailparse.so /usr/lib/php/AAAAMMDD/
 echo "extension=mailparse.so" | sudo tee /etc/php/7.1/mods-available/mailparse.ini
 sudo phpenmod mailparse
 ```
 
-On Windows, you need to download mailparse DLL from http://pecl.php.net/package/mailparse and add the line "extension=php_mailparse.dll" to php.ini accordingly.
+#### Windows
+You need to download mailparse DLL from http://pecl.php.net/package/mailparse and add the line "extension=php_mailparse.dll" to php.ini accordingly.
 
 ## How do I use it?
 
+### Loading an email
+
+You can load an email with 4 differents ways. You only need to use one of the following four.
+
 ```php
-<?php
-// Include the library first
 require_once __DIR__.'/vendor/autoload.php';
 
-$path = 'path/to/mail.txt';
-$Parser = new PhpMimeMailParser\Parser();
+$path = 'path/to/email.eml';
+$parser = new PhpMimeMailParser\Parser();
+
+// 1. Specify a file path (string)
+$parser->setPath($path); 
+
+// 2. Specify the raw mime mail text (string)
+$parser->setText(file_get_contents($path));
 
-// There are four methods available to indicate which mime mail to parse.
-// You only need to use one of the following four:
+// 3. Specify a php file resource (stream)
+$parser->setStream(fopen($path, "r"));
 
-// 1. Specify a file path to the mime mail.
-$Parser->setPath($path); 
+// 4.  Specify a stream to work with mail server (stream)
+$parser->setStream(fopen("php://stdin", "r"));
+```
+
+### Get the metadata of the message
 
-// 2. Specify a php file resource (stream) to the mime mail.
-$Parser->setStream(fopen($path, "r"));
+Get the sender and the receiver:
 
-// 3. Specify the raw mime mail text.
-$Parser->setText(file_get_contents($path));
+```php
+$rawHeaderTo = $parser->getHeader('to');
+// return "test" <test@example.com>, "test2" <test2@example.com>
 
-// 4.  Specify a stream to work with mail server
-$Parser->setStream(fopen("php://stdin", "r"));
+$arrayHeaderTo = $parser->getAddresses('to');
+// return [["display"=>"test", "address"=>"test@example.com", false]]
 
-// Once we've indicated where to find the mail, we can parse out the data
-$to = $Parser->getHeader('to');             // "test" <test@example.com>, "test2" <test2@example.com>
-$addressesTo = $Parser->getAddresses('to'); //Return an array : [["display"=>"test", "address"=>"test@example.com", false],["display"=>"test2", "address"=>"test2@example.com", false]]
+$rawHeaderFrom = $parser->getHeader('from');
+// return "test" <test@example.com>
 
-$from = $Parser->getHeader('from');             // "test" <test@example.com>
-$addressesFrom = $Parser->getAddresses('from'); //Return an array : [["display"=>"test", "address"=>"test@example.com", "is_group"=>false]]
+$arrayHeaderFrom = $parser->getAddresses('from');
+// return [["display"=>"test", "address"=>"test@example.com", "is_group"=>false]]
+```
 
-$subject = $Parser->getHeader('subject');
+Get the subject:
 
-$text = $Parser->getMessageBody('text');
+```php
+$subject = $parser->getHeader('subject');
+```
 
-$html = $Parser->getMessageBody('html');
-$htmlEmbedded = $Parser->getMessageBody('htmlEmbedded'); //HTML Body included data
+Get other headers:
 
-$stringHeaders = $Parser->getHeadersRaw();	// Get all headers as a string, no charset conversion
-$arrayHeaders = $Parser->getHeaders();		// Get all headers as an array, with charset conversion
+```php
+$stringHeaders = $parser->getHeadersRaw();
+// return all headers as a string, no charset conversion
 
-// Pass in a writeable path to save attachments
-$attach_dir = '/path/to/save/attachments/'; 	// Be sure to include the trailing slash
-$include_inline = true;  			// Optional argument to include inline attachments (default: true)
-$Parser->saveAttachments($attach_dir [,$include_inline]);
+$arrayHeaders = $parser->getHeaders();
+// return all headers as an array, with charset conversion
+```
 
-// Get an array of Attachment items from $Parser
-$attachments = $Parser->getAttachments([$include_inline]);
+### Get the body of the message
 
-//  Loop through all the Attachments
-if (count($attachments) > 0) {
-	foreach ($attachments as $attachment) {
-		echo 'Filename : '.$attachment->getFilename().'<br />'; // logo.jpg
-		echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br />'; // 1000
-		echo 'Filetype : '.$attachment->getContentType().'<br />'; // image/jpeg
-		echo 'MIME part string : '.$attachment->getMimePartStr().'<br />'; // (the whole MIME part of the attachment)
-	}
-}
+```php
+$text = $parser->getMessageBody('text');
+// return the text version
+
+$html = $parser->getMessageBody('html');
+// return the html version
+
+$htmlEmbedded = $parser->getMessageBody('htmlEmbedded');
+// return the html version with the embedded contents like images
+
+```
+
+### Get attachments
+
+Save all attachments in a directory
+
+```php
+$parser->saveAttachments('/path/to/save/attachments/');
+// return all attachments saved in the directory (include inline attachments)
+
+$parser->saveAttachments('/path/to/save/attachments/', false);
+// return all attachments saved in the directory (exclude inline attachments)
+
+// Save all attachments with the strategy ATTACHMENT_DUPLICATE_SUFFIX (default)
+$parser->saveAttachments('/path/to/save/attachments/', false, Parser::ATTACHMENT_DUPLICATE_SUFFIX);
+// return all attachments saved in the directory: logo.jpg, logo_1.jpg, ..., logo_100.jpg, YY34UFHBJ.jpg
+
+// Save all attachments with the strategy ATTACHMENT_RANDOM_FILENAME
+$parser->saveAttachments('/path/to/save/attachments/', false, Parser::ATTACHMENT_RANDOM_FILENAME);
+// return all attachments saved in the directory: YY34UFHBJ.jpg and F98DBZ9FZF.jpg
+
+// Save all attachments with the strategy ATTACHMENT_DUPLICATE_THROW
+$parser->saveAttachments('/path/to/save/attachments/', false, Parser::ATTACHMENT_DUPLICATE_THROW);
+// return an exception when there is attachments duplicate.
 
-?>
 ```
 
+Get all attachments
+
+```php
+$attachments = $parser->getAttachments();
+// return an array of all attachments (include inline attachments)
+
+$attachments = $parser->getAttachments(false);
+// return an array of all attachments (exclude inline attachments)
+```
+
+
+Loop through all the Attachments
+```php
+foreach ($attachments as $attachment) {
+    echo 'Filename : '.$attachment->getFilename().'<br />';
+    // return logo.jpg
+    
+    echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br />';
+    // return 1000
+    
+    echo 'Filetype : '.$attachment->getContentType().'<br />';
+    // return image/jpeg
+    
+    echo 'MIME part string : '.$attachment->getMimePartStr().'<br />';
+    // return the whole MIME part of the attachment
+
+    $attachment->save('/path/to/save/myattachment/', Parser::ATTACHMENT_DUPLICATE_SUFFIX);
+    // return the path and the filename saved (same strategy available than saveAttachments)
+}
+```
+
+## Postfix configuration to manage email from a mail server
+
 Next you need to forward emails to this script above. For that I'm using [Postfix](http://www.postfix.org/) like a mail server, you need to configure /etc/postfix/master.cf
 
 Add this line at the end of the file (specify myhook to send all emails to the script test.php)
@@ -150,6 +234,8 @@ smtp      inet  n       -       -       -       -       smtpd
 
 The php script must use the fourth method to work with this configuration.
 
+And finally the easiest way is to use my SaaS https://mailcare.io
+
 
 ## Can I contribute?
 
@@ -162,6 +248,6 @@ Feel free to contribute!
 
 If you report an issue, please provide the raw email that triggered it. This helps us reproduce the issue and fix it more quickly.
 
-### License
+## License
 
 The php-mime-mail-parser/php-mime-mail-parser is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)

+ 10 - 0
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/compile_mailparse.sh

@@ -0,0 +1,10 @@
+#!/bin/sh
+
+git clone https://github.com/php/pecl-mail-mailparse.git
+cd pecl-mail-mailparse
+phpize
+./configure
+sed -i 's/#if\s!HAVE_MBSTRING/#ifndef MBFL_MBFILTER_H/' ./mailparse.c
+make
+sudo mv modules/mailparse.so /home/travis/.phpenv/versions/7.3.2/lib/php/extensions/no-debug-zts-20180731/
+echo 'extension=mailparse.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini

+ 7 - 7
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/composer.json

@@ -1,8 +1,8 @@
 {
     "name": "php-mime-mail-parser/php-mime-mail-parser",
     "type": "library",
-    "description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
-    "keywords": ["mime", "mail", "mailparse", "MimeMailParser"],
+    "description": "A fully tested email parser for PHP 7.1+ (mailparse extension wrapper).",
+    "keywords": ["mime", "mail", "mailparse", "MimeMailParser", "parser", "php"],
     "homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
     "license": "MIT",
     "authors": [      
@@ -42,14 +42,14 @@
         "url":"https://github.com/php-mime-mail-parser/php-mime-mail-parser.git"
     },
     "require": {
-        "php":           "^5.4.0 || ^7.0",
+        "php":           "^7.1",
         "ext-mailparse": "*"
     },
     "require-dev": {
-        "phpunit/phpunit":              "^4.0 || ^5.0",
-        "phpunit/php-token-stream":     "^1.3.0",
-        "satooshi/php-coveralls":       "0.*",
-        "squizlabs/PHP_CodeSniffer":    "2.*"
+        "phpunit/phpunit":              "^7.0",
+        "phpunit/php-token-stream":     "^3.0",
+        "php-coveralls/php-coveralls":  "^2.1",
+        "squizlabs/php_codesniffer":    "^3.4"
     },
     "replace": {
         "exorus/php-mime-mail-parser":   "*",

+ 3 - 3
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/mailparse-stubs.php

@@ -135,7 +135,7 @@ function mailparse_msg_create()
  * @param resource $mimemail A valid MIME resource allocated by
  *                           mailparse_msg_create or mailparse_msg_parse_file
  *
- * @return bool
+ * @return boolean|null
  */
 function mailparse_msg_free($mimemail)
 {
@@ -149,7 +149,7 @@ function mailparse_msg_free($mimemail)
  * @param resource $mimemail A valid MIME resource
  * @param string   $data
  *
- * @return bool
+ * @return boolean|null
  */
 function mailparse_msg_parse($mimemail, $data)
 {
@@ -199,7 +199,7 @@ function mailparse_determine_best_xfer_encoding($fp)
  * @param string   $encoding One of the character encodings supported by the mbstring
  *                           module
  *
- * @return bool
+ * @return boolean|null
  */
 function mailparse_stream_encode($sourcefp, $destfp, $encoding)
 {

+ 90 - 1
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/Attachment.php

@@ -50,6 +50,11 @@ class Attachment
      */
     protected $mimePartStr;
 
+    /**
+     * @var integer $maxDuplicateNumber
+     */
+    public $maxDuplicateNumber = 100;
+
     /**
      * Attachment constructor.
      *
@@ -133,13 +138,44 @@ class Attachment
     /**
      * Get a handle to the stream
      *
-     * @return stream
+     * @return resource
      */
     public function getStream()
     {
         return $this->stream;
     }
 
+    /**
+     * Rename a file if it already exists at its destination.
+     * Renaming is done by adding a duplicate number to the file name. E.g. existingFileName_1.ext.
+     * After a max duplicate number, renaming the file will switch over to generating a random suffix.
+     *
+     * @param string $fileName  Complete path to the file.
+     * @return string           The suffixed file name.
+     */
+    protected function suffixFileName(string $fileName): string
+    {
+        $pathInfo = pathinfo($fileName);
+        $dirname = $pathInfo['dirname'].DIRECTORY_SEPARATOR;
+        $filename = $pathInfo['filename'];
+        $extension  = empty($pathInfo['extension']) ? '' : '.'.$pathInfo['extension'];
+
+        $i = 0;
+        do {
+            $i++;
+
+            if ($i > $this->maxDuplicateNumber) {
+                $duplicateExtension = uniqid();
+            } else {
+                $duplicateExtension = $i;
+            }
+
+            $resultName = $dirname.$filename."_$duplicateExtension".$extension;
+        } while (file_exists($resultName));
+
+        return $resultName;
+    }
+
     /**
      * Read the contents a few bytes at a time until completed
      * Once read to completion, it always returns false
@@ -180,4 +216,57 @@ class Attachment
     {
         return $this->mimePartStr;
     }
+
+    /**
+     * Save the attachment individually
+     *
+     * @param string $attach_dir
+     * @param string $filenameStrategy
+     *
+     * @return string
+     */
+    public function save(
+        $attach_dir,
+        $filenameStrategy = Parser::ATTACHMENT_DUPLICATE_SUFFIX
+    ) {
+        $attach_dir = rtrim($attach_dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
+        if (!is_dir($attach_dir)) {
+            mkdir($attach_dir);
+        }
+
+        // Determine filename
+        switch ($filenameStrategy) {
+            case Parser::ATTACHMENT_RANDOM_FILENAME:
+                $attachment_path = tempnam($attach_dir, '');
+                break;
+            case Parser::ATTACHMENT_DUPLICATE_THROW:
+            case Parser::ATTACHMENT_DUPLICATE_SUFFIX:
+                $attachment_path = $attach_dir.$this->getFilename();
+                break;
+            default:
+                throw new Exception('Invalid filename strategy argument provided.');
+        }
+
+        // Handle duplicate filename
+        if (file_exists($attachment_path)) {
+            switch ($filenameStrategy) {
+                case Parser::ATTACHMENT_DUPLICATE_THROW:
+                    throw new Exception('Could not create file for attachment: duplicate filename.');
+                case Parser::ATTACHMENT_DUPLICATE_SUFFIX:
+                    $attachment_path = $this->suffixFileName($attachment_path);
+                    break;
+            }
+        }
+
+        /** @var resource $fp */
+        if ($fp = fopen($attachment_path, 'w')) {
+            while ($bytes = $this->read()) {
+                fwrite($fp, $bytes);
+            }
+            fclose($fp);
+            return realpath($attachment_path);
+        } else {
+            throw new Exception('Could not write attachments. Your directory may be unwritable by PHP.');
+        }
+    }
 }

+ 31 - 19
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/Charset.php

@@ -78,20 +78,20 @@ class Charset implements CharsetManager
         'cns11643'                 => 'x-euc-tw',
         'x-imap4-modified-utf7'    => 'x-imap4-modified-utf7',
         'x-euc-tw'                 => 'x-euc-tw',
-        'x-mac-ce'                 => 'x-mac-ce',
-        'x-mac-turkish'            => 'x-mac-turkish',
-        'x-mac-greek'              => 'x-mac-greek',
-        'x-mac-icelandic'          => 'x-mac-icelandic',
-        'x-mac-croatian'           => 'x-mac-croatian',
-        'x-mac-romanian'           => 'x-mac-romanian',
-        'x-mac-cyrillic'           => 'x-mac-cyrillic',
-        'x-mac-ukrainian'          => 'x-mac-cyrillic',
-        'x-mac-hebrew'             => 'x-mac-hebrew',
-        'x-mac-arabic'             => 'x-mac-arabic',
-        'x-mac-farsi'              => 'x-mac-farsi',
-        'x-mac-devanagari'         => 'x-mac-devanagari',
-        'x-mac-gujarati'           => 'x-mac-gujarati',
-        'x-mac-gurmukhi'           => 'x-mac-gurmukhi',
+        'x-mac-ce'                 => 'MACCE',
+        'x-mac-turkish'            => 'MACTURKISH',
+        'x-mac-greek'              => 'MACGREEK',
+        'x-mac-icelandic'          => 'MACICELANDIC',
+        'x-mac-croatian'           => 'MACCROATIAN',
+        'x-mac-romanian'           => 'MACROMANIAN',
+        'x-mac-cyrillic'           => 'MACCYRILLIC',
+        'x-mac-ukrainian'          => 'MACUKRAINIAN',
+        'x-mac-hebrew'             => 'MACHEBREW',
+        'x-mac-arabic'             => 'MACARABIC',
+        'x-mac-farsi'              => 'MACFARSI',
+        'x-mac-devanagari'         => 'MACDEVANAGARI',
+        'x-mac-gujarati'           => 'MACGUJARATI',
+        'x-mac-gurmukhi'           => 'MACGURMUKHI',
         'armscii-8'                => 'armscii-8',
         'x-viet-tcvn5712'          => 'x-viet-tcvn5712',
         'x-viet-vps'               => 'x-viet-vps',
@@ -315,11 +315,23 @@ class Charset implements CharsetManager
      */
     public function decodeCharset($encodedString, $charset)
     {
-        if (strtolower($charset) == 'utf-8' || strtolower($charset) == 'us-ascii') {
+        $charset = $this->getCharsetAlias($charset);
+
+        if ($charset == 'utf-8' || $charset == 'us-ascii') {
             return $encodedString;
-        } else {
-            return iconv($this->getCharsetAlias($charset), 'UTF-8//TRANSLIT//IGNORE', $encodedString);
         }
+
+        if (function_exists('mb_convert_encoding')) {
+            if ($charset == 'ISO-2022-JP') {
+                return mb_convert_encoding($encodedString, 'UTF-8', 'ISO-2022-JP-MS');
+            }
+
+            if (array_search($charset, array_map('strtolower', mb_list_encodings()))) {
+                return mb_convert_encoding($encodedString, 'UTF-8', $charset);
+            }
+        }
+
+        return iconv($charset, 'UTF-8//TRANSLIT//IGNORE', $encodedString);
     }
 
     /**
@@ -331,8 +343,8 @@ class Charset implements CharsetManager
 
         if (array_key_exists($charset, $this->charsetAlias)) {
             return $this->charsetAlias[$charset];
-        } else {
-            return null;
         }
+        
+        return 'us-ascii';
     }
 }

+ 2 - 0
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/Middleware.php

@@ -7,6 +7,8 @@ namespace PhpMimeMailParser;
  */
 class Middleware implements Contracts\Middleware
 {
+    protected $parser;
+
     /**
      * Create a middleware using a callable $fn
      *

+ 3 - 3
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/MiddlewareStack.php

@@ -2,7 +2,7 @@
 
 namespace PhpMimeMailParser;
 
-use PhpMimeMailParser\Contracts\MiddleWare;
+use PhpMimeMailParser\Contracts\MiddleWare as MiddleWareContracts;
 
 /**
  * A stack of middleware chained together by (MiddlewareStack $next)
@@ -29,7 +29,7 @@ class MiddlewareStack
      *
      * @param Middleware $middleware
      */
-    public function __construct(Middleware $middleware = null)
+    public function __construct(MiddleWareContracts $middleware = null)
     {
         $this->middleware = $middleware;
     }
@@ -40,7 +40,7 @@ class MiddlewareStack
      * @param Middleware $middleware
      * @return MiddlewareStack Immutable MiddlewareStack
      */
-    public function add(Middleware $middleware)
+    public function add(MiddleWareContracts $middleware)
     {
         $stack = new static($middleware);
         $stack->next = $this;

+ 24 - 45
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/Parser.php

@@ -110,6 +110,15 @@ class Parser
      */
     public function setPath($path)
     {
+        if (is_writable($path)) {
+            $file = fopen($path, 'a+');
+            fseek($file, -1, SEEK_END);
+            if (fread($file, 1) != "\n") {
+                fwrite($file, PHP_EOL);
+            }
+            fclose($file);
+        }
+
         // should parse message incrementally from file
         $this->resource = mailparse_msg_parse_file($path);
         $this->stream = fopen($path, 'r');
@@ -142,6 +151,11 @@ class Parser
             while (!feof($stream)) {
                 fwrite($tmp_fp, fread($stream, 2028));
             }
+
+            if (fread($tmp_fp, 1) != "\n") {
+                fwrite($tmp_fp, PHP_EOL);
+            }
+
             fseek($tmp_fp, 0);
             $this->stream = &$tmp_fp;
         } else {
@@ -170,9 +184,14 @@ class Parser
      */
     public function setText($data)
     {
-        if (!$data) {
+        if (empty($data)) {
             throw new Exception('You must not call MimeMailParser::setText with an empty string parameter');
         }
+
+        if (substr($data, -1) != "\n") {
+            $data = $data.PHP_EOL;
+        }
+
         $this->resource = mailparse_msg_create();
         // does not parse incrementally, fast memory hog might explode
         mailparse_msg_parse($this->resource, $data);
@@ -205,7 +224,7 @@ class Parser
      *
      * @param string $name Header name (case-insensitive)
      *
-     * @return string
+     * @return string|bool
      * @throws Exception
      */
     public function getRawHeader($name)
@@ -214,7 +233,7 @@ class Parser
         if (isset($this->parts[1])) {
             $headers = $this->getPart('headers', $this->parts[1]);
 
-            return (isset($headers[$name])) ? $headers[$name] : false;
+            return isset($headers[$name]) ? $headers[$name] : false;
         } else {
             throw new Exception(
                 'setPath() or setText() or setStream() must be called before retrieving email headers.'
@@ -227,7 +246,7 @@ class Parser
      *
      * @param string $name Header name (case-insensitive)
      *
-     * @return string
+     * @return string|bool
      */
     public function getHeader($name)
     {
@@ -559,50 +578,10 @@ class Parser
         $filenameStrategy = self::ATTACHMENT_DUPLICATE_SUFFIX
     ) {
         $attachments = $this->getAttachments($include_inline);
-        if (empty($attachments)) {
-            return false;
-        }
-
-        if (!is_dir($attach_dir)) {
-            mkdir($attach_dir);
-        }
 
         $attachments_paths = [];
         foreach ($attachments as $attachment) {
-            // Determine filename
-            switch ($filenameStrategy) {
-                case self::ATTACHMENT_RANDOM_FILENAME:
-                    $attachment_path = tempnam($attach_dir, '');
-                    break;
-                case self::ATTACHMENT_DUPLICATE_THROW:
-                case self::ATTACHMENT_DUPLICATE_SUFFIX:
-                    $attachment_path = $attach_dir.$attachment->getFilename();
-                    break;
-                default:
-                    throw new Exception('Invalid filename strategy argument provided.');
-            }
-
-            // Handle duplicate filename
-            if (file_exists($attachment_path)) {
-                switch ($filenameStrategy) {
-                    case self::ATTACHMENT_DUPLICATE_THROW:
-                        throw new Exception('Could not create file for attachment: duplicate filename.');
-                    case self::ATTACHMENT_DUPLICATE_SUFFIX:
-                        $attachment_path = tempnam($attach_dir, $attachment->getFilename());
-                        break;
-                }
-            }
-
-            /** @var resource $fp */
-            if ($fp = fopen($attachment_path, 'w')) {
-                while ($bytes = $attachment->read()) {
-                    fwrite($fp, $bytes);
-                }
-                fclose($fp);
-                $attachments_paths[] = realpath($attachment_path);
-            } else {
-                throw new Exception('Could not write attachments. Your directory may be unwritable by PHP.');
-            }
+            $attachments_paths[] = $attachment->save($attach_dir, $filenameStrategy);
         }
 
         return $attachments_paths;