| <?php |
| /** |
| * Copyright 2007 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /** |
| */ |
| |
| namespace google\appengine\api\mail; |
| |
| use google\appengine\MailAttachment; |
| use google\appengine\MailHeader; |
| use google\appengine\MailMessage; |
| use google\appengine\MailServiceError\ErrorCode; |
| |
| /** |
| * Abstract base class for sending mail using the App Engine mail APIs. |
| */ |
| abstract class BaseMessage { |
| // Force AdminMessage and Message to implement send. |
| abstract public function send(); |
| |
| /* |
| * Force AdminMessage and Message to implment getFunctionArray, required |
| * for parsing the options array. |
| */ |
| abstract protected function getFunctionArray(); |
| |
| // Container for the MailMessage protobuf. |
| protected $message = null; |
| |
| // Whitelisted headers. |
| protected static $allowed_headers = array( |
| 'auto-submitted', 'in-reply-to', 'list-id', 'list-unsubscribe', |
| 'on-behalf-of', 'references', 'resent-date', 'resent-from', 'resent-to'); |
| |
| // Blacklisted extension types. |
| protected static $extension_blacklist = array( |
| 'ade', 'adp', 'bat', 'chm', 'cmd', 'com', 'cpl', 'exe', 'hta', 'ins', |
| 'isp', 'jse', 'lib', 'mde', 'msc', 'msp', 'mst', 'pif', 'scr', 'sct', |
| 'shb', 'sys', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh'); |
| |
| // Setter functions for constructor. |
| protected static $set_functions = array('sender' => 'setSender', |
| 'replyto' => 'setReplyTo', |
| 'to' => 'addTo', |
| 'cc' => 'addCc', |
| 'bcc' => 'addBcc', |
| 'subject' => 'setSubject', |
| 'textBody' => 'setTextBody', |
| 'htmlBody' => 'setHtmlBody', |
| 'header' => 'addHeaderArray', |
| 'attachment' => 'addAttachmentArray'); |
| |
| /** |
| * Construct an instance of Message. |
| * |
| * @param array $options Options for message content, key as per set_functions |
| * shown above, value to be set. |
| * @throws \InvalidArgumentException If the options variable passed was not an |
| * array, if an invalid option was set in the options array, or if a value |
| * to be set by the options array was invalid. |
| */ |
| public function __construct($options = null) { |
| $this->message = new MailMessage(); |
| |
| if (isset($options)) { |
| if (is_array($options)) { |
| foreach($options as $key => $value) { |
| // If this is a valid option to set. |
| $allowed_functions = $this->getFunctionArray(); |
| if (array_key_exists($key, $allowed_functions)) { |
| // Call the corresponding setter function with the input argument. |
| $func_name = $allowed_functions[$key]; |
| call_user_func(array($this, $func_name), $value); |
| } else { |
| $error = sprintf("Message received an invalid option: %s", |
| htmlspecialchars($key)); |
| throw new \InvalidArgumentException($error); |
| } |
| } |
| } else { |
| $error = sprintf("Message expects an array, not %s", gettype($options)); |
| throw new \InvalidArgumentException($error); |
| } |
| } |
| } |
| |
| /** |
| * Adds an attachment to the Message object. |
| * |
| * @param string $filename Filename of the attachment. |
| * @param mixed $data File data of the attachment. |
| * @throws \InvalidArgumentException If the input is not an array or if the |
| * attachment type is invalid (i.e. the filename is not a string, or the |
| * file extension is blacklisted). |
| */ |
| public function addAttachment($filename, $data) { |
| $this->addAttachmentArray(array($filename => $data)); |
| } |
| |
| /** |
| * Adds an array of attachments to the Message object. |
| * |
| * @param array Attachments as filename => data pairs. |
| * Example: array("filename.txt" => "This is the file contents."); |
| * @throws \InvalidArgumentException If the input is not an array or if the |
| * attachment type is invalid (i.e. the filename is not a string, or the |
| * file extension is blacklisted). |
| */ |
| public function addAttachmentArray($attach_array) { |
| if (!is_array($attach_array)) { |
| $error = sprintf("Input is not an array (Actual type: %s).", |
| gettype($attach_array)); |
| throw new \InvalidArgumentException($error); |
| } |
| |
| $error = ""; |
| foreach($attach_array as $filename => $data) { |
| if (!$this->checkValidAttachment($filename, $error)) { |
| throw new \InvalidArgumentException($error); |
| } |
| } |
| |
| foreach($attach_array as $filename => $data) { |
| $new_attachment = $this->message->addAttachment(); |
| $new_attachment->setFilename($filename); |
| $new_attachment->setData($data); |
| } |
| } |
| |
| /** |
| * Adds a header pair to the mail object. |
| * |
| * @param string $key Header name (from the whitelist) to be added. |
| * @param string $value Header value to be added. |
| * @throws \InvalidArgumentException If the header is not on the whitelist, or |
| * if the header is invalid (i.e. not a string). |
| */ |
| public function addHeader($key, $value) { |
| if (!is_string($key)) { |
| $error = sprintf("Header key is not a string (Actual type: %s).", |
| gettype($key)); |
| throw new \InvalidArgumentException($error); |
| } |
| $this->addHeaderArray(array($key => $value)); |
| } |
| |
| /** |
| * Adds an array of headers to the mail object. |
| * |
| * @param array An array of headers. |
| * @throws \InvalidArgumentException If the input is not an array, or if |
| * headers are not on the whitelist, or if a header is invalid |
| * (i.e. not a string). |
| */ |
| public function addHeaderArray($header_array) { |
| if (!is_array($header_array)) { |
| $error = sprintf("Input is not an array (Actual type: %s).", |
| gettype($header_array)); |
| throw new \InvalidArgumentException($error); |
| } |
| |
| $error = ""; |
| foreach($header_array as $key => $value) { |
| if (!$this->checkValidHeader($key, $value, $error)) { |
| throw new \InvalidArgumentException($error); |
| } |
| } |
| |
| foreach($header_array as $key => $value) { |
| $new_header = $this->message->addHeader(); |
| $new_header->setName($key); |
| $new_header->setValue($value); |
| } |
| } |
| |
| /** |
| * Checks that an attachment is valid. |
| * |
| * @param string $filename Filename of the attachment. |
| * @return bool True if successful, false otherwise. |
| * @param string &$error Error message to be set if the header is invalid. |
| */ |
| protected function checkValidAttachment($filename, &$error) { |
| if (!is_string($filename)) { |
| $error = sprintf("Filename must be a string but was type %s", |
| gettype($filename)); |
| return false; |
| } |
| |
| $path_parts = pathinfo($filename); |
| if (isset($path_parts['extension'])) { |
| if (in_array($path_parts['extension'], self::$extension_blacklist)) { |
| $error = sprintf("'%s' is a blacklisted file extension.", |
| $path_parts['extension']); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Checks that an email is valid. |
| * |
| * @param string $email The email to be validated. |
| * @return bool True if valid, false otherwise. |
| */ |
| protected function checkValidEmail($email) { |
| if (filter_var($email, FILTER_VALIDATE_EMAIL) !== false) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Check validity of a header pair. |
| * |
| * @param string $key Header key. |
| * @param string $value Header value. |
| * @param string &$error Error message to be set if the header is invalid. |
| * @return bool True if successful, false otherwise. |
| */ |
| protected function checkValidHeader($key, $value, &$error) { |
| if (!is_string($key)) { |
| $error = sprintf("Header key is not a string (Actual type: %s).", |
| gettype($key)); |
| return false; |
| } else if (!in_array(strtolower($key), self::$allowed_headers)) { |
| // Array keys don't have consistent case. |
| $error = sprintf("Input header '%s: %s' is not whitelisted for use with" . |
| " the Google App Engine Mail Service.", |
| htmlspecialchars($key), |
| htmlspecialchars($value)); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Clear all attachments from the mail object. |
| */ |
| public function clearAttachments() { |
| $this->message->clearAttachment(); |
| } |
| |
| /** |
| * Clear all headers from the mail object. |
| */ |
| public function clearHeaders() { |
| $this->message->clearHeader(); |
| } |
| |
| /** |
| * Handles application errors generated by the RPC call. |
| * |
| * @param ApplicationError An exception caught during the RPC call. |
| * @throws \RuntimeException If there was an internal error or bad request. |
| * @throws \InvalidArgumentException If there was an unauthorized sender, |
| * an invalid attachment type, or an invalid header name. |
| * @throws ApplicationError If the error is not one of the above. |
| */ |
| protected function handleApplicationError($e) { |
| switch($e->getApplicationError()) { |
| case ErrorCode::INTERNAL_ERROR: |
| case ErrorCode::BAD_REQUEST: |
| throw new \RuntimeException( |
| "Mail Service Error: " . $e->getMessage()); |
| case ErrorCode::UNAUTHORIZED_SENDER: |
| $error = sprintf("Mail Service Error: Sender (%s) is not an " . |
| "authorized email address.", |
| htmlspecialchars($this->message->getSender())); |
| throw new \InvalidArgumentException($error); |
| case ErrorCode::INVALID_ATTACHMENT_TYPE: |
| throw new \InvalidArgumentException( |
| "Mail Service Error: Invalid attachment type."); |
| case ErrorCode::INVALID_HEADER_NAME: |
| throw new \InvalidArgumentException( |
| "Mail Service Error: Invalid header name."); |
| default: |
| throw $e; |
| } |
| } |
| |
| /** |
| * Sets HTML content for the email body. |
| * |
| * @param string $text HTML to add. |
| * @throws \InvalidArgumentException If text is not a string. |
| */ |
| public function setHtmlBody($text) { |
| if (!is_string($text)) { |
| $error = sprintf("HTML text given is not a string (Actual type: %s).", |
| gettype($text)); |
| throw new \InvalidArgumentException($error); |
| } |
| $this->message->setHtmlbody($text); |
| } |
| |
| /** |
| * Sets a reply-to address for the mail object. |
| * |
| * @param string $email Reply-to address. |
| * @throws \InvalidArgumentException If the input reply-to address is an |
| * invalid email address. |
| */ |
| public function setReplyTo($email) { |
| if (!$this->checkValidEmail($email)) { |
| throw new \InvalidArgumentException( |
| "Invalid reply-to: ". htmlspecialchars($email)); |
| } |
| $this->message->setReplyto($email); |
| } |
| |
| /** |
| * Sets the sender for the mail object. |
| * |
| * @param string $email Email of the sender. |
| * @throws \InvalidArgumentException If the input sender is an invalid email |
| * address. |
| */ |
| public function setSender($email) { |
| if (!$this->checkValidEmail($email)) { |
| throw new \InvalidArgumentException( |
| "Invalid sender: ". htmlspecialchars($email)); |
| } |
| $this->message->setSender($email); |
| } |
| |
| /** |
| * Sets the subject for the mail object. |
| * |
| * @param string $subject Subject line. |
| * @throws \InvalidArgumentException If subject line is not a string. |
| */ |
| public function setSubject($subject) { |
| if (!is_string($subject)) { |
| $error = sprintf("Subject given is not a string (Actual type: %s).", |
| gettype($subject)); |
| throw new \InvalidArgumentException($error); |
| } |
| $this->message->setSubject($subject); |
| } |
| |
| /** |
| * Sets plain text for the email body. |
| * |
| * @param string $text Plain text to add. |
| * @return bool True if successful, false otherwise. |
| * @throws \InvalidArgumentException If text is not a string. |
| */ |
| public function setTextBody($text) { |
| if (!is_string($text)) { |
| $error = sprintf("Plain text given is not a string (Actual type: %s).", |
| gettype($text)); |
| throw new \InvalidArgumentException($error); |
| } |
| $this->message->setTextbody($text); |
| } |
| } |