/** * Process a message and pass it through the Inbound Message handling systems. * * @param \Horde_Imap_Client_Data_Fetch $message The message to process * @param bool $viewreadmessages Whether to also look at messages which have been marked as read * @param bool $skipsenderverification Whether to skip the sender verification stage */ public function process_message(\Horde_Imap_Client_Data_Fetch $message, $viewreadmessages = false, $skipsenderverification = false) { global $USER; // We use the Client IDs several times - store them here. $messageid = new \Horde_Imap_Client_Ids($message->getUid()); mtrace("- Parsing message " . $messageid); // First flag this message to prevent another running hitting this message while we look at the headers. $this->add_flag_to_message($messageid, self::MESSAGE_FLAGGED); if ($this->is_bulk_message($message, $messageid)) { mtrace("- The message has a bulk header set. This is likely an auto-generated reply - discarding."); return; } // Record the user that this script is currently being run as. This is important when re-processing existing // messages, as cron_setup_user is called multiple times. $originaluser = $USER; $envelope = $message->getEnvelope(); $recipients = $envelope->to->bare_addresses; foreach ($recipients as $recipient) { if (!\core\message\inbound\address_manager::is_correct_format($recipient)) { // Message did not contain a subaddress. mtrace("- Recipient '{$recipient}' did not match Inbound Message headers."); continue; } // Message contained a match. $senders = $message->getEnvelope()->from->bare_addresses; if (count($senders) !== 1) { mtrace("- Received multiple senders. Only the first sender will be used."); } $sender = array_shift($senders); mtrace("-- Subject:\t" . $envelope->subject); mtrace("-- From:\t" . $sender); mtrace("-- Recipient:\t" . $recipient); // Grab messagedata including flags. $query = new \Horde_Imap_Client_Fetch_Query(); $query->structure(); $messagedata = $this->client->fetch($this->get_mailbox(), $query, array('ids' => $messageid))->first(); if (!$viewreadmessages && $this->message_has_flag($messageid, self::MESSAGE_SEEN)) { // Something else has already seen this message. Skip it now. mtrace("-- Skipping the message - it has been marked as seen - perhaps by another process."); continue; } // Mark it as read to lock the message. $this->add_flag_to_message($messageid, self::MESSAGE_SEEN); // Now pass it through the Inbound Message processor. $status = $this->addressmanager->process_envelope($recipient, $sender); if (($status & ~\core\message\inbound\address_manager::VALIDATION_DISABLED_HANDLER) !== $status) { // The handler is disabled. mtrace("-- Skipped message - Handler is disabled. Fail code {$status}"); // In order to handle the user error, we need more information about the message being failed. $this->process_message_data($envelope, $messagedata, $messageid); $this->inform_user_of_error(get_string('handlerdisabled', 'tool_messageinbound', $this->currentmessagedata)); return; } // Check the validation status early. No point processing garbage messages, but we do need to process it // for some validation failure types. if (!$this->passes_key_validation($status, $messageid)) { // None of the above validation failures were found. Skip this message. mtrace("-- Skipped message - it does not appear to relate to a Inbound Message pickup. Fail code {$status}"); // Remove the seen flag from the message as there may be multiple recipients. $this->remove_flag_from_message($messageid, self::MESSAGE_SEEN); // Skip further processing for this recipient. continue; } // Process the message as the user. $user = $this->addressmanager->get_data()->user; mtrace("-- Processing the message as user {$user->id} ({$user->username})."); cron_setup_user($user); // Process and retrieve the message data for this message. // This includes fetching the full content, as well as all headers, and attachments. if (!$this->process_message_data($envelope, $messagedata, $messageid)) { mtrace("--- Message could not be found on the server. Is another process removing messages?"); return; } // When processing validation replies, we need to skip the sender verification phase as this has been // manually completed. if (!$skipsenderverification && $status !== 0) { // Check the validation status for failure types which require confirmation. // The validation result is tested in a bitwise operation. mtrace("-- Message did not meet validation but is possibly recoverable. Fail code {$status}"); // This is a recoverable error, but requires user input. if ($this->handle_verification_failure($messageid, $recipient)) { mtrace("--- Original message retained on mail server and confirmation message sent to user."); } else { mtrace("--- Invalid Recipient Handler - unable to save. Informing the user of the failure."); $this->inform_user_of_error(get_string('invalidrecipientfinal', 'tool_messageinbound', $this->currentmessagedata)); } // Returning to normal cron user. mtrace("-- Returning to the original user."); cron_setup_user($originaluser); return; } // Add the content and attachment data. mtrace("-- Validation completed. Fetching rest of message content."); $this->process_message_data_body($messagedata, $messageid); // The message processor throws exceptions upon failure. These must be caught and notifications sent to // the user here. try { $result = $this->send_to_handler(); } catch (\core\message\inbound\processing_failed_exception $e) { // We know about these kinds of errors and they should result in the user being notified of the // failure. Send the user a notification here. $this->inform_user_of_error($e->getMessage()); // Returning to normal cron user. mtrace("-- Returning to the original user."); cron_setup_user($originaluser); return; } catch (\Exception $e) { // An unknown error occurred. The user is not informed, but the administrator is. mtrace("-- Message processing failed. An unexpected exception was thrown. Details follow."); mtrace($e->getMessage()); // Returning to normal cron user. mtrace("-- Returning to the original user."); cron_setup_user($originaluser); return; } if ($result) { // Handle message cleanup. Messages are deleted once fully processed. mtrace("-- Marking the message for removal."); $this->add_flag_to_message($messageid, self::MESSAGE_DELETED); } else { mtrace("-- The Inbound Message processor did not return a success status. Skipping message removal."); } // Returning to normal cron user. mtrace("-- Returning to the original user."); cron_setup_user($originaluser); mtrace("-- Finished processing " . $message->getUid()); // Skip the outer loop too. The message has already been processed and it could be possible for there to // be two recipients in the envelope which match somehow. return; } }
/** * The address processor function. * * @param string $address * @return void */ public function process($address) { return parent::process($address); }