/packages/amazon-ses
[return to app]1
<?php
2 /**
3 * This file is packaged as part of the Amazon Simple Email Service (Amazon SES) Vork App
4 * for use with the Vork Enterprise Framework www.Vork.us
5 *
6 * Copyright (c) 2011, Dan Myers.
7 * Parts copyright (c) 2008, Donovan Schonknecht.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * This is a modified BSD license (the third clause has been removed).
32 * The BSD license may be found here:
33 * http://www.opensource.org/licenses/bsd-license.php
34 *
35 * Amazon Simple Email Service is a trademark of Amazon.com, Inc. or its affiliates.
36 *
37 * SimpleEmailService is based on Donovan Schonknecht's Amazon S3 PHP class, found here:
38 * http://undesigned.org.za/2007/10/22/amazon-s3-php-class
39 */
40
41 /**
42 * Amazon SimpleEmailService PHP class
43 *
44 * @link http://sourceforge.net/projects/php-aws-ses/
45 * version 0.8.2
46 *
47 */
48 class SimpleEmailService {
49 protected $__accessKey; // AWS Access key
50 protected $__secretKey; // AWS Secret key
51 protected $__host;
52
53 public function getAccessKey() { return $this->__accessKey; }
54 public function getSecretKey() { return $this->__secretKey; }
55 public function getHost() { return $this->__host; }
56
57 protected $__verifyHost = 1;
58 protected $__verifyPeer = 1;
59
60 // verifyHost and verifyPeer determine whether curl verifies ssl certificates.
61 // It may be necessary to disable these checks on certain systems.
62 // These only have an effect if SSL is enabled.
63 public function verifyHost() { return $this->__verifyHost; }
64 public function enableVerifyHost($enable = true) { $this->__verifyHost = $enable; }
65
66 public function verifyPeer() { return $this->__verifyPeer; }
67 public function enableVerifyPeer($enable = true) { $this->__verifyPeer = $enable; }
68
69 /**
70 * Constructor
71 *
72 * @param string $accessKey Access key
73 * @param string $secretKey Secret key
74 * @return void
75 */
76 public function __construct($accessKey = null, $secretKey = null, $host = 'email.us-east-1.amazonaws.com') {
77 if ($accessKey !== null && $secretKey !== null) {
78 $this->setAuth($accessKey, $secretKey);
79 }
80 $this->__host = $host;
81 }
82
83 /**
84 * Set AWS access key and secret key
85 *
86 * @param string $accessKey Access key
87 * @param string $secretKey Secret key
88 * @return void
89 */
90 public function setAuth($accessKey, $secretKey) {
91 $this->__accessKey = $accessKey;
92 $this->__secretKey = $secretKey;
93 }
94
95 /**
96 * Lists the email addresses that have been verified and can be used as the 'From' address
97 *
98 * @return An array containing two items: a list of verified email addresses, and the request id.
99 */
100 public function listVerifiedEmailAddresses() {
101 $rest = new SimpleEmailServiceRequest($this, 'GET');
102 $rest->setParameter('Action', 'ListVerifiedEmailAddresses');
103
104 $rest = $rest->getResponse();
105 if ($rest->error === false && $rest->code !== 200) {
106 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
107 }
108 if ($rest->error !== false) {
109 $this->__triggerError('listVerifiedEmailAddresses', $rest->error);
110 return false;
111 }
112
113 $response = array();
114 if (!isset($rest->body)) {
115 return $response;
116 }
117
118 $addresses = array();
119 foreach($rest->body->ListVerifiedEmailAddressesResult->VerifiedEmailAddresses->member as $address) {
120 $addresses[] = (string)$address;
121 }
122
123 $response['Addresses'] = $addresses;
124 $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
125
126 return $response;
127 }
128
129 /**
130 * Requests verification of the provided email address, so it can be used
131 * as the 'From' address when sending emails through SimpleEmailService.
132 *
133 * After submitting this request, you should receive a verification email
134 * from Amazon at the specified address containing instructions to follow.
135 *
136 * @param string email The email address to get verified
137 * @return The request id for this request.
138 */
139 public function verifyEmailAddress($email) {
140 $rest = new SimpleEmailServiceRequest($this, 'POST');
141 $rest->setParameter('Action', 'VerifyEmailAddress');
142 $rest->setParameter('EmailAddress', $email);
143
144 $rest = $rest->getResponse();
145 if ($rest->error === false && $rest->code !== 200) {
146 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
147 }
148 if ($rest->error !== false) {
149 $this->__triggerError('verifyEmailAddress', $rest->error);
150 return false;
151 }
152
153 $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
154 return $response;
155 }
156
157 /**
158 * Removes the specified email address from the list of verified addresses.
159 *
160 * @param string email The email address to remove
161 * @return The request id for this request.
162 */
163 public function deleteVerifiedEmailAddress($email) {
164 $rest = new SimpleEmailServiceRequest($this, 'DELETE');
165 $rest->setParameter('Action', 'DeleteVerifiedEmailAddress');
166 $rest->setParameter('EmailAddress', $email);
167
168 $rest = $rest->getResponse();
169 if ($rest->error === false && $rest->code !== 200) {
170 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
171 }
172 if ($rest->error !== false) {
173 $this->__triggerError('deleteVerifiedEmailAddress', $rest->error);
174 return false;
175 }
176
177 $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
178 return $response;
179 }
180
181 /**
182 * Retrieves information on the current activity limits for this account.
183 * See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html
184 *
185 * @return An array containing information on this account's activity limits.
186 */
187 public function getSendQuota() {
188 $rest = new SimpleEmailServiceRequest($this, 'GET');
189 $rest->setParameter('Action', 'GetSendQuota');
190
191 $rest = $rest->getResponse();
192 if ($rest->error === false && $rest->code !== 200) {
193 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
194 }
195 if ($rest->error !== false) {
196 $this->__triggerError('getSendQuota', $rest->error);
197 return false;
198 }
199
200 $response = array();
201 if (!isset($rest->body)) {
202 return $response;
203 }
204
205 $response['Max24HourSend'] = (string)$rest->body->GetSendQuotaResult->Max24HourSend;
206 $response['MaxSendRate'] = (string)$rest->body->GetSendQuotaResult->MaxSendRate;
207 $response['SentLast24Hours'] = (string)$rest->body->GetSendQuotaResult->SentLast24Hours;
208 $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
209
210 return $response;
211 }
212
213 /**
214 * Retrieves statistics for the last two weeks of activity on this account.
215 * See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html
216 *
217 * @return An array of activity statistics. Each array item covers a 15-minute period.
218 */
219 public function getSendStatistics() {
220 $rest = new SimpleEmailServiceRequest($this, 'GET');
221 $rest->setParameter('Action', 'GetSendStatistics');
222
223 $rest = $rest->getResponse();
224 if ($rest->error === false && $rest->code !== 200) {
225 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
226 }
227 if ($rest->error !== false) {
228 $this->__triggerError('getSendStatistics', $rest->error);
229 return false;
230 }
231
232 $response = array();
233 if (!isset($rest->body)) {
234 return $response;
235 }
236
237 $datapoints = array();
238 foreach($rest->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) {
239 $p = array();
240 $p['Bounces'] = (string)$datapoint->Bounces;
241 $p['Complaints'] = (string)$datapoint->Complaints;
242 $p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts;
243 $p['Rejects'] = (string)$datapoint->Rejects;
244 $p['Timestamp'] = (string)$datapoint->Timestamp;
245
246 $datapoints[] = $p;
247 }
248
249 $response['SendDataPoints'] = $datapoints;
250 $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
251
252 return $response;
253 }
254
255 /**
256 * Given a SimpleEmailServiceMessage object, submits the message to the service for sending.
257 *
258 * @return An array containing the unique identifier for this message and a separate request id.
259 * Returns false if the provided message is missing any required fields.
260 */
261 public function sendEmail($sesMessage) {
262 if (!$sesMessage->validate()) {
263 $this->__triggerError('sendEmail', 'Message failed validation.');
264 return false;
265 }
266
267 $rest = new SimpleEmailServiceRequest($this, 'POST');
268 $rest->setParameter('Action', 'SendEmail');
269
270 $i = 1;
271 foreach($sesMessage->to as $to) {
272 $rest->setParameter('Destination.ToAddresses.member.'.$i, $to);
273 $i++;
274 }
275
276 if (is_array($sesMessage->cc)) {
277 $i = 1;
278 foreach($sesMessage->cc as $cc) {
279 $rest->setParameter('Destination.CcAddresses.member.'.$i, $cc);
280 $i++;
281 }
282 }
283
284 if (is_array($sesMessage->bcc)) {
285 $i = 1;
286 foreach($sesMessage->bcc as $bcc) {
287 $rest->setParameter('Destination.BccAddresses.member.'.$i, $bcc);
288 $i++;
289 }
290 }
291
292 if (is_array($sesMessage->replyto)) {
293 $i = 1;
294 foreach($sesMessage->replyto as $replyto) {
295 $rest->setParameter('ReplyToAddresses.member.'.$i, $replyto);
296 $i++;
297 }
298 }
299
300 $rest->setParameter('Source', $sesMessage->from);
301
302 if ($sesMessage->returnpath != null) {
303 $rest->setParameter('ReturnPath', $sesMessage->returnpath);
304 }
305
306 if ($sesMessage->subject != null && strlen($sesMessage->subject) > 0) {
307 $rest->setParameter('Message.Subject.Data', $sesMessage->subject);
308 if ($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) {
309 $rest->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset);
310 }
311 }
312
313 if ($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) {
314 $rest->setParameter('Message.Body.Text.Data', $sesMessage->messagetext);
315 if ($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) {
316 $rest->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset);
317 }
318 }
319
320 if ($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) {
321 $rest->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml);
322 if ($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) {
323 $rest->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset);
324 }
325 }
326
327 $rest = $rest->getResponse();
328 if ($rest->error === false && $rest->code !== 200) {
329 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
330 }
331 if ($rest->error !== false) {
332 $this->__triggerError('sendEmail', $rest->error);
333 return false;
334 }
335
336 $response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId;
337 $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
338 return $response;
339 }
340
341 /**
342 * Trigger an error message
343 *
344 * @internal Used by member functions to output errors
345 * @param array $error Array containing error information
346 * @return string
347 */
348 public function __triggerError($functionname, $error) {
349 if ($error == false) {
350 trigger_error(sprintf("SimpleEmailService::%s(): Encountered an error, but no description given",
$functionname), E_USER_WARNING);
351 } else if (isset($error['curl']) && $error['curl']) {
352 trigger_error(sprintf("SimpleEmailService::%s(): %s %s", $functionname, $error['code'],
$error['message']), E_USER_WARNING);
353 } else if (isset($error['Error'])) {
354 $e = $error['Error'];
355 $message = sprintf("SimpleEmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname,
$e['Type'], $e['Code'], $e['Message'], $error['RequestId']);
356 trigger_error($message, E_USER_WARNING);
357 } else {
358 trigger_error(sprintf("SimpleEmailService::%s(): Encountered an error: %s", $functionname, $error),
E_USER_WARNING);
359 }
360 }
361 }
362
363 final class SimpleEmailServiceRequest {
364 private $ses, $verb, $parameters = array();
365 public $response, $resource;
366
367 /**
368 * Constructor
369 *
370 * @param string $ses The SimpleEmailService object making this request
371 * @param string $action action
372 * @param string $verb HTTP verb
373 * @return mixed
374 */
375 public function __construct($ses, $verb) {
376 $this->ses = $ses;
377 $this->verb = $verb;
378 $this->response = new STDClass;
379 $this->response->error = false;
380 }
381
382 /**
383 * Set request parameter
384 *
385 * @param string $key Key
386 * @param string $value Value
387 * @param boolean $replace Whether to replace the key if it already exists (default true)
388 * @return void
389 */
390 public function setParameter($key, $value, $replace = true) {
391 if (!$replace && isset($this->parameters[$key])) {
392 $temp = (array)($this->parameters[$key]);
393 $temp[] = $value;
394 $this->parameters[$key] = $temp;
395 } else {
396 $this->parameters[$key] = $value;
397 }
398 }
399
400 /**
401 * Get the response
402 *
403 * @return object | false
404 */
405 public function getResponse() {
406
407 $params = array();
408 foreach ($this->parameters as $var => $value) {
409 if (is_array($value)) {
410 foreach($value as $v) {
411 $params[] = $var.'='.$this->__customUrlEncode($v);
412 }
413 } else {
414 $params[] = $var.'='.$this->__customUrlEncode($value);
415 }
416 }
417
418 sort($params, SORT_STRING);
419
420 // must be in format 'Sun, 06 Nov 1994 08:49:37 GMT'
421 $date = gmdate('D, d M Y H:i:s e');
422
423 $query = implode('&', $params);
424
425 $headers = array();
426 $headers[] = 'Date: '.$date;
427 $headers[] = 'Host: '.$this->ses->getHost();
428
429 $auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->ses->getAccessKey();
430 $auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date);
431 $headers[] = 'X-Amzn-Authorization: '.$auth;
432
433 $url = 'https://'.$this->ses->getHost().'/';
434
435 // Basic setup
436 $curl = curl_init();
437 curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleEmailService/php');
438
439 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->ses->verifyHost() ? 1 : 0));
440 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->ses->verifyPeer() ? 1 : 0));
441
442 // Request types
443 switch ($this->verb) {
444 case 'GET':
445 $url .= '?'.$query;
446 break;
447 case 'POST':
448 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
449 curl_setopt($curl, CURLOPT_POSTFIELDS, $query);
450 $headers[] = 'Content-Type: application/x-www-form-urlencoded';
451 break;
452 case 'DELETE':
453 $url .= '?'.$query;
454 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
455 break;
456 default: break;
457 }
458 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
459 curl_setopt($curl, CURLOPT_HEADER, false);
460
461 curl_setopt($curl, CURLOPT_URL, $url);
462 curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
463 curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
464 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
465
466 // Execute, grab errors
467 if (curl_exec($curl)) {
468 $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
469 } else {
470 $this->response->error = array(
471 'curl' => true,
472 'code' => curl_errno($curl),
473 'message' => curl_error($curl),
474 'resource' => $this->resource
475 );
476 }
477
478 @curl_close($curl);
479
480 // Parse body into XML
481 if ($this->response->error === false && isset($this->response->body)) {
482 $this->response->body = simplexml_load_string($this->response->body);
483
484 // Grab SES errors
485 if (!in_array($this->response->code, array(200, 201, 202, 204))
486 && isset($this->response->body->Error)) {
487 $error = $this->response->body->Error;
488 $output = array();
489 $output['curl'] = false;
490 $output['Error'] = array();
491 $output['Error']['Type'] = (string)$error->Type;
492 $output['Error']['Code'] = (string)$error->Code;
493 $output['Error']['Message'] = (string)$error->Message;
494 $output['RequestId'] = (string)$this->response->body->RequestId;
495
496 $this->response->error = $output;
497 unset($this->response->body);
498 }
499 }
500
501 return $this->response;
502 }
503
504 /**
505 * CURL write callback
506 *
507 * @param resource &$curl CURL resource
508 * @param string &$data Data
509 * @return integer
510 */
511 private function __responseWriteCallback(&$curl, &$data) {
512 $this->response->body .= $data;
513 return strlen($data);
514 }
515
516 /**
517 * Contributed by afx114
518 * URL encode the parameters as per
http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html
519 * PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde
(~), so convert it back after rawurlencode
520 * See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php
521 *
522 * @param string $var String to encode
523 * @return string
524 */
525 private function __customUrlEncode($var) {
526 return str_replace('%7E', '~', rawurlencode($var));
527 }
528
529 /**
530 * Generate the auth string using Hmac-SHA256
531 *
532 * @internal Used by SimpleDBRequest::getResponse()
533 * @param string $string String to sign
534 * @return string
535 */
536 private function __getSignature($string) {
537 return base64_encode(hash_hmac('sha256', $string, $this->ses->getSecretKey(), true));
538 }
539 }
540
541 final class SimpleEmailServiceMessage {
542
543 // these are public for convenience only
544 // these are not to be used outside of the SimpleEmailService class!
545 public $to, $cc, $bcc, $replyto;
546 public $from, $returnpath;
547 public $subject, $messagetext, $messagehtml;
548 public $subjectCharset, $messageTextCharset, $messageHtmlCharset;
549
550 public function __construct() {
551 $this->to = array();
552 $this->cc = array();
553 $this->bcc = array();
554 $this->replyto = array();
555
556 $this->from = null;
557 $this->returnpath = null;
558
559 $this->subject = null;
560 $this->messagetext = null;
561 $this->messagehtml = null;
562
563 $this->subjectCharset = null;
564 $this->messageTextCharset = null;
565 $this->messageHtmlCharset = null;
566 }
567
568 /**
569 * addTo, addCC, addBCC, and addReplyTo have the following behavior:
570 * If a single address is passed, it is appended to the current list of addresses.
571 * If an array of addresses is passed, that array is merged into the current list.
572 */
573 public function addTo($to) {
574 if (!is_array($to)) {
575 $this->to[] = $to;
576 } else {
577 $this->to = array_merge($this->to, $to);
578 }
579 }
580
581 public function addCC($cc) {
582 if (!is_array($cc)) {
583 $this->cc[] = $cc;
584 } else {
585 $this->cc = array_merge($this->cc, $cc);
586 }
587 }
588
589 public function addBCC($bcc) {
590 if (!is_array($bcc)) {
591 $this->bcc[] = $bcc;
592 } else {
593 $this->bcc = array_merge($this->bcc, $bcc);
594 }
595 }
596
597 public function addReplyTo($replyto) {
598 if (!is_array($replyto)) {
599 $this->replyto[] = $replyto;
600 } else {
601 $this->replyto = array_merge($this->replyto, $replyto);
602 }
603 }
604
605 public function setFrom($from) {
606 $this->from = $from;
607 }
608
609 public function setReturnPath($returnpath) {
610 $this->returnpath = $returnpath;
611 }
612
613 public function setSubject($subject) {
614 $this->subject = $subject;
615 }
616
617 public function setSubjectCharset($charset) {
618 $this->subjectCharset = $charset;
619 }
620
621 public function setMessageFromString($text, $html = null) {
622 $this->messagetext = $text;
623 $this->messagehtml = $html;
624 }
625
626 public function setMessageFromFile($textfile, $htmlfile = null) {
627 if (file_exists($textfile) && is_file($textfile) && is_readable($textfile)) {
628 $this->messagetext = file_get_contents($textfile);
629 } else {
630 $this->messagetext = null;
631 }
632 if (file_exists($htmlfile) && is_file($htmlfile) && is_readable($htmlfile)) {
633 $this->messagehtml = file_get_contents($htmlfile);
634 } else {
635 $this->messagehtml = null;
636 }
637 }
638
639 public function setMessageFromURL($texturl, $htmlurl = null) {
640 if ($texturl !== null) {
641 $this->messagetext = file_get_contents($texturl);
642 } else {
643 $this->messagetext = null;
644 }
645 if ($htmlurl !== null) {
646 $this->messagehtml = file_get_contents($htmlurl);
647 } else {
648 $this->messagehtml = null;
649 }
650 }
651
652 public function setMessageCharset($textCharset, $htmlCharset = null) {
653 $this->messageTextCharset = $textCharset;
654 $this->messageHtmlCharset = $htmlCharset;
655 }
656
657 /**
658 * Validates whether the message object has sufficient information to submit a request to SES.
659 * This does not guarantee the message will arrive, nor that the request will succeed;
660 * instead, it makes sure that no required fields are missing.
661 *
662 * This is used internally before attempting a SendEmail or SendRawEmail request,
663 * but it can be used outside of this file if verification is desired.
664 * May be useful if e.g. the data is being populated from a form; developers can generally
665 * use this function to verify completeness instead of writing custom logic.
666 *
667 * @return boolean
668 */
669 public function validate() {
670 if (count($this->to) == 0)
671 return false;
672 if ($this->from == null || strlen($this->from) == 0)
673 return false;
674 // messages require at least one of: subject, messagetext, messagehtml.
675 if (($this->subject == null || strlen($this->subject) == 0)
676 && ($this->messagetext == null || strlen($this->messagetext) == 0)
677 && ($this->messagehtml == null || strlen($this->messagehtml) == 0)) {
678 return false;
679 }
680
681 return true;
682 }
683 }