1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Mail
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Email helper class, provides static methods to perform various tasks relevant
14 * to the Joomla email routines.
15 *
16 * TODO: Test these methods as the regex work is first run and not tested thoroughly
17 *
18 * @since 11.1
19 */
20 abstract class JMailHelper
21 {
22 /**
23 * Cleans single line inputs.
24 *
25 * @param string $value String to be cleaned.
26 *
27 * @return string Cleaned string.
28 *
29 * @since 11.1
30 */
31 public static function cleanLine($value)
32 {
33 $value = JStringPunycode::emailToPunycode($value);
34
35 return trim(preg_replace('/(%0A|%0D|\n+|\r+)/i', '', $value));
36 }
37
38 /**
39 * Cleans multi-line inputs.
40 *
41 * @param string $value Multi-line string to be cleaned.
42 *
43 * @return string Cleaned multi-line string.
44 *
45 * @since 11.1
46 */
47 public static function cleanText($value)
48 {
49 return trim(preg_replace('/(%0A|%0D|\n+|\r+)(content-type:|to:|cc:|bcc:)/i', '', $value));
50 }
51
52 /**
53 * Cleans any injected headers from the email body.
54 *
55 * @param string $body email body string.
56 *
57 * @return string Cleaned email body string.
58 *
59 * @since 11.1
60 */
61 public static function cleanBody($body)
62 {
63 // Strip all email headers from a string
64 return preg_replace("/((From:|To:|Cc:|Bcc:|Subject:|Content-type:) ([\S]+))/", '', $body);
65 }
66
67 /**
68 * Cleans any injected headers from the subject string.
69 *
70 * @param string $subject email subject string.
71 *
72 * @return string Cleaned email subject string.
73 *
74 * @since 11.1
75 */
76 public static function cleanSubject($subject)
77 {
78 return preg_replace("/((From:|To:|Cc:|Bcc:|Content-type:) ([\S]+))/", '', $subject);
79 }
80
81 /**
82 * Verifies that an email address does not have any extra headers injected into it.
83 *
84 * @param string $address email address.
85 *
86 * @return mixed email address string or boolean false if injected headers are present.
87 *
88 * @since 11.1
89 */
90 public static function cleanAddress($address)
91 {
92 if (preg_match("[\s;,]", $address))
93 {
94 return false;
95 }
96
97 return $address;
98 }
99
100 /**
101 * Verifies that the string is in a proper email address format.
102 *
103 * @param string $email String to be verified.
104 *
105 * @return boolean True if string has the correct format; false otherwise.
106 *
107 * @since 11.1
108 */
109 public static function isEmailAddress($email)
110 {
111 // Split the email into a local and domain
112 $atIndex = strrpos($email, '@');
113 $domain = substr($email, $atIndex + 1);
114 $local = substr($email, 0, $atIndex);
115
116 // Check Length of domain
117 $domainLen = strlen($domain);
118
119 if ($domainLen < 1 || $domainLen > 255)
120 {
121 return false;
122 }
123
124 /*
125 * Check the local address
126 * We're a bit more conservative about what constitutes a "legal" address, that is, a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-
127 * The first and last character in local cannot be a period ('.')
128 * Also, period should not appear 2 or more times consecutively
129 */
130 $allowed = "a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-";
131 $regex = "/^[$allowed][\.$allowed]{0,63}$/";
132
133 if (!preg_match($regex, $local) || substr($local, -1) == '.' || $local[0] == '.' || preg_match('/\.\./', $local))
134 {
135 return false;
136 }
137
138 // No problem if the domain looks like an IP address, ish
139 $regex = '/^[0-9\.]+$/';
140
141 if (preg_match($regex, $domain))
142 {
143 return true;
144 }
145
146 // Check Lengths
147 $localLen = strlen($local);
148
149 if ($localLen < 1 || $localLen > 64)
150 {
151 return false;
152 }
153
154 // Check the domain
155 $domain_array = explode('.', rtrim($domain, '.'));
156 $regex = '/^[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/';
157
158 foreach ($domain_array as $domain)
159 {
160 // Convert domain to punycode
161 $domain = JStringPunycode::toPunycode($domain);
162
163 // Must be something
164 if (!$domain)
165 {
166 return false;
167 }
168
169 // Check for invalid characters
170 if (!preg_match($regex, $domain))
171 {
172 return false;
173 }
174
175 // Check for a dash at the beginning of the domain
176 if (strpos($domain, '-') === 0)
177 {
178 return false;
179 }
180
181 // Check for a dash at the end of the domain
182 $length = strlen($domain) - 1;
183
184 if (strpos($domain, '-', $length) === $length)
185 {
186 return false;
187 }
188 }
189
190 return true;
191 }
192 }
193