/** * Main email posting controller, reads, parses, checks and posts an email message or PM * * What it does: * - Allows a user to reply to a topic on the board by emailing a reply to a * notification message. * - It must have the security key in the email or it will be rejected * - It must be from the email of a registered user * - The key must have been sent to that user * - Keys are used once and then discarded * - Accessed by email imap cron script, and ManageMaillist.controller.php. * * @param string|null $data used to supply a full headers+body email * @param boolean $force used to override common failure errors * @param string|null $key used to supply a lost key */ public function action_pbe_post($data = null, $force = false, $key = null) { global $txt, $modSettings, $language, $user_info, $maintenance; // The function is not even on ... if (empty($modSettings['maillist_enabled'])) { return; } // Our mail parser and our main subs require_once SUBSDIR . '/EmailParse.class.php'; require_once SUBSDIR . '/Emailpost.subs.php'; // Init loadLanguage('Maillist'); setMemoryLimit('128M'); // Load the email parser and get some data to work with $email_message = new Email_Parse(); $email_message->read_data($data, BOARDDIR); if (!$email_message->raw_message) { return false; } // Ask for an html version (if available) and some needed details $email_message->read_email(true, $email_message->raw_message); $email_message->load_address(); $email_message->load_key($key); // If the feature is on but the post/pm function is not enabled, just log the message. if (empty($modSettings['pbe_post_enabled']) && empty($modSettings['pbe_pm_enabled'])) { return pbe_emailError('error_email_notenabled', $email_message); } // Spam I am? if ($email_message->load_spam() && !$force) { return pbe_emailError('error_found_spam', $email_message); } // Load the user from the database based on the sending email address $email_message->email['from'] = !empty($email_message->email['from']) ? strtolower($email_message->email['from']) : ''; $pbe = query_load_user_info($email_message->email['from']); // Can't find this email in our database, a non-user, a spammer, a looser, a poser or even worse? if (empty($pbe)) { return pbe_emailError('error_not_find_member', $email_message); } // Find the message security key, without it we are not going anywhere ever if (empty($email_message->message_key_id)) { return pbe_emailError('error_missing_key', $email_message); } // Good we have a key, who was it sent to? $key_owner = query_key_owner($email_message->message_key_id); // Can't find this key in the database, either // a) spam attempt or b) replying with an expired/consumed key if (empty($key_owner) && !$force) { return pbe_emailError('error_' . ($email_message->message_type === 'p' ? 'pm_' : '') . 'not_find_entry', $email_message); } // The key received was not sent to this member ... how we love those email aggregators if (strtolower($key_owner) !== $email_message->email['from'] && !$force) { return pbe_emailError('error_key_sender_match', $email_message); } // In maintenance mode, just log it for now if (!empty($maintenance) && $maintenance !== 2 && !$pbe['user_info']['is_admin'] && !$user_info['is_admin']) { return pbe_emailError('error_in_maintenance_mode', $email_message); } // The email looks valid, now on to check the actual user trying to make the post/pm // lets load the topic/message info and any additional permissions we need if ($email_message->message_type === 't' || $email_message->message_type === 'm') { // Load the message/topic details $topic_info = query_load_message($email_message->message_type, $email_message->message_id, $pbe); if (empty($topic_info)) { return pbe_emailError('error_topic_gone', $email_message); } // Load board permissions query_load_permissions('board', $pbe, $topic_info); } else { // Load the PM details $pm_info = query_load_message($email_message->message_type, $email_message->message_id, $pbe); if (empty($pm_info)) { // Duh oh ... likely they deleted the PM on the site and are now // replying to the PM by email, the agony! return pbe_emailError('error_pm_not_found', $email_message); } } // Account for moderation actions pbe_check_moderation($pbe); // Maybe they want to do additional spam / security checking call_integration_hook('integrate_mailist_checks_before', array($email_message, $pbe)); // Load in the correct Re: for the language if ($language === $pbe['user_info']['language']) { $pbe['response_prefix'] = $txt['response_prefix']; } else { loadLanguage('index', $language, false); $pbe['response_prefix'] = $txt['response_prefix']; loadLanguage('index'); } // Allow for new topics to be started via a email subject change if (!empty($modSettings['maillist_newtopic_change']) && $email_message->message_type === 'm') { $subject = str_replace($pbe['response_prefix'], '', pbe_clean_email_subject($email_message->subject)); $current_subject = str_replace($pbe['response_prefix'], '', $topic_info['subject']); // If it does not match, then we go to make a new topic instead if (trim($subject) != trim($current_subject)) { $board_info = query_load_board_details($topic_info['id_board'], $pbe); return pbe_create_topic($pbe, $email_message, $board_info); } } // Time to make a Post or a PM, first up topic and message replies if ($email_message->message_type === 't' || $email_message->message_type === 'm') { $result = pbe_create_post($pbe, $email_message, $topic_info); } elseif ($email_message->message_type === 'p') { $result = pbe_create_pm($pbe, $email_message, $pm_info); } if (!empty($result)) { // We have now posted or PM'ed .. lets do some database maintenance cause maintenance is fun :'( query_key_maintenance($email_message); // Update this user so the log shows they were/are active, no luking in the email ether query_update_member_stats($pbe, $email_message, $email_message->message_type === 'p' ? $pm_info : $topic_info); } return !empty($result); }
/** * Creates a failed email entry in the postby_emails_error table * * - Attempts an auto-correct for common errors so the admin / moderator * - can choose to approve the email with the corrections * * @package Maillist * @param string $error * @param Email_Parse $email_message */ function pbe_emailError($error, $email_message) { global $txt; $db = database(); loadLanguage('EmailTemplates'); // Some extra items we will need to remove from the message subject $pm_subject_leader = str_replace('{SUBJECT}', '', $txt['new_pm_subject']); // Clean the subject like we don't know where it has been $subject = trim(str_replace($pm_subject_leader, '', $email_message->subject)); $subject = pbe_clean_email_subject($subject); $subject = $subject === '' ? $txt['no_subject'] : $subject; // Start off with what we know about the security key, even if its nothing $message_key = (string) $email_message->message_key_id; $message_type = (string) $email_message->message_type; $message_id = (int) $email_message->message_id; $board_id = -1; // First up is the old, wrong email address, lets see who this should have come from if its not a new topic request if ($error === 'error_not_find_member' && $email_message->message_type !== 'x') { $key_owner = query_key_owner($email_message->message_key_id); if (!empty($key_owner)) { // Valid key so show who should have sent this key in? email aggravaters :P often mess this up $email_message->email['from'] = $email_message->email['from'] . ' => ' . $key_owner; // Since we have a valid key set those details as well $message_key = $email_message->message_key_id; $message_type = $email_message->message_type; $message_id = $email_message->message_id; } } // A valid key but it was not sent to this user ... but we did get the email from a valid site user if ($error === 'error_key_sender_match') { $key_owner = query_key_owner($email_message->message_key_id); if (!empty($key_owner)) { // Valid key so show who should have sent this key in $email_message->email['from'] = $key_owner . ' => ' . $email_message->email['from']; // Since we have a valid key set those details as well $message_key = $email_message->message_key_id; $message_type = $email_message->message_type; $message_id = $email_message->message_id; } } // No key? We should at a minimum have who its from and a subject, so use that if ($email_message->message_type !== 'x' && (empty($message_key) || $error === 'error_pm_not_found')) { // We don't have the message type (since we don't have a key) // Attempt to see if it might be a PM so we handle it correctly if (empty($message_type) && strpos($email_message->subject, $pm_subject_leader) !== false) { $message_type = 'p'; } // Find all keys sent to this user, sorted by date $user_keys = query_user_keys($email_message->email['from']); // While we have keys to look at see if we can match up this lost message on subjects foreach ($user_keys as $user_key) { if (preg_match('~([a-z0-9]{32})\\-(p|t|m)(\\d+)~', $user_key['id_email'], $match)) { $key = $match[0]; $type = $match[2]; $message = $match[3]; // If we know/suspect its a "m,t or p" then use that to avoid a match on a wrong type, that would be bad ;) if (!empty($message_type) && $message_type === $type || empty($message_type) && $type !== 'p') { // lets look up this message/topic/pm and see if the subjects match ... if they do then tada! if (query_load_subject($message, $type, $email_message->email['from']) === $subject) { // This email has a subject that matches the subject of a message that was sent to them $message_key = $key; $message_id = $message; $message_type = $type; break; } } } } } // Maybe we have enough to find the board id where this was going if (!empty($message_id) && $message_type !== 'p') { $board_id = query_load_board($message_id); } // Log the error so the moderators can take a look, helps keep them sharp $id = isset($_REQUEST['item']) ? (int) $_REQUEST['item'] : 0; $db->insert(!empty($id) ? 'replace' : 'ignore', '{db_prefix}postby_emails_error', array('id_email' => 'int', 'error' => 'string', 'data_id' => 'string', 'subject' => 'string', 'id_message' => 'int', 'id_board' => 'int', 'email_from' => 'string', 'message_type' => 'string', 'message' => 'string'), array($id, $error, $message_key, $subject, $message_id, $board_id, $email_message->email['from'], $message_type, $email_message->raw_message), array('id_email')); // Flush the moderator error number cache, if we are here it likely just changed. cache_put_data('num_menu_errors', null, 900); // If not running from the cli, then go back to the form if (isset($_POST['item'])) { // Back to the form we go $_SESSION['email_error'] = $txt[$error]; redirectexit('action=admin;area=maillist'); } return false; }