Update version.php to 3.3.0
[isso.git] / Mail.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # Blue Static ISSO Framework
5 || # Copyright (c)2005-2009 Blue Static
6 || #
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version 2 of the License.
10 || #
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 || # more details.
15 || #
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
21
22 /**
23 * Mail Sender (mail.php)
24 *
25 * @package ISSO
26 */
27
28 require_once(ISSO . '/Functions.php');
29
30 /**
31 * Mail Sender
32 *
33 * This framework is a wrapper for the PHP mail function that properly
34 * sends mail with full email headers.
35 *
36 * @author Blue Static
37 * @copyright Copyright (c)2005 - 2009, Blue Static
38 * @package ISSO
39 *
40 */
41 class BSMail
42 {
43 /**
44 * The subject of the message
45 * @var string
46 */
47 private $subject = '';
48
49 /**
50 * Body plain-text of the message
51 * @var string
52 */
53 private $bodyText = '';
54
55 /**
56 * HTML multi-part body of the message
57 * @var string
58 */
59 private $bodyHtml = '';
60
61 /**
62 * The message sender's email address
63 * @var string
64 */
65 private $from = '';
66
67 /**
68 * The message sender's display name
69 * @var string
70 */
71 private $fromName = '';
72
73 /**
74 * Additional message headers
75 * @var string
76 */
77 private $headers = '';
78
79 /**
80 * The new line delimiter used in the message
81 * @var string
82 * @access private
83 */
84 private $delim = "\n";
85
86 /**
87 * Character set used to send messages with
88 * @var string
89 * @access public
90 */
91 private $charset = 'utf-8'; // should we be using iso-8859-1 ?
92
93 /**
94 * Constructor
95 */
96 public function __construct()
97 {
98 if (!BSApp::$input instanceof BSInput)
99 {
100 throw new Exception('BSApp::$input is not an instance of BSInput');
101 }
102 }
103
104 /**
105 * Sets the subject
106 *
107 * @param string Subject text
108 */
109 public function setSubject($subject)
110 {
111 $this->subject = $subject;
112 }
113
114 /**
115 * Sets the body text (required)
116 *
117 * @param string Body text
118 */
119 public function setBodyText($body)
120 {
121 $this->bodyText = $body;
122 }
123
124 /**
125 * Sets the HTML body (optional)
126 *
127 * @param string Body HTML
128 */
129 public function setBodyHtml($body)
130 {
131 $this->bodyHtml = $body;
132 }
133
134 /**
135 * Sets the from address
136 *
137 * @param string Sending email address
138 */
139 public function setFromAddress($address)
140 {
141 $this->from = $address;
142 }
143
144 /**
145 * Sets the from display name
146 *
147 * @param string From name
148 */
149 public function setFromName($name)
150 {
151 $this->fromName = $name;
152 }
153
154 /**
155 * Sets any additional headers
156 *
157 * @param string Additional headers separated by a \n
158 */
159 public function setHeaders($headers)
160 {
161 $this->headers = $headers;
162 }
163
164 /**
165 * Sets the character set to send the email in
166 *
167 * @param string Charset
168 */
169 public function setCharset($charset)
170 {
171 $this->charset = $charset;
172 }
173
174 /**
175 * Sends an email to the specified address with the specified
176 * sender, subject, and body.
177 *
178 * @param string Email address to send to
179 * @param string Name of the recipient
180 * @param bool Send an HTML multipart (if HTML body specified)?
181 *
182 * @return bool Status of the message
183 */
184 public function send($address, $name = null, $sendhtml = false)
185 {
186 if (empty($address))
187 {
188 throw new Exception('You need to specify an email address');
189 }
190
191 // make sure we have a mailer
192 // TODO - add support for SMTP
193 if (!@ini_get('sendmail_path'))
194 {
195 BSApp::debug("BSMail: no sendmail -> not sending");
196 return false;
197 }
198
199 // sort out the to addresses
200 $address = $this->_fetchFirstLine($address);
201 $address = trim(BSApp::$input->unsanitize($address));
202 $name = $this->_fetchFirstLine($name);
203 $name = trim(BSApp::$input->unsanitize($name));
204 $tostring = ($name == null ? $address : "\"$name\" <$address>");
205
206 // sanitize the from field
207 $from = $this->_fetchFirstLine($this->from);
208 $from = trim(BSApp::$input->unsanitize($from));
209 if (empty($from))
210 {
211 throw new Exception('You need to specify a from email address');
212 }
213
214 // sanitize the from name
215 $fromName = $this->_fetchFirstLine($this->fromName);
216 $fromName = ($fromName == '' ? $from : trim(BSApp::$input->unsanitize($fromName)));
217 $fromName = $this->_encodeHeaderValue($this->fromName);
218
219 // sanitize the subject
220 $subject = $this->_fetchFirstLine($this->subject);
221 $subject = trim(BSApp::$input->unsanitize($subject));
222 if (empty($subject))
223 {
224 throw new Exception('You need to specify a subject for the message');
225 }
226
227 // sanitize the body
228 $bodyText = BSFunctions::convert_line_breaks($this->bodyText, $this->delim);
229 $bodyText = trim(BSApp::$input->unsanitize($bodyText, true));
230 if (empty($bodyText))
231 {
232 throw new Exception('You need to specify body text before sending the message');
233 }
234
235 // attach additional headers
236 $headers = BSFunctions::convert_line_breaks($this->headers, $this->delim);
237 $headers .= ((!preg_match("#{$this->delim}$#", $headers) && $headers != '') ? "\n" : '') . "From: \"{$fromName}\" <{$from}>" . $this->delim;
238 $headers .= "Return-Path: {$from}" . $this->delim;
239 $headers .= "X-Mailer: ISSO Mail Framework" . $this->delim;
240 $headers .= "MIME-Version: 1.0" . $this->delim;
241
242 // see if we need to use mime/multipart
243 if ($sendhtml && !empty($bodyhtml) == true)
244 {
245 require_once ISSO . '/Functions.php';
246 $boundary = 'ISSO-MULTIPART-' . BSFunctions::random(10);
247 $headers .= "Content-Type: multipart/alternative; boundary=\"$boundary\"" . $this->delim;
248
249 $bodyHtml = BSFunctions::convert_line_breaks($this->bodyHtml, $this->delim);
250
251 // first part of the message (plaintext)
252 $body = "--$boundary" . $this->delim;
253 $body .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->delim;
254 $body .= "Content-Transfer-Encoding: 8bit" . $this->delim . $this->delim;
255 $body .= $bodyText . $this->delim;
256
257 // add some space between the parts
258 $body .= $this->delim . $this->delim . $this->delim;
259
260 // second part (html)
261 $body .= "--$boundary" . $this->delim;
262 $body .= "Content-Type: text/html; charset=\"" . $this->charset . "\"" . $this->delim;
263 $body .= "Content-Transfer-Encoding: 8bit" . $this->delim;
264 $body .= "Content-Disposition: inline" . $this->delim . $this->delim;
265 $body .= $bodyHtml . $this->delim;
266 $body .= "--$boundary--";
267 }
268 else
269 {
270 $headers .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->delim;
271 $body = $bodyText;
272 }
273 $headers .= "Content-Transfer-Encoding: 8bit" . $this->delim;
274
275 $headers = trim($headers);
276
277 // attempt to send the mail!
278 if (mail($tostring, $subject, $body, $headers, "-f {$from}"))
279 {
280 BSApp::debug("BSMail: sent to $address");
281 }
282 else
283 {
284 BSApp::debug("BSMail: error sending to $address");
285 }
286 }
287
288 /**
289 * Fetches the first line of a string
290 *
291 * @param string A string
292 *
293 * @return string The first line of the string
294 */
295 private function _fetchFirstLine($string)
296 {
297 $string = BSFunctions::convert_line_breaks($string);
298 $broken = explode("\n", $string);
299 return $broken[0];
300 }
301
302 /**
303 * Encodes a header value (to name, fron name, subject, etc.) according
304 * to RFC 2047
305 *
306 * @param string The text to encode
307 *
308 * @return string Encoded text
309 */
310 function _encodeHeaderValue($text)
311 {
312 if (preg_match('#[^a-zA-Z0-9\+\-\*!/]#', $text) == 0)
313 {
314 return $text;
315 }
316
317 // perform this on non-ASCII characters; excluding _ and = because we want them to be encoded as they have
318 // different meanings in mail messages
319 $text = preg_replace('#([^a-zA-Z0-9\+\-\*!/])#e', '"=" . strtoupper(dechex(ord("\\1")))', $text);
320 $text = str_replace('=20', '_' , $text);
321
322 return '=?' . $this->charset . '?q?' . $text . '?=';
323 }
324 }
325
326 ?>