Fixed line endings DOS->UNIX in #16911: Restructure of t3lib_utility_Mail hook subscriber
authorJigal van Hemert <jigal@xs4all.nl>
Sat, 15 Jan 2011 18:52:09 +0000 (18:52 +0000)
committerJigal van Hemert <jigal@xs4all.nl>
Sat, 15 Jan 2011 18:52:09 +0000 (18:52 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@10086 709f56b5-9817-0410-a4d7-c38de5d9e867

t3lib/interfaces/interface.t3lib_mail_maileradapter.php
t3lib/mail/class.t3lib_mail_rfc822addressesparser.php
t3lib/mail/class.t3lib_mail_swiftmaileradapter.php

index 0fb8ec1..8bb87af 100644 (file)
@@ -1,52 +1,52 @@
-<?php\r
-/***************************************************************\r
- *  Copyright notice\r
- *\r
- *  (c) 2011 Ingo Renner <ingo@typo3.org>\r
- *  All rights reserved\r
- *\r
- *  This script is part of the TYPO3 project. The TYPO3 project is\r
- *  free software; you can redistribute it and/or modify\r
- *  it under the terms of the GNU General Public License as published by\r
- *  the Free Software Foundation; either version 2 of the License, or\r
- *  (at your option) any later version.\r
- *\r
- *  The GNU General Public License can be found at\r
- *  http://www.gnu.org/copyleft/gpl.html.\r
- *  A copy is found in the textfile GPL.txt and important notices to the license\r
- *  from the author is found in LICENSE.txt distributed with these scripts.\r
- *\r
- *\r
- *  This script is distributed in the hope that it will be useful,\r
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *  GNU General Public License for more details.\r
- *\r
- *  This copyright notice MUST APPEAR in all copies of the script!\r
- ***************************************************************/\r
-\r
-/**\r
- * Mailer Adapter interface\r
- *\r
- * @author     Ingo Renner <ingo@typo3.org>\r
- * @package TYPO3\r
- * @subpackage t3lib\r
- */\r
-interface t3lib_mail_MailerAdapter {\r
-\r
-       /**\r
-        * Mail sending function\r
-        *\r
-        * @param       string  $to Mail recipient.\r
-        * @param       string  $subject Mail subject.\r
-        * @param       string  $messageBody Mail body.\r
-        * @param       array   $additionalHeaders Additional mail headers.\r
-        * @param       array   $additionalParameters Additional mailer parameters.\r
-        * @param       boolean $fakeSending Whether to fake sending or not, used in Unit Tests.\r
-        * @return      boolean TRUE if the mail was successfully sent, FALSE otherwise.\r
-        */\r
-       public function mail($to, $subject, $messageBody, $additionalHeaders = NULL, $additionalParameters = NULL, $fakeSending = FALSE);\r
-\r
-}\r
-\r
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2011 Ingo Renner <ingo@typo3.org>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Mailer Adapter interface
+ *
+ * @author     Ingo Renner <ingo@typo3.org>
+ * @package TYPO3
+ * @subpackage t3lib
+ */
+interface t3lib_mail_MailerAdapter {
+
+       /**
+        * Mail sending function
+        *
+        * @param       string  $to Mail recipient.
+        * @param       string  $subject Mail subject.
+        * @param       string  $messageBody Mail body.
+        * @param       array   $additionalHeaders Additional mail headers.
+        * @param       array   $additionalParameters Additional mailer parameters.
+        * @param       boolean $fakeSending Whether to fake sending or not, used in Unit Tests.
+        * @return      boolean TRUE if the mail was successfully sent, FALSE otherwise.
+        */
+       public function mail($to, $subject, $messageBody, $additionalHeaders = NULL, $additionalParameters = NULL, $fakeSending = FALSE);
+
+}
+
 ?>
\ No newline at end of file
index 1002c3d..5978598 100644 (file)
-<?php\r
-/**\r
- * RFC 822 Email address list validation Utility\r
- *\r
- * PHP versions 4 and 5\r
- *\r
- * LICENSE:\r
- *\r
- * Copyright (c) 2001-2010, Richard Heyes\r
- * All rights reserved.\r
- *\r
- * Redistribution and use in source and binary forms, with or without\r
- * modification, are permitted provided that the following conditions\r
- * are met:\r
- *\r
- * o Redistributions of source code must retain the above copyright\r
- *   notice, this list of conditions and the following disclaimer.\r
- * o Redistributions in binary form must reproduce the above copyright\r
- *   notice, this list of conditions and the following disclaimer in the\r
- *   documentation and/or other materials provided with the distribution.\r
- * o The names of the authors may not be used to endorse or promote\r
- *   products derived from this software without specific prior written\r
- *   permission.\r
- *\r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- *\r
- * @category    Mail\r
- * @package     Mail\r
- * @author      Richard Heyes <richard@phpguru.org>\r
- * @author      Chuck Hagenbuch <chuck@horde.org\r
- * @copyright   2001-2010 Richard Heyes\r
- * @license     http://opensource.org/licenses/bsd-license.php New BSD License\r
- * @version     CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $\r
- * @link        http://pear.php.net/package/Mail/\r
- *\r
- * Incorporated in TYPO3 by Ernesto Baschny <ernst@cron-it.de>\r
- */\r
-\r
-/**\r
- * RFC 822 Email address list validation Utility\r
- *\r
- * What is it?\r
- *\r
- * This class will take an address string, and parse it into it's consituent\r
- * parts, be that either addresses, groups, or combinations. Nested groups\r
- * are not supported. The structure it returns is pretty straight forward,\r
- * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use\r
- * print_r() to view the structure.\r
- *\r
- * How do I use it?\r
- *\r
- * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';\r
- * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)\r
- * print_r($structure);\r
- *\r
- * @author  Richard Heyes <richard@phpguru.org>\r
- * @author  Chuck Hagenbuch <chuck@horde.org>\r
- * @version $Revision: 294749 $\r
- * @license BSD\r
- * @package Mail\r
- */\r
-class t3lib_mail_Rfc822AddressesParser {\r
-\r
-       /**\r
-        * The address being parsed by the RFC822 object.\r
-        * @var string $address\r
-        */\r
-       private $address = '';\r
-\r
-       /**\r
-        * The default domain to use for unqualified addresses.\r
-        * @var string $default_domain\r
-        */\r
-       private $default_domain = 'localhost';\r
-\r
-       /**\r
-       /**\r
-        * Whether or not to validate atoms for non-ascii characters.\r
-        * @var boolean $validate\r
-        */\r
-       private $validate = TRUE;\r
-\r
-       /**\r
-        * The array of raw addresses built up as we parse.\r
-        * @var array $addresses\r
-        */\r
-       private $addresses = array();\r
-\r
-       /**\r
-        * The final array of parsed address information that we build up.\r
-        * @var array $structure\r
-        */\r
-       private $structure = array();\r
-\r
-       /**\r
-        * The current error message, if any.\r
-        * @var string $error\r
-        */\r
-       private $error = null;\r
-\r
-       /**\r
-        * An internal counter/pointer.\r
-        * @var integer $index\r
-        */\r
-       private $index = null;\r
-\r
-       /**\r
-        * The number of groups that have been found in the address list.\r
-        * @var integer $num_groups\r
-        * @access public\r
-        */\r
-       private $num_groups = 0;\r
-\r
-       /**\r
-        * A limit after which processing stops\r
-        * @var int $limit\r
-        */\r
-       private $limit = null;\r
-\r
-       /**\r
-        * Sets up the object.\r
-        *\r
-        * @access public\r
-        * @param string  $address               The address(es) to validate.\r
-        * @param string  $default_domain  Default domain/host etc. If not supplied, will be set to localhost.\r
-        * @param boolean $validate             Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.\r
-        */\r
-       public function __construct($address = null, $default_domain = null, $validate = null, $limit = null) {\r
-               if (isset($address)) $this->address = $address;\r
-               if (isset($default_domain)) $this->default_domain = $default_domain;\r
-               if (isset($validate)) $this->validate = $validate;\r
-               if (isset($limit)) $this->limit = $limit;\r
-       }\r
-\r
-       /**\r
-        * Starts the whole process. The address must either be set here\r
-        * or when creating the object. One or the other.\r
-        *\r
-        * @access public\r
-        * @param string  $address               The address(es) to validate.\r
-        * @param string  $default_domain  Default domain/host etc.\r
-        * @param boolean $nest_groups   Whether to return the structure with groups nested for easier viewing.\r
-        * @param boolean $validate             Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.\r
-        *\r
-        * @return array A structured array of addresses.\r
-        */\r
-       public function parseAddressList($address = null, $default_domain = null, $validate = null, $limit = null) {\r
-               if (isset($address)) $this->address = $address;\r
-               if (isset($default_domain)) $this->default_domain = $default_domain;\r
-               if (isset($validate)) $this->validate = $validate;\r
-               if (isset($limit)) $this->limit = $limit;\r
-\r
-               $this->structure = array();\r
-               $this->addresses = array();\r
-               $this->error = null;\r
-               $this->index = null;\r
-\r
-               // Unfold any long lines in $this->address.\r
-               $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);\r
-               $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);\r
-\r
-               while ($this->address = $this->_splitAddresses($this->address));\r
-\r
-               if ($this->address === false || isset($this->error)) {\r
-                       throw new Exception($this->error, 1294681466);\r
-               }\r
-\r
-               // Validate each address individually.  If we encounter an invalid\r
-               // address, stop iterating and return an error immediately.\r
-               foreach ($this->addresses as $address) {\r
-                       $valid = $this->_validateAddress($address);\r
-\r
-                       if ($valid === false || isset($this->error)) {\r
-                               throw new Exception($this->error, 1294681467);\r
-                       }\r
-\r
-                       $this->structure = array_merge($this->structure, $valid);\r
-               }\r
-\r
-               return $this->structure;\r
-       }\r
-\r
-       /**\r
-        * Splits an address into separate addresses.\r
-        *\r
-        * @access private\r
-        * @param string $address The addresses to split.\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function _splitAddresses($address) {\r
-               if (!empty($this->limit) && count($this->addresses) == $this->limit) {\r
-                       return '';\r
-               }\r
-\r
-               if ($this->_isGroup($address) && !isset($this->error)) {\r
-                       $split_char = ';';\r
-                       $is_group = true;\r
-               } elseif (!isset($this->error)) {\r
-                       $split_char = ',';\r
-                       $is_group = false;\r
-               } elseif (isset($this->error)) {\r
-                       return false;\r
-               }\r
-\r
-               // Split the string based on the above ten or so lines.\r
-               $parts = explode($split_char, $address);\r
-               $string = $this->_splitCheck($parts, $split_char);\r
-\r
-               // If a group...\r
-               if ($is_group) {\r
-                       // If $string does not contain a colon outside of\r
-                       // brackets/quotes etc then something's fubar.\r
-\r
-                       // First check there's a colon at all:\r
-                       if (strpos($string, ':') === false) {\r
-                               $this->error = 'Invalid address: ' . $string;\r
-                               return false;\r
-                       }\r
-\r
-                       // Now check it's outside of brackets/quotes:\r
-                       if (!$this->_splitCheck(explode(':', $string), ':')) {\r
-                               return false;\r
-                       }\r
-\r
-                       // We must have a group at this point, so increase the counter:\r
-                       $this->num_groups++;\r
-               }\r
-\r
-               // $string now contains the first full address/group.\r
-               // Add to the addresses array.\r
-               $this->addresses[] = array(\r
-                       'address' => trim($string),\r
-                       'group' => $is_group\r
-               );\r
-\r
-               // Remove the now stored address from the initial line, the +1\r
-               // is to account for the explode character.\r
-               $address = trim(substr($address, strlen($string) + 1));\r
-\r
-               // If the next char is a comma and this was a group, then\r
-               // there are more addresses, otherwise, if there are any more\r
-               // chars, then there is another address.\r
-               if ($is_group && substr($address, 0, 1) == ',') {\r
-                       $address = trim(substr($address, 1));\r
-                       return $address;\r
-\r
-               } elseif (strlen($address) > 0) {\r
-                       return $address;\r
-\r
-               } else {\r
-                       return '';\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Checks for a group at the start of the string.\r
-        *\r
-        * @access private\r
-        * @param string $address The address to check.\r
-        * @return boolean Whether or not there is a group at the start of the string.\r
-        */\r
-       protected function _isGroup($address) {\r
-               // First comma not in quotes, angles or escaped:\r
-               $parts = explode(',', $address);\r
-               $string = $this->_splitCheck($parts, ',');\r
-\r
-               // Now we have the first address, we can reliably check for a\r
-               // group by searching for a colon that's not escaped or in\r
-               // quotes or angle brackets.\r
-               if (count($parts = explode(':', $string)) > 1) {\r
-                       $string2 = $this->_splitCheck($parts, ':');\r
-                       return ($string2 !== $string);\r
-               } else {\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * A common function that will check an exploded string.\r
-        *\r
-        * @access private\r
-        * @param array $parts The exloded string.\r
-        * @param string $char  The char that was exploded on.\r
-        * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.\r
-        */\r
-       protected function _splitCheck($parts, $char) {\r
-               $string = $parts[0];\r
-\r
-               for ($i = 0; $i < count($parts); $i++) {\r
-                       if ($this->_hasUnclosedQuotes($string)\r
-                                       || $this->_hasUnclosedBrackets($string, '<>')\r
-                                       || $this->_hasUnclosedBrackets($string, '[]')\r
-                                       || $this->_hasUnclosedBrackets($string, '()')\r
-                                       || substr($string, -1) == '\\') {\r
-                               if (isset($parts[$i + 1])) {\r
-                                       $string = $string . $char . $parts[$i + 1];\r
-                               } else {\r
-                                       $this->error = 'Invalid address spec. Unclosed bracket or quotes';\r
-                                       return false;\r
-                               }\r
-                       } else {\r
-                               $this->index = $i;\r
-                               break;\r
-                       }\r
-               }\r
-\r
-               return $string;\r
-       }\r
-\r
-       /**\r
-        * Checks if a string has unclosed quotes or not.\r
-        *\r
-        * @access private\r
-        * @param string $string  The string to check.\r
-        * @return boolean  True if there are unclosed quotes inside the string,\r
-        *                                false otherwise.\r
-        */\r
-       protected function _hasUnclosedQuotes($string) {\r
-               $string = trim($string);\r
-               $iMax = strlen($string);\r
-               $in_quote = false;\r
-               $i = $slashes = 0;\r
-\r
-               for (; $i < $iMax; ++$i) {\r
-                       switch ($string[$i]) {\r
-                               case '\\':\r
-                                       ++$slashes;\r
-                                       break;\r
-\r
-                               case '"':\r
-                                       if ($slashes % 2 == 0) {\r
-                                               $in_quote = !$in_quote;\r
-                                       }\r
-                               // Fall through to default action below.\r
-\r
-                               default:\r
-                                       $slashes = 0;\r
-                                       break;\r
-                       }\r
-               }\r
-\r
-               return $in_quote;\r
-       }\r
-\r
-       /**\r
-        * Checks if a string has an unclosed brackets or not. IMPORTANT:\r
-        * This function handles both angle brackets and square brackets;\r
-        *\r
-        * @access private\r
-        * @param string $string The string to check.\r
-        * @param string $chars  The characters to check for.\r
-        * @return boolean True if there are unclosed brackets inside the string, false otherwise.\r
-        */\r
-       protected function _hasUnclosedBrackets($string, $chars) {\r
-               $num_angle_start = substr_count($string, $chars[0]);\r
-               $num_angle_end = substr_count($string, $chars[1]);\r
-\r
-               $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);\r
-               $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);\r
-\r
-               if ($num_angle_start < $num_angle_end) {\r
-                       $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';\r
-                       return false;\r
-               } else {\r
-                       return ($num_angle_start > $num_angle_end);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Sub function that is used only by hasUnclosedBrackets().\r
-        *\r
-        * @access private\r
-        * @param string $string The string to check.\r
-        * @param integer &$num The number of occurences.\r
-        * @param string $char   The character to count.\r
-        * @return integer The number of occurences of $char in $string, adjusted for backslashes.\r
-        */\r
-       protected function _hasUnclosedBracketsSub($string, &$num, $char) {\r
-               $parts = explode($char, $string);\r
-               for ($i = 0; $i < count($parts); $i++) {\r
-                       if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))\r
-                               $num--;\r
-                       if (isset($parts[$i + 1]))\r
-                               $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];\r
-               }\r
-\r
-               return $num;\r
-       }\r
-\r
-       /**\r
-        * Function to begin checking the address.\r
-        *\r
-        * @access private\r
-        * @param string $address The address to validate.\r
-        * @return mixed False on failure, or a structured array of address information on success.\r
-        */\r
-       protected function _validateAddress($address) {\r
-               $is_group = false;\r
-               $addresses = array();\r
-\r
-               if ($address['group']) {\r
-                       $is_group = true;\r
-\r
-                       // Get the group part of the name\r
-                       $parts = explode(':', $address['address']);\r
-                       $groupname = $this->_splitCheck($parts, ':');\r
-                       $structure = array();\r
-\r
-                       // And validate the group part of the name.\r
-                       if (!$this->_validatePhrase($groupname)) {\r
-                               $this->error = 'Group name did not validate.';\r
-                               return false;\r
-                       }\r
-\r
-                       $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));\r
-               }\r
-\r
-               // If a group then split on comma and put into an array.\r
-               // Otherwise, Just put the whole address in an array.\r
-               if ($is_group) {\r
-                       while (strlen($address['address']) > 0) {\r
-                               $parts = explode(',', $address['address']);\r
-                               $addresses[] = $this->_splitCheck($parts, ',');\r
-                               $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));\r
-                       }\r
-               } else {\r
-                       $addresses[] = $address['address'];\r
-               }\r
-\r
-               // Check that $addresses is set, if address like this:\r
-               // Groupname:;\r
-               // Then errors were appearing.\r
-               if (!count($addresses)) {\r
-                       $this->error = 'Empty group.';\r
-                       return false;\r
-               }\r
-\r
-               // Trim the whitespace from all of the address strings.\r
-               array_map('trim', $addresses);\r
-\r
-               // Validate each mailbox.\r
-               // Format could be one of: name <geezer@domain.com>\r
-               //                         geezer@domain.com\r
-               //                         geezer\r
-               // ... or any other format valid by RFC 822.\r
-               for ($i = 0; $i < count($addresses); $i++) {\r
-                       if (!$this->validateMailbox($addresses[$i])) {\r
-                               if (empty($this->error)) {\r
-                                       $this->error = 'Validation failed for: ' . $addresses[$i];\r
-                               }\r
-                               return false;\r
-                       }\r
-               }\r
-\r
-               if ($is_group) {\r
-                       $structure = array_merge($structure, $addresses);\r
-               } else {\r
-                       $structure = $addresses;\r
-               }\r
-\r
-               return $structure;\r
-       }\r
-\r
-       /**\r
-        * Function to validate a phrase.\r
-        *\r
-        * @access private\r
-        * @param string $phrase The phrase to check.\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function _validatePhrase($phrase) {\r
-               // Splits on one or more Tab or space.\r
-               $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);\r
-\r
-               $phrase_parts = array();\r
-               while (count($parts) > 0) {\r
-                       $phrase_parts[] = $this->_splitCheck($parts, ' ');\r
-                       for ($i = 0; $i < $this->index + 1; $i++)\r
-                               array_shift($parts);\r
-               }\r
-\r
-               foreach ($phrase_parts as $part) {\r
-                       // If quoted string:\r
-                       if (substr($part, 0, 1) == '"') {\r
-                               if (!$this->_validateQuotedString($part)) {\r
-                                       return false;\r
-                               }\r
-                               continue;\r
-                       }\r
-\r
-                       // Otherwise it's an atom:\r
-                       if (!$this->_validateAtom($part)) return false;\r
-               }\r
-\r
-               return true;\r
-       }\r
-\r
-       /**\r
-        * Function to validate an atom which from rfc822 is:\r
-        * atom = 1*<any CHAR except specials, SPACE and CTLs>\r
-        *\r
-        * If validation ($this->validate) has been turned off, then\r
-        * validateAtom() doesn't actually check anything. This is so that you\r
-        * can split a list of addresses up before encoding personal names\r
-        * (umlauts, etc.), for example.\r
-        *\r
-        * @access private\r
-        * @param string $atom The string to check.\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function _validateAtom($atom) {\r
-               if (!$this->validate) {\r
-                       // Validation has been turned off; assume the atom is okay.\r
-                       return true;\r
-               }\r
-\r
-               // Check for any char from ASCII 0 - ASCII 127\r
-               if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {\r
-                       return false;\r
-               }\r
-\r
-               // Check for specials:\r
-               if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {\r
-                       return false;\r
-               }\r
-\r
-               // Check for control characters (ASCII 0-31):\r
-               if (preg_match('/[\\x00-\\x1F]+/', $atom)) {\r
-                       return false;\r
-               }\r
-\r
-               return true;\r
-       }\r
-\r
-       /**\r
-        * Function to validate quoted string, which is:\r
-        * quoted-string = <"> *(qtext/quoted-pair) <">\r
-        *\r
-        * @access private\r
-        * @param string $qstring The string to check\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function _validateQuotedString($qstring) {\r
-               // Leading and trailing "\r
-               $qstring = substr($qstring, 1, -1);\r
-\r
-               // Perform check, removing quoted characters first.\r
-               return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));\r
-       }\r
-\r
-       /**\r
-        * Function to validate a mailbox, which is:\r
-        * mailbox =   addr-spec                 ; simple address\r
-        *                 / phrase route-addr ; name and route-addr\r
-        *\r
-        * @access public\r
-        * @param string &$mailbox The string to check.\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function validateMailbox(&$mailbox) {\r
-               // A couple of defaults.\r
-               $phrase = '';\r
-               $comment = '';\r
-               $comments = array();\r
-\r
-               // Catch any RFC822 comments and store them separately.\r
-               $_mailbox = $mailbox;\r
-               while (strlen(trim($_mailbox)) > 0) {\r
-                       $parts = explode('(', $_mailbox);\r
-                       $before_comment = $this->_splitCheck($parts, '(');\r
-                       if ($before_comment != $_mailbox) {\r
-                               // First char should be a (.\r
-                               $comment = substr(str_replace($before_comment, '', $_mailbox), 1);\r
-                               $parts = explode(')', $comment);\r
-                               $comment = $this->_splitCheck($parts, ')');\r
-                               $comments[] = $comment;\r
-\r
-                               // +2 is for the brackets\r
-                               $_mailbox = substr($_mailbox, strpos($_mailbox, '(' . $comment) + strlen($comment) + 2);\r
-                       } else {\r
-                               break;\r
-                       }\r
-               }\r
-\r
-               foreach ($comments as $comment) {\r
-                       $mailbox = str_replace("($comment)", '', $mailbox);\r
-               }\r
-\r
-               $mailbox = trim($mailbox);\r
-\r
-               // Check for name + route-addr\r
-               if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {\r
-                       $parts = explode('<', $mailbox);\r
-                       $name = $this->_splitCheck($parts, '<');\r
-\r
-                       $phrase = trim($name);\r
-                       $route_addr = trim(substr($mailbox, strlen($name . '<'), -1));\r
-\r
-                       if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {\r
-                               return false;\r
-                       }\r
-\r
-                       // Only got addr-spec\r
-               } else {\r
-                       // First snip angle brackets if present.\r
-                       if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {\r
-                               $addr_spec = substr($mailbox, 1, -1);\r
-                       } else {\r
-                               $addr_spec = $mailbox;\r
-                       }\r
-\r
-                       if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {\r
-                               return false;\r
-                       }\r
-               }\r
-\r
-               // Construct the object that will be returned.\r
-               $mbox = new stdClass();\r
-\r
-               // Add the phrase (even if empty) and comments\r
-               $mbox->personal = $phrase;\r
-               $mbox->comment = isset($comments) ? $comments : array();\r
-\r
-               if (isset($route_addr)) {\r
-                       $mbox->mailbox = $route_addr['local_part'];\r
-                       $mbox->host = $route_addr['domain'];\r
-                       $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';\r
-               } else {\r
-                       $mbox->mailbox = $addr_spec['local_part'];\r
-                       $mbox->host = $addr_spec['domain'];\r
-               }\r
-\r
-               $mailbox = $mbox;\r
-               return true;\r
-       }\r
-\r
-       /**\r
-        * This function validates a route-addr which is:\r
-        * route-addr = "<" [route] addr-spec ">"\r
-        *\r
-        * Angle brackets have already been removed at the point of\r
-        * getting to this function.\r
-        *\r
-        * @access private\r
-        * @param string $route_addr The string to check.\r
-        * @return mixed False on failure, or an array containing validated address/route information on success.\r
-        */\r
-       protected function _validateRouteAddr($route_addr) {\r
-               // Check for colon.\r
-               if (strpos($route_addr, ':') !== false) {\r
-                       $parts = explode(':', $route_addr);\r
-                       $route = $this->_splitCheck($parts, ':');\r
-               } else {\r
-                       $route = $route_addr;\r
-               }\r
-\r
-               // If $route is same as $route_addr then the colon was in\r
-               // quotes or brackets or, of course, non existent.\r
-               if ($route === $route_addr) {\r
-                       unset($route);\r
-                       $addr_spec = $route_addr;\r
-                       if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {\r
-                               return false;\r
-                       }\r
-               } else {\r
-                       // Validate route part.\r
-                       if (($route = $this->_validateRoute($route)) === false) {\r
-                               return false;\r
-                       }\r
-\r
-                       $addr_spec = substr($route_addr, strlen($route . ':'));\r
-\r
-                       // Validate addr-spec part.\r
-                       if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {\r
-                               return false;\r
-                       }\r
-               }\r
-\r
-               if (isset($route)) {\r
-                       $return['adl'] = $route;\r
-               } else {\r
-                       $return['adl'] = '';\r
-               }\r
-\r
-               $return = array_merge($return, $addr_spec);\r
-               return $return;\r
-       }\r
-\r
-       /**\r
-        * Function to validate a route, which is:\r
-        * route = 1#("@" domain) ":"\r
-        *\r
-        * @access private\r
-        * @param string $route The string to check.\r
-        * @return mixed False on failure, or the validated $route on success.\r
-        */\r
-       protected function _validateRoute($route) {\r
-               // Split on comma.\r
-               $domains = explode(',', trim($route));\r
-\r
-               foreach ($domains as $domain) {\r
-                       $domain = str_replace('@', '', trim($domain));\r
-                       if (!$this->_validateDomain($domain)) return false;\r
-               }\r
-\r
-               return $route;\r
-       }\r
-\r
-       /**\r
-        * Function to validate a domain, though this is not quite what\r
-        * you expect of a strict internet domain.\r
-        *\r
-        * domain = sub-domain *("." sub-domain)\r
-        *\r
-        * @access private\r
-        * @param string $domain The string to check.\r
-        * @return mixed False on failure, or the validated domain on success.\r
-        */\r
-       protected function _validateDomain($domain) {\r
-               // Note the different use of $subdomains and $sub_domains\r
-               $subdomains = explode('.', $domain);\r
-\r
-               while (count($subdomains) > 0) {\r
-                       $sub_domains[] = $this->_splitCheck($subdomains, '.');\r
-                       for ($i = 0; $i < $this->index + 1; $i++)\r
-                               array_shift($subdomains);\r
-               }\r
-\r
-               foreach ($sub_domains as $sub_domain) {\r
-                       if (!$this->_validateSubdomain(trim($sub_domain)))\r
-                               return false;\r
-               }\r
-\r
-               // Managed to get here, so return input.\r
-               return $domain;\r
-       }\r
-\r
-       /**\r
-        * Function to validate a subdomain:\r
-        *   subdomain = domain-ref / domain-literal\r
-        *\r
-        * @access private\r
-        * @param string $subdomain The string to check.\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function _validateSubdomain($subdomain) {\r
-               if (preg_match('|^\[(.*)]$|', $subdomain, $arr)) {\r
-                       if (!$this->_validateDliteral($arr[1])) return false;\r
-               } else {\r
-                       if (!$this->_validateAtom($subdomain)) return false;\r
-               }\r
-\r
-               // Got here, so return successful.\r
-               return true;\r
-       }\r
-\r
-       /**\r
-        * Function to validate a domain literal:\r
-        *   domain-literal =  "[" *(dtext / quoted-pair) "]"\r
-        *\r
-        * @access private\r
-        * @param string $dliteral The string to check.\r
-        * @return boolean Success or failure.\r
-        */\r
-       protected function _validateDliteral($dliteral) {\r
-               return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';\r
-       }\r
-\r
-       /**\r
-        * Function to validate an addr-spec.\r
-        *\r
-        * addr-spec = local-part "@" domain\r
-        *\r
-        * @access private\r
-        * @param string $addr_spec The string to check.\r
-        * @return mixed False on failure, or the validated addr-spec on success.\r
-        */\r
-       protected function _validateAddrSpec($addr_spec) {\r
-               $addr_spec = trim($addr_spec);\r
-\r
-               // Split on @ sign if there is one.\r
-               if (strpos($addr_spec, '@') !== false) {\r
-                       $parts = explode('@', $addr_spec);\r
-                       $local_part = $this->_splitCheck($parts, '@');\r
-                       $domain = substr($addr_spec, strlen($local_part . '@'));\r
-\r
-                       // No @ sign so assume the default domain.\r
-               } else {\r
-                       $local_part = $addr_spec;\r
-                       $domain = $this->default_domain;\r
-               }\r
-\r
-               if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;\r
-               if (($domain = $this->_validateDomain($domain)) === false) return false;\r
-\r
-               // Got here so return successful.\r
-               return array('local_part' => $local_part, 'domain' => $domain);\r
-       }\r
-\r
-       /**\r
-        * Function to validate the local part of an address:\r
-        *   local-part = word *("." word)\r
-        *\r
-        * @access private\r
-        * @param string $local_part\r
-        * @return mixed False on failure, or the validated local part on success.\r
-        */\r
-       protected function _validateLocalPart($local_part) {\r
-               $parts = explode('.', $local_part);\r
-               $words = array();\r
-\r
-               // Split the local_part into words.\r
-               while (count($parts) > 0) {\r
-                       $words[] = $this->_splitCheck($parts, '.');\r
-                       for ($i = 0; $i < $this->index + 1; $i++) {\r
-                               array_shift($parts);\r
-                       }\r
-               }\r
-\r
-               // Validate each word.\r
-               foreach ($words as $word) {\r
-                       // If this word contains an unquoted space, it is invalid. (6.2.4)\r
-                       if (strpos($word, ' ') && $word[0] !== '"') {\r
-                               return false;\r
-                       }\r
-\r
-                       if ($this->_validatePhrase(trim($word)) === false) return false;\r
-               }\r
-\r
-               // Managed to get here, so return the input.\r
-               return $local_part;\r
-       }\r
-}\r
-\r
-?>\r
+<?php
+/**
+ * RFC 822 Email address list validation Utility
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2001-2010, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o 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.
+ * o The names of the authors may not 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
+ * OWNER OR 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.
+ *
+ * @category    Mail
+ * @package     Mail
+ * @author      Richard Heyes <richard@phpguru.org>
+ * @author      Chuck Hagenbuch <chuck@horde.org
+ * @copyright   2001-2010 Richard Heyes
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ *
+ * Incorporated in TYPO3 by Ernesto Baschny <ernst@cron-it.de>
+ */
+
+/**
+ * RFC 822 Email address list validation Utility
+ *
+ * What is it?
+ *
+ * This class will take an address string, and parse it into it's consituent
+ * parts, be that either addresses, groups, or combinations. Nested groups
+ * are not supported. The structure it returns is pretty straight forward,
+ * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
+ * print_r() to view the structure.
+ *
+ * How do I use it?
+ *
+ * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
+ * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
+ * print_r($structure);
+ *
+ * @author  Richard Heyes <richard@phpguru.org>
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @version $Revision: 294749 $
+ * @license BSD
+ * @package Mail
+ */
+class t3lib_mail_Rfc822AddressesParser {
+
+       /**
+        * The address being parsed by the RFC822 object.
+        * @var string $address
+        */
+       private $address = '';
+
+       /**
+        * The default domain to use for unqualified addresses.
+        * @var string $default_domain
+        */
+       private $default_domain = 'localhost';
+
+       /**
+       /**
+        * Whether or not to validate atoms for non-ascii characters.
+        * @var boolean $validate
+        */
+       private $validate = TRUE;
+
+       /**
+        * The array of raw addresses built up as we parse.
+        * @var array $addresses
+        */
+       private $addresses = array();
+
+       /**
+        * The final array of parsed address information that we build up.
+        * @var array $structure
+        */
+       private $structure = array();
+
+       /**
+        * The current error message, if any.
+        * @var string $error
+        */
+       private $error = null;
+
+       /**
+        * An internal counter/pointer.
+        * @var integer $index
+        */
+       private $index = null;
+
+       /**
+        * The number of groups that have been found in the address list.
+        * @var integer $num_groups
+        * @access public
+        */
+       private $num_groups = 0;
+
+       /**
+        * A limit after which processing stops
+        * @var int $limit
+        */
+       private $limit = null;
+
+       /**
+        * Sets up the object.
+        *
+        * @access public
+        * @param string  $address               The address(es) to validate.
+        * @param string  $default_domain  Default domain/host etc. If not supplied, will be set to localhost.
+        * @param boolean $validate             Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
+        */
+       public function __construct($address = null, $default_domain = null, $validate = null, $limit = null) {
+               if (isset($address)) $this->address = $address;
+               if (isset($default_domain)) $this->default_domain = $default_domain;
+               if (isset($validate)) $this->validate = $validate;
+               if (isset($limit)) $this->limit = $limit;
+       }
+
+       /**
+        * Starts the whole process. The address must either be set here
+        * or when creating the object. One or the other.
+        *
+        * @access public
+        * @param string  $address               The address(es) to validate.
+        * @param string  $default_domain  Default domain/host etc.
+        * @param boolean $nest_groups   Whether to return the structure with groups nested for easier viewing.
+        * @param boolean $validate             Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
+        *
+        * @return array A structured array of addresses.
+        */
+       public function parseAddressList($address = null, $default_domain = null, $validate = null, $limit = null) {
+               if (isset($address)) $this->address = $address;
+               if (isset($default_domain)) $this->default_domain = $default_domain;
+               if (isset($validate)) $this->validate = $validate;
+               if (isset($limit)) $this->limit = $limit;
+
+               $this->structure = array();
+               $this->addresses = array();
+               $this->error = null;
+               $this->index = null;
+
+               // Unfold any long lines in $this->address.
+               $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
+               $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
+
+               while ($this->address = $this->_splitAddresses($this->address));
+
+               if ($this->address === false || isset($this->error)) {
+                       throw new Exception($this->error, 1294681466);
+               }
+
+               // Validate each address individually.  If we encounter an invalid
+               // address, stop iterating and return an error immediately.
+               foreach ($this->addresses as $address) {
+                       $valid = $this->_validateAddress($address);
+
+                       if ($valid === false || isset($this->error)) {
+                               throw new Exception($this->error, 1294681467);
+                       }
+
+                       $this->structure = array_merge($this->structure, $valid);
+               }
+
+               return $this->structure;
+       }
+
+       /**
+        * Splits an address into separate addresses.
+        *
+        * @access private
+        * @param string $address The addresses to split.
+        * @return boolean Success or failure.
+        */
+       protected function _splitAddresses($address) {
+               if (!empty($this->limit) && count($this->addresses) == $this->limit) {
+                       return '';
+               }
+
+               if ($this->_isGroup($address) && !isset($this->error)) {
+                       $split_char = ';';
+                       $is_group = true;
+               } elseif (!isset($this->error)) {
+                       $split_char = ',';
+                       $is_group = false;
+               } elseif (isset($this->error)) {
+                       return false;
+               }
+
+               // Split the string based on the above ten or so lines.
+               $parts = explode($split_char, $address);
+               $string = $this->_splitCheck($parts, $split_char);
+
+               // If a group...
+               if ($is_group) {
+                       // If $string does not contain a colon outside of
+                       // brackets/quotes etc then something's fubar.
+
+                       // First check there's a colon at all:
+                       if (strpos($string, ':') === false) {
+                               $this->error = 'Invalid address: ' . $string;
+                               return false;
+                       }
+
+                       // Now check it's outside of brackets/quotes:
+                       if (!$this->_splitCheck(explode(':', $string), ':')) {
+                               return false;
+                       }
+
+                       // We must have a group at this point, so increase the counter:
+                       $this->num_groups++;
+               }
+
+               // $string now contains the first full address/group.
+               // Add to the addresses array.
+               $this->addresses[] = array(
+                       'address' => trim($string),
+                       'group' => $is_group
+               );
+
+               // Remove the now stored address from the initial line, the +1
+               // is to account for the explode character.
+               $address = trim(substr($address, strlen($string) + 1));
+
+               // If the next char is a comma and this was a group, then
+               // there are more addresses, otherwise, if there are any more
+               // chars, then there is another address.
+               if ($is_group && substr($address, 0, 1) == ',') {
+                       $address = trim(substr($address, 1));
+                       return $address;
+
+               } elseif (strlen($address) > 0) {
+                       return $address;
+
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * Checks for a group at the start of the string.
+        *
+        * @access private
+        * @param string $address The address to check.
+        * @return boolean Whether or not there is a group at the start of the string.
+        */
+       protected function _isGroup($address) {
+               // First comma not in quotes, angles or escaped:
+               $parts = explode(',', $address);
+               $string = $this->_splitCheck($parts, ',');
+
+               // Now we have the first address, we can reliably check for a
+               // group by searching for a colon that's not escaped or in
+               // quotes or angle brackets.
+               if (count($parts = explode(':', $string)) > 1) {
+                       $string2 = $this->_splitCheck($parts, ':');
+                       return ($string2 !== $string);
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * A common function that will check an exploded string.
+        *
+        * @access private
+        * @param array $parts The exloded string.
+        * @param string $char  The char that was exploded on.
+        * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
+        */
+       protected function _splitCheck($parts, $char) {
+               $string = $parts[0];
+
+               for ($i = 0; $i < count($parts); $i++) {
+                       if ($this->_hasUnclosedQuotes($string)
+                                       || $this->_hasUnclosedBrackets($string, '<>')
+                                       || $this->_hasUnclosedBrackets($string, '[]')
+                                       || $this->_hasUnclosedBrackets($string, '()')
+                                       || substr($string, -1) == '\\') {
+                               if (isset($parts[$i + 1])) {
+                                       $string = $string . $char . $parts[$i + 1];
+                               } else {
+                                       $this->error = 'Invalid address spec. Unclosed bracket or quotes';
+                                       return false;
+                               }
+                       } else {
+                               $this->index = $i;
+                               break;
+                       }
+               }
+
+               return $string;
+       }
+
+       /**
+        * Checks if a string has unclosed quotes or not.
+        *
+        * @access private
+        * @param string $string  The string to check.
+        * @return boolean  True if there are unclosed quotes inside the string,
+        *                                false otherwise.
+        */
+       protected function _hasUnclosedQuotes($string) {
+               $string = trim($string);
+               $iMax = strlen($string);
+               $in_quote = false;
+               $i = $slashes = 0;
+
+               for (; $i < $iMax; ++$i) {
+                       switch ($string[$i]) {
+                               case '\\':
+                                       ++$slashes;
+                                       break;
+
+                               case '"':
+                                       if ($slashes % 2 == 0) {
+                                               $in_quote = !$in_quote;
+                                       }
+                               // Fall through to default action below.
+
+                               default:
+                                       $slashes = 0;
+                                       break;
+                       }
+               }
+
+               return $in_quote;
+       }
+
+       /**
+        * Checks if a string has an unclosed brackets or not. IMPORTANT:
+        * This function handles both angle brackets and square brackets;
+        *
+        * @access private
+        * @param string $string The string to check.
+        * @param string $chars  The characters to check for.
+        * @return boolean True if there are unclosed brackets inside the string, false otherwise.
+        */
+       protected function _hasUnclosedBrackets($string, $chars) {
+               $num_angle_start = substr_count($string, $chars[0]);
+               $num_angle_end = substr_count($string, $chars[1]);
+
+               $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
+               $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
+
+               if ($num_angle_start < $num_angle_end) {
+                       $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
+                       return false;
+               } else {
+                       return ($num_angle_start > $num_angle_end);
+               }
+       }
+
+       /**
+        * Sub function that is used only by hasUnclosedBrackets().
+        *
+        * @access private
+        * @param string $string The string to check.
+        * @param integer &$num The number of occurences.
+        * @param string $char   The character to count.
+        * @return integer The number of occurences of $char in $string, adjusted for backslashes.
+        */
+       protected function _hasUnclosedBracketsSub($string, &$num, $char) {
+               $parts = explode($char, $string);
+               for ($i = 0; $i < count($parts); $i++) {
+                       if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
+                               $num--;
+                       if (isset($parts[$i + 1]))
+                               $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
+               }
+
+               return $num;
+       }
+
+       /**
+        * Function to begin checking the address.
+        *
+        * @access private
+        * @param string $address The address to validate.
+        * @return mixed False on failure, or a structured array of address information on success.
+        */
+       protected function _validateAddress($address) {
+               $is_group = false;
+               $addresses = array();
+
+               if ($address['group']) {
+                       $is_group = true;
+
+                       // Get the group part of the name
+                       $parts = explode(':', $address['address']);
+                       $groupname = $this->_splitCheck($parts, ':');
+                       $structure = array();
+
+                       // And validate the group part of the name.
+                       if (!$this->_validatePhrase($groupname)) {
+                               $this->error = 'Group name did not validate.';
+                               return false;
+                       }
+
+                       $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
+               }
+
+               // If a group then split on comma and put into an array.
+               // Otherwise, Just put the whole address in an array.
+               if ($is_group) {
+                       while (strlen($address['address']) > 0) {
+                               $parts = explode(',', $address['address']);
+                               $addresses[] = $this->_splitCheck($parts, ',');
+                               $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
+                       }
+               } else {
+                       $addresses[] = $address['address'];
+               }
+
+               // Check that $addresses is set, if address like this:
+               // Groupname:;
+               // Then errors were appearing.
+               if (!count($addresses)) {
+                       $this->error = 'Empty group.';
+                       return false;
+               }
+
+               // Trim the whitespace from all of the address strings.
+               array_map('trim', $addresses);
+
+               // Validate each mailbox.
+               // Format could be one of: name <geezer@domain.com>
+               //                         geezer@domain.com
+               //                         geezer
+               // ... or any other format valid by RFC 822.
+               for ($i = 0; $i < count($addresses); $i++) {
+                       if (!$this->validateMailbox($addresses[$i])) {
+                               if (empty($this->error)) {
+                                       $this->error = 'Validation failed for: ' . $addresses[$i];
+                               }
+                               return false;
+                       }
+               }
+
+               if ($is_group) {
+                       $structure = array_merge($structure, $addresses);
+               } else {
+                       $structure = $addresses;
+               }
+
+               return $structure;
+       }
+
+       /**
+        * Function to validate a phrase.
+        *
+        * @access private
+        * @param string $phrase The phrase to check.
+        * @return boolean Success or failure.
+        */
+       protected function _validatePhrase($phrase) {
+               // Splits on one or more Tab or space.
+               $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
+
+               $phrase_parts = array();
+               while (count($parts) > 0) {
+                       $phrase_parts[] = $this->_splitCheck($parts, ' ');
+                       for ($i = 0; $i < $this->index + 1; $i++)
+                               array_shift($parts);
+               }
+
+               foreach ($phrase_parts as $part) {
+                       // If quoted string:
+                       if (substr($part, 0, 1) == '"') {
+                               if (!$this->_validateQuotedString($part)) {
+                                       return false;
+                               }
+                               continue;
+                       }
+
+                       // Otherwise it's an atom:
+                       if (!$this->_validateAtom($part)) return false;
+               }
+
+               return true;
+       }
+
+       /**
+        * Function to validate an atom which from rfc822 is:
+        * atom = 1*<any CHAR except specials, SPACE and CTLs>
+        *
+        * If validation ($this->validate) has been turned off, then
+        * validateAtom() doesn't actually check anything. This is so that you
+        * can split a list of addresses up before encoding personal names
+        * (umlauts, etc.), for example.
+        *
+        * @access private
+        * @param string $atom The string to check.
+        * @return boolean Success or failure.
+        */
+       protected function _validateAtom($atom) {
+               if (!$this->validate) {
+                       // Validation has been turned off; assume the atom is okay.
+                       return true;
+               }
+
+               // Check for any char from ASCII 0 - ASCII 127
+               if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
+                       return false;
+               }
+
+               // Check for specials:
+               if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
+                       return false;
+               }
+
+               // Check for control characters (ASCII 0-31):
+               if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
+                       return false;
+               }
+
+               return true;
+       }
+
+       /**
+        * Function to validate quoted string, which is:
+        * quoted-string = <"> *(qtext/quoted-pair) <">
+        *
+        * @access private
+        * @param string $qstring The string to check
+        * @return boolean Success or failure.
+        */
+       protected function _validateQuotedString($qstring) {
+               // Leading and trailing "
+               $qstring = substr($qstring, 1, -1);
+
+               // Perform check, removing quoted characters first.
+               return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
+       }
+
+       /**
+        * Function to validate a mailbox, which is:
+        * mailbox =   addr-spec                 ; simple address
+        *                 / phrase route-addr ; name and route-addr
+        *
+        * @access public
+        * @param string &$mailbox The string to check.
+        * @return boolean Success or failure.
+        */
+       protected function validateMailbox(&$mailbox) {
+               // A couple of defaults.
+               $phrase = '';
+               $comment = '';
+               $comments = array();
+
+               // Catch any RFC822 comments and store them separately.
+               $_mailbox = $mailbox;
+               while (strlen(trim($_mailbox)) > 0) {
+                       $parts = explode('(', $_mailbox);
+                       $before_comment = $this->_splitCheck($parts, '(');
+                       if ($before_comment != $_mailbox) {
+                               // First char should be a (.
+                               $comment = substr(str_replace($before_comment, '', $_mailbox), 1);
+                               $parts = explode(')', $comment);
+                               $comment = $this->_splitCheck($parts, ')');
+                               $comments[] = $comment;
+
+                               // +2 is for the brackets
+                               $_mailbox = substr($_mailbox, strpos($_mailbox, '(' . $comment) + strlen($comment) + 2);
+                       } else {
+                               break;
+                       }
+               }
+
+               foreach ($comments as $comment) {
+                       $mailbox = str_replace("($comment)", '', $mailbox);
+               }
+
+               $mailbox = trim($mailbox);
+
+               // Check for name + route-addr
+               if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
+                       $parts = explode('<', $mailbox);
+                       $name = $this->_splitCheck($parts, '<');
+
+                       $phrase = trim($name);
+                       $route_addr = trim(substr($mailbox, strlen($name . '<'), -1));
+
+                       if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
+                               return false;
+                       }
+
+                       // Only got addr-spec
+               } else {
+                       // First snip angle brackets if present.
+                       if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
+                               $addr_spec = substr($mailbox, 1, -1);
+                       } else {
+                               $addr_spec = $mailbox;
+                       }
+
+                       if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                               return false;
+                       }
+               }
+
+               // Construct the object that will be returned.
+               $mbox = new stdClass();
+
+               // Add the phrase (even if empty) and comments
+               $mbox->personal = $phrase;
+               $mbox->comment = isset($comments) ? $comments : array();
+
+               if (isset($route_addr)) {
+                       $mbox->mailbox = $route_addr['local_part'];
+                       $mbox->host = $route_addr['domain'];
+                       $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
+               } else {
+                       $mbox->mailbox = $addr_spec['local_part'];
+                       $mbox->host = $addr_spec['domain'];
+               }
+
+               $mailbox = $mbox;
+               return true;
+       }
+
+       /**
+        * This function validates a route-addr which is:
+        * route-addr = "<" [route] addr-spec ">"
+        *
+        * Angle brackets have already been removed at the point of
+        * getting to this function.
+        *
+        * @access private
+        * @param string $route_addr The string to check.
+        * @return mixed False on failure, or an array containing validated address/route information on success.
+        */
+       protected function _validateRouteAddr($route_addr) {
+               // Check for colon.
+               if (strpos($route_addr, ':') !== false) {
+                       $parts = explode(':', $route_addr);
+                       $route = $this->_splitCheck($parts, ':');
+               } else {
+                       $route = $route_addr;
+               }
+
+               // If $route is same as $route_addr then the colon was in
+               // quotes or brackets or, of course, non existent.
+               if ($route === $route_addr) {
+                       unset($route);
+                       $addr_spec = $route_addr;
+                       if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                               return false;
+                       }
+               } else {
+                       // Validate route part.
+                       if (($route = $this->_validateRoute($route)) === false) {
+                               return false;
+                       }
+
+                       $addr_spec = substr($route_addr, strlen($route . ':'));
+
+                       // Validate addr-spec part.
+                       if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
+                               return false;
+                       }
+               }
+
+               if (isset($route)) {
+                       $return['adl'] = $route;
+               } else {
+                       $return['adl'] = '';
+               }
+
+               $return = array_merge($return, $addr_spec);
+               return $return;
+       }
+
+       /**
+        * Function to validate a route, which is:
+        * route = 1#("@" domain) ":"
+        *
+        * @access private
+        * @param string $route The string to check.
+        * @return mixed False on failure, or the validated $route on success.
+        */
+       protected function _validateRoute($route) {
+               // Split on comma.
+               $domains = explode(',', trim($route));
+
+               foreach ($domains as $domain) {
+                       $domain = str_replace('@', '', trim($domain));
+                       if (!$this->_validateDomain($domain)) return false;
+               }
+
+               return $route;
+       }
+
+       /**
+        * Function to validate a domain, though this is not quite what
+        * you expect of a strict internet domain.
+        *
+        * domain = sub-domain *("." sub-domain)
+        *
+        * @access private
+        * @param string $domain The string to check.
+        * @return mixed False on failure, or the validated domain on success.
+        */
+       protected function _validateDomain($domain) {
+               // Note the different use of $subdomains and $sub_domains
+               $subdomains = explode('.', $domain);
+
+               while (count($subdomains) > 0) {
+                       $sub_domains[] = $this->_splitCheck($subdomains, '.');
+                       for ($i = 0; $i < $this->index + 1; $i++)
+                               array_shift($subdomains);
+               }
+
+               foreach ($sub_domains as $sub_domain) {
+                       if (!$this->_validateSubdomain(trim($sub_domain)))
+                               return false;
+               }
+
+               // Managed to get here, so return input.
+               return $domain;
+       }
+
+       /**
+        * Function to validate a subdomain:
+        *   subdomain = domain-ref / domain-literal
+        *
+        * @access private
+        * @param string $subdomain The string to check.
+        * @return boolean Success or failure.
+        */
+       protected function _validateSubdomain($subdomain) {
+               if (preg_match('|^\[(.*)]$|', $subdomain, $arr)) {
+                       if (!$this->_validateDliteral($arr[1])) return false;
+               } else {
+                       if (!$this->_validateAtom($subdomain)) return false;
+               }
+
+               // Got here, so return successful.
+               return true;
+       }
+
+       /**
+        * Function to validate a domain literal:
+        *   domain-literal =  "[" *(dtext / quoted-pair) "]"
+        *
+        * @access private
+        * @param string $dliteral The string to check.
+        * @return boolean Success or failure.
+        */
+       protected function _validateDliteral($dliteral) {
+               return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
+       }
+
+       /**
+        * Function to validate an addr-spec.
+        *
+        * addr-spec = local-part "@" domain
+        *
+        * @access private
+        * @param string $addr_spec The string to check.
+        * @return mixed False on failure, or the validated addr-spec on success.
+        */
+       protected function _validateAddrSpec($addr_spec) {
+               $addr_spec = trim($addr_spec);
+
+               // Split on @ sign if there is one.
+               if (strpos($addr_spec, '@') !== false) {
+                       $parts = explode('@', $addr_spec);
+                       $local_part = $this->_splitCheck($parts, '@');
+                       $domain = substr($addr_spec, strlen($local_part . '@'));
+
+                       // No @ sign so assume the default domain.
+               } else {
+                       $local_part = $addr_spec;
+                       $domain = $this->default_domain;
+               }
+
+               if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
+               if (($domain = $this->_validateDomain($domain)) === false) return false;
+
+               // Got here so return successful.
+               return array('local_part' => $local_part, 'domain' => $domain);
+       }
+
+       /**
+        * Function to validate the local part of an address:
+        *   local-part = word *("." word)
+        *
+        * @access private
+        * @param string $local_part
+        * @return mixed False on failure, or the validated local part on success.
+        */
+       protected function _validateLocalPart($local_part) {
+               $parts = explode('.', $local_part);
+               $words = array();
+
+               // Split the local_part into words.
+               while (count($parts) > 0) {
+                       $words[] = $this->_splitCheck($parts, '.');
+                       for ($i = 0; $i < $this->index + 1; $i++) {
+                               array_shift($parts);
+                       }
+               }
+
+               // Validate each word.
+               foreach ($words as $word) {
+                       // If this word contains an unquoted space, it is invalid. (6.2.4)
+                       if (strpos($word, ' ') && $word[0] !== '"') {
+                               return false;
+                       }
+
+                       if ($this->_validatePhrase(trim($word)) === false) return false;
+               }
+
+               // Managed to get here, so return the input.
+               return $local_part;
+       }
+}
+
+?>
index 1de4197..843b4ee 100644 (file)
-<?php\r
-/***************************************************************\r
- *  Copyright notice\r
- *\r
- *  (c) 2010 Jigal van Hemert <jigal@xs4all.nl>\r
- *  All rights reserved\r
- *\r
- *  This script is part of the TYPO3 project. The TYPO3 project is\r
- *  free software; you can redistribute it and/or modify\r
- *  it under the terms of the GNU General Public License as published by\r
- *  the Free Software Foundation; either version 2 of the License, or\r
- *  (at your option) any later version.\r
- *\r
- *  The GNU General Public License can be found at\r
- *  http://www.gnu.org/copyleft/gpl.html.\r
- *  A copy is found in the textfile GPL.txt and important notices to the license\r
- *  from the author is found in LICENSE.txt distributed with these scripts.\r
- *\r
- *\r
- *  This script is distributed in the hope that it will be useful,\r
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *  GNU General Public License for more details.\r
- *\r
- *  This copyright notice MUST APPEAR in all copies of the script!\r
- ***************************************************************/\r
-\r
-/**\r
- * Hook subscriber for using Swift Mailer with the t3lib_utility_mail function\r
- *\r
- * $Id$\r
- *\r
- * @author     Jigal van Hemert <jigal@xs4all.nl>\r
- * @package TYPO3\r
- * @subpackage t3lib\r
- */\r
-class t3lib_mail_SwiftMailerAdapter implements t3lib_mail_MailerAdapter {\r
-\r
-       /** @var $mailer t3lib_mail_Mailer */\r
-       protected $mailer;\r
-\r
-       /** @var $message Swift_Message */\r
-       protected $message;\r
-\r
-       /** @var $messageHeaders Swift_Mime_HeaderSet */\r
-       protected $messageHeaders;\r
-\r
-       /** @var string */\r
-       protected $boundary = '';\r
-\r
-       /**\r
-        * Constructor\r
-        *\r
-        * @return void\r
-        */\r
-       public function __construct() {\r
-                       // create mailer object\r
-               $this->mailer = t3lib_div::makeInstance('t3lib_mail_Mailer');\r
-                       // create message object\r
-               $this->message = Swift_Message::newInstance();\r
-       }\r
-\r
-       /**\r
-        * Parses parts of the mail message and sends it with the Swift Mailer functions\r
-        *\r
-        * @param string $to Email address to send the message to\r
-        * @param string $subject Subject of mail message\r
-        * @param string $messageBody Raw body (may be multipart)\r
-        * @param array $additionalHeaders Additional mail headers\r
-        * @param array $additionalParameters Extra parameters for the mail() command\r
-        * @param bool $fakeSending If set fake sending a mail\r
-        * @throws t3lib_exception\r
-        * @return bool\r
-        */\r
-       public function mail($to, $subject, $messageBody, $additionalHeaders = NULL, $additionalParameters = NULL, $fakeSending = FALSE) {\r
-\r
-                       // report success for fake sending\r
-               if ($fakeSending === TRUE) {\r
-                       return TRUE;\r
-               }\r
-               $this->message->setSubject($subject);\r
-               $this->message->setTo($to);\r
-                       // handle additional headers\r
-               $headers = t3lib_div::trimExplode(LF, $additionalHeaders, TRUE);\r
-               $this->messageHeaders = $this->message->getHeaders();\r
-               foreach ($headers as $header) {\r
-                       list($headerName, $headerValue) = t3lib_div::trimExplode(':', $header, FALSE, 2);\r
-                       $this->setHeader($headerName, $headerValue);\r
-               }\r
-                       // handle additional parameters (force return path)\r
-               if (preg_match('/-f\s*(\S*?)/', $additionalParameters, $matches)) {\r
-                       $this->message->setReturnPath($this->unescapeShellArguments($matches[1]));\r
-               }\r
-                       // handle from:\r
-               $this->fixSender();\r
-                       // handle message body\r
-               $this->setBody($messageBody);\r
-                       // send mail\r
-               $result = $this->mailer->send($this->message);\r
-\r
-                       // report success/failure\r
-               return (bool) $result;\r
-       }\r
-\r
-       /**\r
-        * Tries to undo the action by escapeshellarg()\r
-        *\r
-        * @param  $escapedString String escaped by escapeshellarg()\r
-        * @return string       String with escapeshellarg() action undone as best as possible\r
-        */\r
-       protected function unescapeShellArguments($escapedString) {\r
-               if (TYPO3_OS === 'WIN') {\r
-                               // on Windows double quotes are used and % signs are replaced by spaces\r
-                       if (preg_match('/^"([^"]*)"$/', trim($escapedString), $matches)) {\r
-                               $result = str_replace('\"', '"', $matches[1]);\r
-                                       // % signs are replaced with spaces, so they can't be recovered\r
-                       }\r
-               } else {\r
-                               // on Unix-like systems single quotes are escaped\r
-                       if (preg_match('/^\'([^' . preg_quote('\'') . ']*)\'$/', trim($escapedString), $matches)) {\r
-                               $result = str_replace('\\\'', '\'', $matches[1]);\r
-                       }\r
-               }\r
-               return $result;\r
-       }\r
-\r
-       /**\r
-        * Handles setting and replacing of mail headers\r
-        *\r
-        * @param  $headerName Name of header\r
-        * @param  $headerValue Value of header\r
-        * @return void\r
-        */\r
-       protected function setHeader($headerName, $headerValue) {\r
-                       // check for boundary in headers\r
-               if (preg_match('/^boundary="(.*)"$/', $headerName, $matches) > 0) {\r
-                       $this->boundary = $matches[1];\r
-                       return;\r
-               }\r
-                       // process other, real headers\r
-               if ($this->messageHeaders->has($headerName)) {\r
-                       $header = $this->messageHeaders->get($headerName);\r
-                       $headerType = $header->getFieldType();\r
-                       switch ($headerType) {\r
-                               case Swift_Mime_Header::TYPE_TEXT:\r
-                                       $header->setValue($headerValue);\r
-                                       break;\r
-                               case Swift_Mime_Header::TYPE_PARAMETERIZED:\r
-                                       $header->setValue(rtrim($headerValue, ';'));\r
-                                       break;\r
-                               case Swift_Mime_Header::TYPE_MAILBOX:\r
-                                       $addressList = $this->parseAddresses($headerValue);\r
-                                       if (count($addressList) > 0) {\r
-                                               $header->setNameAddresses($addressList);\r
-                                       }\r
-                                       break;\r
-                               case Swift_Mime_Header::TYPE_DATE:\r
-                                       $header->setTimeStamp(strtotime($headerValue));\r
-                                       break;\r
-                               case Swift_Mime_Header::TYPE_ID:\r
-                                               // remove '<' and '>' from ID headers\r
-                                       $header->setId(trim($headerValue, '<>'));\r
-                                       break;\r
-                               case Swift_Mime_Header::TYPE_PATH:\r
-                                       $header->setAddress($headerValue);\r
-                                       break;\r
-                       }\r
-                               // change value\r
-               } else {\r
-                       switch ($headerName) {\r
-                                       // mailbox headers\r
-                               case 'From':\r
-                               case 'To':\r
-                               case 'Cc':\r
-                               case 'Bcc':\r
-                               case 'Reply-To':\r
-                               case 'Sender':\r
-                                       $addressList = $this->parseAddresses($headerValue);\r
-                                       if (count($addressList) > 0) {\r
-                                               $this->messageHeaders->addMailboxHeader($headerName, $addressList);\r
-                                       }\r
-                                       break;\r
-                                       // date headers\r
-                               case 'Date':\r
-                                       $this->messageHeaders->addDateHeader($headerName, strtotime($headerValue));\r
-                                       break;\r
-                                       // ID headers\r
-                               case 'Message-ID':\r
-                                               // remove '<' and '>' from ID headers\r
-                                       $this->messageHeaders->addIdHeader($headerName, trim($headerValue, '<>'));\r
-                                       // path headers\r
-                               case 'Return-Path':\r
-                                       $this->messageHeaders->addPathHeader($headerName, $headerValue);\r
-                                       break;\r
-                                       // parameterized headers\r
-                               case 'Content-Type':\r
-                               case 'Content-Disposition':\r
-                                       $this->messageHeaders->addParameterizedHeader($headerName, rtrim($headerValue, ';'));\r
-                                       break;\r
-                                       // text headers\r
-                               default:\r
-                                       $this->messageHeaders->addTextheader($headerName, $headerValue);\r
-                                       break;\r
-                       }\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Sets body of mail message. Handles multi-part and single part messages. Encoded body parts are decoded prior to adding\r
-        * them to the message object.\r
-        *\r
-        * @param string $body Raw body, may be multi-part\r
-        * @return void\r
-        */\r
-       protected function setBody($body) {\r
-               if ($this->boundary) {\r
-                               // handle multi-part\r
-                       $bodyParts = preg_split('/--' . preg_quote($this->boundary) . '(--)?/m', $body, NULL, PREG_SPLIT_NO_EMPTY);\r
-                       foreach ($bodyParts as $bodyPart) {\r
-                                       // skip empty parts\r
-                               if (trim($bodyPart) == '') {\r
-                                       continue;\r
-                               }\r
-                                       // keep leading white space when exploding the text\r
-                       $lines = explode(LF, $bodyPart);\r
-                               // set defaults for this part\r
-                               $encoding = '';\r
-                               $charset = 'utf-8';\r
-                               $contentType = 'text/plain';\r
-                                       // skip intro messages\r
-                               if (trim($lines[0]) == 'This is a multi-part message in MIME format.') {\r
-                                       continue;\r
-                               }\r
-                                       // first line is empty leftover from splitting\r
-                               array_shift($lines);\r
-                               while (count($lines) > 0) {\r
-                                       $line = array_shift($lines);\r
-                                       if (preg_match('/^content-type:(.*);( charset=(.*))?$/i', $line, $matches)) {\r
-                                               $contentType = trim($matches[1]);\r
-                                               if ($matches[2]) {\r
-                                                       $charset = trim($matches[3]);\r
-                                               }\r
-                                       } else if (preg_match('/^content-transfer-encoding:(.*)$/i', $line, $matches)) {\r
-                                               $encoding = trim($matches[1]);\r
-                                       } else if (strlen(trim($line)) == 0) {\r
-                                                       // empty line before actual content of this part\r
-                                               break;\r
-                                       }\r
-                               }\r
-                                       // use rest of part as body, but reverse encoding first\r
-                               $bodyPart = $this->decode(implode(LF, $lines), $encoding);\r
-                               $this->message->addPart($bodyPart, $contentType, $charset);\r
-                       }\r
-               } else {\r
-                               // Handle single body\r
-                               // The headers have already been set, so use header information\r
-                       $contentType = $this->message->getContentType();\r
-                       $charset = $this->message->getCharset();\r
-                       $encoding = $this->message->getEncoder();\r
-                               // reverse encoding and set body\r
-                       $rawBody = $this->decode($body, $encoding);\r
-                       $this->message->setBody($rawBody, $contentType, $charset);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Reverts encoding of body text\r
-        *\r
-        * @param string $text Body text to be decoded\r
-        * @param string $encoding Encoding type to be reverted\r
-        * @return string Decoded message body\r
-        */\r
-       protected function decode($text, $encoding) {\r
-               $result = $text;\r
-               switch ($encoding) {\r
-                       case 'quoted-printable':\r
-                               $result = quoted_printable_decode($text);\r
-                               break;\r
-                       case 'base64':\r
-                               $result = base64_decode($text);\r
-                               break;\r
-               }\r
-               return $result;\r
-       }\r
-\r
-       /**\r
-        * Parses mailbox headers and turns them into an array.\r
-        *\r
-        * Mailbox headers are a comma separated list of 'name <email@example.org' combinations or plain email addresses (or a mix\r
-        * of these).\r
-        * The resulting array has key-value pairs where the key is either a number (no name in the mailbox header) or a display\r
-        * name and the value is the email address.\r
-        *\r
-        * @param string $rawAddresses Comma separated list of email addresses (optionally with display name)\r
-        * @return array Parsed list of addresses.\r
-        */\r
-       protected function parseAddresses($rawAddresses = '') {\r
-                       /** @var $addressParser t3lib_mail_Rfc822AddressesParser */\r
-               $addressParser = t3lib_div::makeInstance('t3lib_mail_Rfc822AddressesParser', $rawAddresses);\r
-               $addresses = $addressParser->parseAddressList();\r
-               $addressList = array();\r
-               foreach ($addresses as $address) {\r
-                       if ($address->personal) {\r
-                               // item with name found ( name <email@example.org> )\r
-                               $addressList[$address->mailbox . '@' . $address->host] = $address->personal;\r
-                       } else {\r
-                               // item without name found ( email@example.org )\r
-                               $addressList[] = $address->mailbox . '@' . $address->host;\r
-                       }\r
-               }\r
-               return $addressList;\r
-       }\r
-\r
-       /**\r
-        * Makes sure there is a correct sender set.\r
-        *\r
-        * If there is no from header the returnpath will be used. If that also fails a fake address will be used to make sure\r
-        * Swift Mailer will be able to send the message. Some SMTP server will not accept mail messages without a valid sender.\r
-        *\r
-        * @return void\r
-        */\r
-       protected function fixSender() {\r
-               $from = $this->message->getFrom();\r
-               if (count($from) > 0) {\r
-                       reset($from);\r
-                       list($fromAddress, $fromName) = each($from);\r
-               } else {\r
-                       $fromAddress = $this->message->getReturnPath();\r
-                       $fromName = $fromAddress;\r
-               }\r
-               if (strlen($fromAddress) == 0) {\r
-                       $fromAddress = 'no-reply@example.org';\r
-                       $fromName = 'TYPO3 CMS';\r
-               }\r
-               $this->message->setFrom(array($fromAddress => $fromName));\r
-       }\r
-}\r
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Jigal van Hemert <jigal@xs4all.nl>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Hook subscriber for using Swift Mailer with the t3lib_utility_mail function
+ *
+ * $Id$
+ *
+ * @author     Jigal van Hemert <jigal@xs4all.nl>
+ * @package TYPO3
+ * @subpackage t3lib
+ */
+class t3lib_mail_SwiftMailerAdapter implements t3lib_mail_MailerAdapter {
+
+       /** @var $mailer t3lib_mail_Mailer */
+       protected $mailer;
+
+       /** @var $message Swift_Message */
+       protected $message;
+
+       /** @var $messageHeaders Swift_Mime_HeaderSet */
+       protected $messageHeaders;
+
+       /** @var string */
+       protected $boundary = '';
+
+       /**
+        * Constructor
+        *
+        * @return void
+        */
+       public function __construct() {
+                       // create mailer object
+               $this->mailer = t3lib_div::makeInstance('t3lib_mail_Mailer');
+                       // create message object
+               $this->message = Swift_Message::newInstance();
+       }
+
+       /**
+        * Parses parts of the mail message and sends it with the Swift Mailer functions
+        *
+        * @param string $to Email address to send the message to
+        * @param string $subject Subject of mail message
+        * @param string $messageBody Raw body (may be multipart)
+        * @param array $additionalHeaders Additional mail headers
+        * @param array $additionalParameters Extra parameters for the mail() command
+        * @param bool $fakeSending If set fake sending a mail
+        * @throws t3lib_exception
+        * @return bool
+        */
+       public function mail($to, $subject, $messageBody, $additionalHeaders = NULL, $additionalParameters = NULL, $fakeSending = FALSE) {
+
+                       // report success for fake sending
+               if ($fakeSending === TRUE) {
+                       return TRUE;
+               }
+               $this->message->setSubject($subject);
+               $this->message->setTo($to);
+                       // handle additional headers
+               $headers = t3lib_div::trimExplode(LF, $additionalHeaders, TRUE);
+               $this->messageHeaders = $this->message->getHeaders();
+               foreach ($headers as $header) {
+                       list($headerName, $headerValue) = t3lib_div::trimExplode(':', $header, FALSE, 2);
+                       $this->setHeader($headerName, $headerValue);
+               }
+                       // handle additional parameters (force return path)
+               if (preg_match('/-f\s*(\S*?)/', $additionalParameters, $matches)) {
+                       $this->message->setReturnPath($this->unescapeShellArguments($matches[1]));
+               }
+                       // handle from:
+               $this->fixSender();
+                       // handle message body
+               $this->setBody($messageBody);
+                       // send mail
+               $result = $this->mailer->send($this->message);
+
+                       // report success/failure
+               return (bool) $result;
+       }
+
+       /**
+        * Tries to undo the action by escapeshellarg()
+        *
+        * @param  $escapedString String escaped by escapeshellarg()
+        * @return string       String with escapeshellarg() action undone as best as possible
+        */
+       protected function unescapeShellArguments($escapedString) {
+               if (TYPO3_OS === 'WIN') {
+                               // on Windows double quotes are used and % signs are replaced by spaces
+                       if (preg_match('/^"([^"]*)"$/', trim($escapedString), $matches)) {
+                               $result = str_replace('\"', '"', $matches[1]);
+                                       // % signs are replaced with spaces, so they can't be recovered
+                       }
+               } else {
+                               // on Unix-like systems single quotes are escaped
+                       if (preg_match('/^\'([^' . preg_quote('\'') . ']*)\'$/', trim($escapedString), $matches)) {
+                               $result = str_replace('\\\'', '\'', $matches[1]);
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Handles setting and replacing of mail headers
+        *
+        * @param  $headerName Name of header
+        * @param  $headerValue Value of header
+        * @return void
+        */
+       protected function setHeader($headerName, $headerValue) {
+                       // check for boundary in headers
+               if (preg_match('/^boundary="(.*)"$/', $headerName, $matches) > 0) {
+                       $this->boundary = $matches[1];
+                       return;
+               }
+                       // process other, real headers
+               if ($this->messageHeaders->has($headerName)) {
+                       $header = $this->messageHeaders->get($headerName);
+                       $headerType = $header->getFieldType();
+                       switch ($headerType) {
+                               case Swift_Mime_Header::TYPE_TEXT:
+                                       $header->setValue($headerValue);
+                                       break;
+                               case Swift_Mime_Header::TYPE_PARAMETERIZED:
+                                       $header->setValue(rtrim($headerValue, ';'));
+                                       break;
+                               case Swift_Mime_Header::TYPE_MAILBOX:
+                                       $addressList = $this->parseAddresses($headerValue);
+                                       if (count($addressList) > 0) {
+                                               $header->setNameAddresses($addressList);
+                                       }
+                                       break;
+                               case Swift_Mime_Header::TYPE_DATE:
+                                       $header->setTimeStamp(strtotime($headerValue));
+                                       break;
+                               case Swift_Mime_Header::TYPE_ID:
+                                               // remove '<' and '>' from ID headers
+                                       $header->setId(trim($headerValue, '<>'));
+                                       break;
+                               case Swift_Mime_Header::TYPE_PATH:
+                                       $header->setAddress($headerValue);
+                                       break;
+                       }
+                               // change value
+               } else {
+                       switch ($headerName) {
+                                       // mailbox headers
+                               case 'From':
+                               case 'To':
+                               case 'Cc':
+                               case 'Bcc':
+                               case 'Reply-To':
+                               case 'Sender':
+                                       $addressList = $this->parseAddresses($headerValue);
+                                       if (count($addressList) > 0) {
+                                               $this->messageHeaders->addMailboxHeader($headerName, $addressList);
+                                       }
+                                       break;
+                                       // date headers
+                               case 'Date':
+                                       $this->messageHeaders->addDateHeader($headerName, strtotime($headerValue));
+                                       break;
+                                       // ID headers
+                               case 'Message-ID':
+                                               // remove '<' and '>' from ID headers
+                                       $this->messageHeaders->addIdHeader($headerName, trim($headerValue, '<>'));
+                                       // path headers
+                               case 'Return-Path':
+                                       $this->messageHeaders->addPathHeader($headerName, $headerValue);
+                                       break;
+                                       // parameterized headers
+                               case 'Content-Type':
+                               case 'Content-Disposition':
+                                       $this->messageHeaders->addParameterizedHeader($headerName, rtrim($headerValue, ';'));
+                                       break;
+                                       // text headers
+                               default:
+                                       $this->messageHeaders->addTextheader($headerName, $headerValue);
+                                       break;
+                       }
+               }
+       }
+
+       /**
+        * Sets body of mail message. Handles multi-part and single part messages. Encoded body parts are decoded prior to adding
+        * them to the message object.
+        *
+        * @param string $body Raw body, may be multi-part
+        * @return void
+        */
+       protected function setBody($body) {
+               if ($this->boundary) {
+                               // handle multi-part
+                       $bodyParts = preg_split('/--' . preg_quote($this->boundary) . '(--)?/m', $body, NULL, PREG_SPLIT_NO_EMPTY);
+                       foreach ($bodyParts as $bodyPart) {
+                                       // skip empty parts
+                               if (trim($bodyPart) == '') {
+                                       continue;
+                               }
+                                       // keep leading white space when exploding the text
+                       $lines = explode(LF, $bodyPart);
+                               // set defaults for this part
+                               $encoding = '';
+                               $charset = 'utf-8';
+                               $contentType = 'text/plain';
+                                       // skip intro messages
+                               if (trim($lines[0]) == 'This is a multi-part message in MIME format.') {
+                                       continue;
+                               }
+                                       // first line is empty leftover from splitting
+                               array_shift($lines);
+                               while (count($lines) > 0) {
+                                       $line = array_shift($lines);
+                                       if (preg_match('/^content-type:(.*);( charset=(.*))?$/i', $line, $matches)) {
+                                               $contentType = trim($matches[1]);
+                                               if ($matches[2]) {
+                                                       $charset = trim($matches[3]);
+                                               }
+                                       } else if (preg_match('/^content-transfer-encoding:(.*)$/i', $line, $matches)) {
+                                               $encoding = trim($matches[1]);
+                                       } else if (strlen(trim($line)) == 0) {
+                                                       // empty line before actual content of this part
+                                               break;
+                                       }
+                               }
+                                       // use rest of part as body, but reverse encoding first
+                               $bodyPart = $this->decode(implode(LF, $lines), $encoding);
+                               $this->message->addPart($bodyPart, $contentType, $charset);
+                       }
+               } else {
+                               // Handle single body
+                               // The headers have already been set, so use header information
+                       $contentType = $this->message->getContentType();
+                       $charset = $this->message->getCharset();
+                       $encoding = $this->message->getEncoder();
+                               // reverse encoding and set body
+                       $rawBody = $this->decode($body, $encoding);
+                       $this->message->setBody($rawBody, $contentType, $charset);
+               }
+       }
+
+       /**
+        * Reverts encoding of body text
+        *
+        * @param string $text Body text to be decoded
+        * @param string $encoding Encoding type to be reverted
+        * @return string Decoded message body
+        */
+       protected function decode($text, $encoding) {
+               $result = $text;
+               switch ($encoding) {
+                       case 'quoted-printable':
+                               $result = quoted_printable_decode($text);
+                               break;
+                       case 'base64':
+                               $result = base64_decode($text);
+                               break;
+               }
+               return $result;
+       }
+
+       /**
+        * Parses mailbox headers and turns them into an array.
+        *
+        * Mailbox headers are a comma separated list of 'name <email@example.org' combinations or plain email addresses (or a mix
+        * of these).
+        * The resulting array has key-value pairs where the key is either a number (no name in the mailbox header) or a display
+        * name and the value is the email address.
+        *
+        * @param string $rawAddresses Comma separated list of email addresses (optionally with display name)
+        * @return array Parsed list of addresses.
+        */
+       protected function parseAddresses($rawAddresses = '') {
+                       /** @var $addressParser t3lib_mail_Rfc822AddressesParser */
+               $addressParser = t3lib_div::makeInstance('t3lib_mail_Rfc822AddressesParser', $rawAddresses);
+               $addresses = $addressParser->parseAddressList();
+               $addressList = array();
+               foreach ($addresses as $address) {
+                       if ($address->personal) {
+                               // item with name found ( name <email@example.org> )
+                               $addressList[$address->mailbox . '@' . $address->host] = $address->personal;
+                       } else {
+                               // item without name found ( email@example.org )
+                               $addressList[] = $address->mailbox . '@' . $address->host;
+                       }
+               }
+               return $addressList;
+       }
+
+       /**
+        * Makes sure there is a correct sender set.
+        *
+        * If there is no from header the returnpath will be used. If that also fails a fake address will be used to make sure
+        * Swift Mailer will be able to send the message. Some SMTP server will not accept mail messages without a valid sender.
+        *
+        * @return void
+        */
+       protected function fixSender() {
+               $from = $this->message->getFrom();
+               if (count($from) > 0) {
+                       reset($from);
+                       list($fromAddress, $fromName) = each($from);
+               } else {
+                       $fromAddress = $this->message->getReturnPath();
+                       $fromName = $fromAddress;
+               }
+               if (strlen($fromAddress) == 0) {
+                       $fromAddress = 'no-reply@example.org';
+                       $fromName = 'TYPO3 CMS';
+               }
+               $this->message->setFrom(array($fromAddress => $fromName));
+       }
+}
 ?>
\ No newline at end of file