/** * Sends an email, wrapping PHP's mail() function. * ALL emails sent by b2evolution must be sent through this function (for consistency and for logging) * * {@link $current_locale} will be used to set the charset. * * Note: we use a single \n as line ending, though it does not comply to {@link http://www.faqs.org/rfcs/rfc2822 RFC2822}, but seems to be safer, * because some mail transfer agents replace \n by \r\n automatically. * * @todo Unit testing with "nice addresses" This gets broken over and over again. * * @param string Recipient email address. * @param string Recipient name. * @param string Subject of the mail * @param string|array The message text OR Array: 'charset', 'full', 'html', 'text' * @param string From address, being added to headers (we'll prevent injections); see {@link http://securephp.damonkohler.com/index.php/Email_Injection}. * Defaults to {@link GeneralSettings::get('notification_sender_email') } if NULL. * @param string From name. * @param array Additional headers ( headername => value ). Take care of injection! * @param integer User ID * @return boolean True if mail could be sent (not necessarily delivered!), false if not - (return value of {@link mail()}) */ function send_mail($to, $to_name, $subject, $message, $from = NULL, $from_name = NULL, $headers = array(), $user_ID = NULL) { global $servertimenow; // Stop a request from the blocked IP addresses or Domains antispam_block_request(); global $debug, $app_name, $app_version, $current_locale, $current_charset, $evo_charset, $locales, $Debuglog, $Settings, $demo_mode, $sendmail_additional_params; $message_data = $message; if (is_array($message_data) && isset($message_data['full'])) { // If content is multipart $message = $message_data['full']; } elseif (is_string($message_data)) { // Convert $message_data to array $message_data = array('full' => $message); } // Replace secret content in the mail logs message body $message = preg_replace('~\\$secret_content_start\\$.*\\$secret_content_end\\$~', '***secret-content-removed***', $message); // Remove secret content marks from the message $message_data = str_replace(array('$secret_content_start$', '$secret_content_end$'), '', $message_data); // Memorize email address $to_email_address = $to; $NL = "\r\n"; if ($demo_mode) { // Debug mode restriction: Sending email in debug mode is not allowed return false; } if (!is_array($headers)) { // Make sure $headers is an array $headers = array($headers); } if (empty($from)) { $from = user_get_notification_sender($user_ID, 'email'); } if (empty($from_name)) { $from_name = user_get_notification_sender($user_ID, 'name'); } // Pass these data for SMTP mailer $message_data['to_email'] = $to; $message_data['to_name'] = empty($to_name) ? NULL : $to_name; $message_data['from_email'] = $from; $message_data['from_name'] = empty($from_name) ? NULL : $from_name; $return_path = $Settings->get('notification_return_path'); // Add real name into $from... if (!is_windows()) { // fplanque: Windows XP, Apache 1.3, PHP 4.4, MS SMTP : will not accept "nice" addresses. if (!empty($to_name)) { $to = '"' . mail_encode_header_string($to_name) . '" <' . $to . '>'; } if (!empty($from_name)) { $from = '"' . mail_encode_header_string($from_name) . '" <' . $from . '>'; } } $from = mail_sanitize_header_string($from, true); // From has to go into headers $headers['From'] = $from; if (!empty($return_path)) { // Set a return path $headers['Return-Path'] = $return_path; } // echo 'sending email to: ['.htmlspecialchars($to).'] from ['.htmlspecialchars($from).']'; $clear_subject = $subject; $subject = mail_encode_header_string($subject); $message = str_replace(array("\r\n", "\r"), $NL, $message); // Convert encoding of message (from internal encoding to the one of the message): // fp> why do we actually convert to $current_charset? // dh> I do not remember. Appears to make sense sending it unconverted in $evo_charset. // asimo> converting the message creates wrong output, no need for conversion, however this needs further investigation // $message = convert_charset( $message, $current_charset, $evo_charset ); if (!isset($headers['Content-Type'])) { // Specify charset and content-type of email $headers['Content-Type'] = 'text/plain; charset=' . $current_charset; } $headers['MIME-Version'] = '1.0'; $headers['Date'] = gmdate('r', $servertimenow); // ADDITIONAL HEADERS: $headers['X-Mailer'] = $app_name . ' ' . $app_version . ' - PHP/' . phpversion(); $ip_list = implode(',', get_ip_list()); if (!empty($ip_list)) { // Add X-Remote_Addr param only if its value is not empty $headers['X-Remote-Addr'] = $ip_list; } // COMPACT HEADERS: $headerstring = get_mail_headers($headers, $NL); // Set an additional parameter for the return path: if (!empty($sendmail_additional_params)) { $additional_parameters = str_replace(array('$from-address$', '$return-address$'), array($from, empty($return_path) ? $from : $return_path), $sendmail_additional_params); } else { $additional_parameters = ''; } if (mail_is_blocked($to_email_address)) { // Check if the email address is blocked $Debuglog->add('Sending mail to «' . htmlspecialchars($to_email_address) . '» FAILED, because this email marked with spam or permanent errors.', 'error'); mail_log($user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'blocked'); return false; } // SEND MESSAGE: if ($debug > 1) { // We agree to die for debugging... if (!evo_mail($to, $subject, $message_data, $headers, $additional_parameters)) { mail_log($user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'error'); debug_die('Sending mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '» FAILED.'); } } else { // Soft debugging only.... if (!evo_mail($to, $subject, $message_data, $headers, $additional_parameters)) { $Debuglog->add('Sending mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '» FAILED.', 'error'); mail_log($user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'error'); return false; } } $Debuglog->add('Sent mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '».'); mail_log($user_ID, $to_email_address, $clear_subject, $message, $headerstring, 'ok'); return true; }
$Messages->add(T_('Invalid account activation request!'), 'error'); $action = 'req_validatemail'; break; } // log in with user $Session->set_user_ID($userID); // activate user account $User->activate_from_Request(); $Messages->add(T_('Your account is now activated.'), 'success'); header_redirect(redirect_after_account_activation()); /* exited */ break; case 'validatemail': // Clicked "Validate email" link from a mail // Stop a request from the blocked IP addresses or Domains antispam_block_request(); param('reqID', 'string', ''); param('sessID', 'integer', ''); if (check_user_status('is_validated')) { // Already validated, e.g. clicked on an obsolete email link: $Messages->add(T_('Your email address has already been validated.'), 'note'); // no break: cleanup & redirect below } else { // Check valid format: if (empty($reqID)) { // This was not requested $Messages->add(T_('Invalid email address validation request!'), 'error'); $action = 'req_validatemail'; break; } // Check valid session (format only, meant as help for the user):
/** * Handle messaging module htsrv actions */ function handle_htsrv_action() { global $current_User, $Blog, $Session, $Messages, $samedomain_htsrv_url; // Init objects we want to work on. $action = param_action(true, true); $disp = param('disp', '/^[a-z0-9\\-_]+$/', 'threads'); // Check that this action request is not a CSRF hacked request: $Session->assert_received_crumb('messaging_' . $disp); // Load classes load_class('messaging/model/_thread.class.php', 'Thread'); load_class('messaging/model/_message.class.php', 'Message'); if (!is_logged_in()) { // user must be logged in debug_die('User must be logged in to proceed with messaging updates!'); } // Check permission: $current_User->check_perm('perm_messaging', 'reply', true); // set where to redirect $redirect_to = param('redirect_to', 'url', NULL); if (empty($redirect_to)) { if (isset($Blog)) { $redirect_to = url_add_param($Blog->gen_baseurl(), 'disp=' . $disp); } else { $redirect_to = url_add_param($baseurl, 'disp=' . $disp); } } if ($disp != 'contacts' && ($thrd_ID = param('thrd_ID', 'integer', '', true))) { // Load thread from cache: $ThreadCache =& get_ThreadCache(); if (($edited_Thread =& $ThreadCache->get_by_ID($thrd_ID, false)) === false) { // Thread doesn't exists with this ID unset($edited_Thread); forget_param('thrd_ID'); $Messages->add(T_('The requested thread does not exist any longer.'), 'error'); $action = 'nil'; } } switch ($disp) { // threads action case 'threads': if ($action != 'create' && $action != 'preview') { // Make sure we got a thrd_ID: param('thrd_ID', 'integer', true); } switch ($action) { case 'create': // create thread // create thread case 'preview': // preview message // Stop a request from the blocked IP addresses or Domains antispam_block_request(); // check if create new thread is allowed if (check_create_thread_limit()) { // max new threads limit reached, don't allow to create new thread debug_die('Invalid request, new conversation limit already reached!'); } $creating_success = create_new_thread(); if (!$creating_success || $action == 'preview') { // unsuccessful new thread creation OR preview mode global $edited_Thread, $edited_Message, $thrd_recipients, $thrd_recipients_array; $redirect_to .= '&action=new'; // save new message and thread params into the Session to not lose the content $unsaved_message_params = array(); $unsaved_message_params['action'] = $action; $unsaved_message_params['subject'] = $edited_Thread->title; $unsaved_message_params['message'] = $edited_Message->text; $unsaved_message_params['message_original'] = $edited_Message->original_text; $unsaved_message_params['renderers'] = $edited_Message->get_renderers_validated(); $unsaved_message_params['thrdtype'] = param('thrdtype', 'string', 'individual'); // alternative: discussion $unsaved_message_params['thrd_recipients'] = $thrd_recipients; $unsaved_message_params['thrd_recipients_array'] = $thrd_recipients_array; $unsaved_message_params['creating_success'] = $creating_success; save_message_params_to_session($unsaved_message_params); } break; case 'delete': // delete thread // Check permission: $current_User->check_perm('perm_messaging', 'delete', true); $confirmed = param('confirmed', 'integer', 0); if ($confirmed) { $msg = sprintf(T_('Thread «%s» deleted.'), $edited_Thread->dget('title')); $edited_Thread->dbdelete(); unset($edited_Thread); forget_param('thrd_ID'); $Messages->add($msg, 'success'); } else { $delete_url = $samedomain_htsrv_url . 'action.php?mname=messaging&thrd_ID=' . $edited_Thread->ID . '&action=delete&confirmed=1&redirect_to=' . $redirect_to . '&' . url_crumb('messaging_threads'); $ok_button = '<a href="' . $delete_url . '" class="btn btn-danger">' . T_('I am sure!') . '</a>'; $cancel_button = '<a href="' . $redirect_to . '" class="btn btn-default">CANCEL</a>'; $msg = sprintf(T_('You are about to delete all messages in the conversation «%s».'), $edited_Thread->dget('title')); $msg .= '<br />' . T_('This CANNOT be undone!') . '<br />' . T_('Are you sure?') . '<br /><br />' . $ok_button . "\t" . $cancel_button; $Messages->add($msg, 'error'); } break; case 'leave': // user wants to leave the thread leave_thread($edited_Thread->ID, $current_User->ID, false); $Messages->add(sprintf(T_('You have successfuly left the «%s» conversation!'), $edited_Thread->get('title')), 'success'); break; case 'close': // close the thread // close the thread case 'close_and_block': // close the thread and block contact leave_thread($edited_Thread->ID, $current_User->ID, true); // user has closed this conversation because there was only one other user involved $Messages->add(sprintf(T_('You have successfuly closed the «%s» conversation!'), $edited_Thread->get('title')), 'success'); if ($action == 'close_and_block') { // user also wants to block contact with the other user involved in this thread $block_user_ID = param('block_ID', 'integer', true); $UserCache =& get_UserCache(); $blocked_User = $UserCache->get_by_ID($block_user_ID); set_contact_blocked($block_user_ID, true); $Messages->add(sprintf(T_('«%s» was blocked.'), $blocked_User->get('login')), 'success'); } break; } break; // break from threads action switch // contacts action // break from threads action switch // contacts action case 'contacts': $user_ID = param('user_ID', 'string', true); if ($action != 'block' && $action != 'unblock') { // only block or unblock is valid debug_die("Invalid action param"); } if (set_contact_blocked($user_ID, $action == 'block' ? 1 : 0)) { if ($action == 'block') { $Messages->add(T_('You have blocked this user from contacting you.'), 'success'); } else { $Messages->add(T_('You have unblocked this user so he can contact you again.'), 'success'); } } $redirect_to = str_replace('&', '&', $redirect_to); break; // messages action // messages action case 'messages': switch ($action) { case 'create': // create new message // Stop a request from the blocked IP addresses or Domains antispam_block_request(); create_new_message($thrd_ID); break; case 'preview': // create new message // Stop a request from the blocked IP addresses or Domains antispam_block_request(); global $edited_Message; $creating_success = create_new_message($thrd_ID, 'preview'); // save new message and thread params into the Session to not lose the content $unsaved_message_params = array(); $unsaved_message_params['action'] = $action; $unsaved_message_params['message'] = $edited_Message->text; $unsaved_message_params['message_original'] = $edited_Message->original_text; $unsaved_message_params['renderers'] = $edited_Message->get_renderers_validated(); $unsaved_message_params['creating_success'] = $creating_success; save_message_params_to_session($unsaved_message_params); break; case 'delete': // delete message // Check permission: $current_User->check_perm('perm_messaging', 'delete', true); $msg_ID = param('msg_ID', 'integer', true); $MessageCache =& get_MessageCache(); if (($edited_Message =& $MessageCache->get_by_ID($msg_ID, false)) === false) { $Messages->add(sprintf(T_('Requested «%s» object does not exist any longer.'), T_('Message')), 'error'); break; } $confirmed = param('confirmed', 'integer', 0); if ($confirmed) { // delete message $edited_Message->dbdelete(); unset($edited_Message); $Messages->add(T_('Message deleted.'), 'success'); } else { $delete_url = $samedomain_htsrv_url . 'action.php?mname=messaging&disp=messages&thrd_ID=' . $thrd_ID . '&msg_ID=' . $msg_ID . '&action=delete&confirmed=1'; if (!empty($Blog)) { // Add blog ID to correctly redirect after deleting: $delete_url .= '&blog=' . $Blog->ID; } $delete_url = url_add_param($delete_url, 'redirect_to=' . rawurlencode($redirect_to), '&') . '&' . url_crumb('messaging_messages'); $ok_button = '<a href="' . $delete_url . '" class="btn btn-danger">' . T_('I am sure!') . '</a>'; $cancel_button = '<a href="' . $redirect_to . '" class="btn btn-default">' . T_('CANCEL') . '</a>'; $msg = T_('You are about to delete this message. ') . '<br /> ' . T_('This CANNOT be undone!') . '<br />' . T_('Are you sure?') . '<br /><br />' . $ok_button . "\t" . $cancel_button; $Messages->add($msg, 'error'); } break; } break; } header_redirect($redirect_to); // Will save $Messages into Session }