/** * Constructor * * This may INSERT a basedomain and a useragent but NOT the HIT itself! */ function Hit($referer = NULL, $IP = NULL, $session_id = NULL, $hit_time = NULL, $test_mode = NULL, $test_uri = NULL, $user_agent = NULL, $test_admin = NULL, $test_rss = NULL) { global $debug; if (isset($IP)) { $this->IP = $IP; } else { // Get the first IP in the list of REMOTE_ADDR and HTTP_X_FORWARDED_FOR $this->IP = get_ip_list(true); } if (!empty($session_id)) { $this->session_id = $session_id; } if (!empty($hit_time)) { $this->hit_time = $hit_time; } if (!empty($test_mode)) { $this->test_mode = $test_mode; } if (!empty($test_uri)) { $this->test_uri = $test_uri; } if (!empty($user_agent)) { $this->user_agent = $user_agent; } if (!empty($test_admin)) { $this->test_admin = $test_admin; } if (!empty($test_rss)) { $this->test_rss = $test_rss; } $this->hit_type = $this->get_hit_type(); // Check the REFERER and determine referer_type: // TODO: dh> move this out of here, too, only if "antispam_block_spam_referers" is true, // do something about it (here or somewhere else, but early). $this->detect_referer($referer); // May EXIT if we are set up to block referer spam. if ($debug) { global $Debuglog; $Debuglog->add('Hit: IP: ' . $this->IP, 'request'); $Debuglog->add('Hit: UserAgent: ' . $this->get_user_agent(), 'request'); $Debuglog->add('Hit: Referer: ' . var_export($this->referer, true) . '; type=' . $this->referer_type, 'request'); $Debuglog->add('Hit: Remote Host: ' . $this->get_remote_host(false), 'request'); } }
die('Please, do not access this page directly.'); } // ---------------------------- EMAIL HEADER INCLUDED HERE ---------------------------- emailskin_include('_email_header.inc.txt.php', $params); // ------------------------------- END OF EMAIL HEADER -------------------------------- global $htsrv_url, $samedomain_htsrv_url; // Default params: $params = array_merge(array('sender_name' => '', 'sender_address' => '', 'message_footer' => '', 'Blog' => NULL, 'message' => '', 'comment_id' => NULL, 'post_id' => NULL, 'recipient_User' => NULL, 'Comment' => NULL), $params); $Blog =& $params['Blog']; $recipient_User =& $params['recipient_User']; // show sender name echo sprintf(T_('%s has sent you this message:'), $params['sender_name']) . "\n\n"; echo $params['message']; echo "\n\n-- \n"; // show sender IP address echo sprintf(T_('This message was typed by a user connecting from this IP address: %s.'), implode(', ', get_ip_list(false, true))) . "\n\n"; // show sender email address echo sprintf(T_('By replying, your email will go directly to %s.'), $params['sender_address']); // show additional message info if (!empty($Blog)) { if (!empty($params['comment_id'])) { echo "\n\n" . T_('Message sent from your comment:') . "\n" . url_add_param($Blog->get('url'), 'p=' . $params['post_id'] . '#' . $params['comment_id'], '&'); } elseif (!empty($params['post_id'])) { echo "\n\n" . T_('Message sent from your post:') . "\n" . url_add_param($Blog->get('url'), 'p=' . $params['post_id'], '&'); } else { echo "\n\n" . sprintf(T_('Message sent through the contact form on %s.'), $Blog->get('shortname')) . "\n"; } } if (!empty($recipient_User)) { // Member: global $Settings;
/** * 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 The message 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 antispam_block_ip(); global $debug, $app_name, $app_version, $current_locale, $current_charset, $evo_charset, $locales, $Debuglog, $Settings, $demo_mode, $sendmail_additional_params; // 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'); } $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 = ''; reset($headers); while (list($lKey, $lValue) = each($headers)) { // Add additional headers $headerstring .= $lKey . ': ' . $lValue . $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 (!mail($to, $subject, $message, $headerstring, $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 (!@mail($to, $subject, $message, $headerstring, $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; }
/** * Constructor */ function Hit() { global $Debuglog, $DB; // Get the first IP in the list of REMOTE_ADDR and HTTP_X_FORWARDED_FOR $this->IP = get_ip_list(true); // Check the referer and determine referer_type: $this->detect_referer(); // Check if we know the base domain: $this->referer_basedomain = get_base_domain($this->referer); if ($this->referer_basedomain) { // This referer has a base domain // Check if we have met it before: $hit_basedomain = $DB->get_row(' SELECT dom_ID FROM T_basedomains WHERE dom_name = ' . $DB->quote($this->referer_basedomain)); if (!empty($hit_basedomain->dom_ID)) { // This basedomain has visited before: $this->referer_domain_ID = $hit_basedomain->dom_ID; // fp> The blacklist handling that was here made no sense. } else { // This is the first time this base domain visits: // The INSERT below can fail, probably if we get two simultaneous hits (seen in the demo logfiles) $old_hold_on_error = $DB->halt_on_error; $old_show_errors = $DB->show_errors; $DB->halt_on_error = false; $DB->show_errors = false; if ($DB->query(' INSERT INTO T_basedomains( dom_name ) VALUES( ' . $DB->quote($this->referer_basedomain) . ' )')) { // INSERTed ok: $this->referer_domain_ID = $DB->insert_id; } else { // INSERT failed: see, try to select again (may become/stay NULL) $this->referer_domain_ID = $DB->get_var(' SELECT dom_ID FROM T_basedomains WHERE dom_name = ' . $DB->quote($this->referer_basedomain)); } $DB->halt_on_error = $old_hold_on_error; $DB->show_errors = $old_show_errors; } } $this->detect_useragent(); $Debuglog->add('IP: ' . $this->IP, 'hit'); $Debuglog->add('UserAgent: ' . $this->user_agent, 'hit'); $Debuglog->add('Referer: ' . var_export($this->referer, true) . '; type=' . $this->referer_type, 'hit'); $Debuglog->add('Remote Host: ' . $this->get_remote_host(false), 'hit'); }
/** * Sends a mail, wrapping PHP's mail() function. * * {@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, either email only or in "Name <*****@*****.**>" format (RFC2822). * Can be multiple comma-separated addresses. * @param string Subject of the mail * @param string The message text * @param string From address, being added to headers (we'll prevent injections); * see {@link http://securephp.damonkohler.com/index.php/Email_Injection}. * Might be just an email address or of the same form as {@link $to}. * {@link $notify_from} gets used as default (if NULL). * @param array Additional headers ( headername => value ). Take care of injection! * @return boolean True if mail could be sent (not necessarily delivered!), false if not - (return value of {@link mail()}) */ function send_mail($to, $subject, $message, $from = NULL, $headers = array()) { global $debug, $app_name, $app_version, $current_locale, $current_charset, $evo_charset, $locales, $Debuglog, $notify_from; $NL = "\n"; if (is_windows()) { // fplanque: Windows XP, Apache 1.3, PHP 4.4, MS SMTP : will not accept "nice" addresses. $to = preg_replace('/^.*?<(.+?)>$/', '$1', $to); $from = preg_replace('/^.*?<(.+?)>$/', '$1', $from); } // echo 'sending email to: ['.htmlspecialchars($to).'] from ['.htmlspecialchars($from).']'; if (!is_array($headers)) { // Make sure $headers is an array $headers = array($headers); } // Specify charset and content-type of email $headers['Content-Type'] = 'text/plain; charset=' . $current_charset; $headers['X-Mailer'] = $app_name . ' ' . $app_version . ' - PHP/' . phpversion(); $headers['X-Remote-Addr'] = implode(',', get_ip_list()); // -- Build headers ---- if (empty($from)) { $from = $notify_from; } else { $from = trim($from); } if (!empty($from)) { // From has to go into headers $from_save = preg_replace('~(\\r|\\n).*$~s', '', $from); // Prevent injection! (remove everything after (and including) \n or \r) if ($from != $from_save) { if (strpos($from_save, '<') !== false && !strpos($from_save, '>')) { // We have probably stripped the '>' at the end! $from_save .= '>'; } // Add X-b2evo notification mail header about this $headers['X-b2evo'] = 'Fixed email header injection (From)'; $Debuglog->add('Detected email injection! Fixed «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($from_save) . '».', 'security'); $from = $from_save; } $headers['From'] = $from; } $headerstring = ''; reset($headers); while (list($lKey, $lValue) = each($headers)) { // Add additional headers $headerstring .= $lKey . ': ' . $lValue . $NL; } if (function_exists('mb_encode_mimeheader')) { // encode subject $subject = mb_encode_mimeheader($subject, mb_internal_encoding(), 'B', $NL); } $message = str_replace(array("\r\n", "\r"), $NL, $message); // Convert encoding of message (from internal encoding to the one of the message): $message = convert_charset($message, $current_charset, $evo_charset); if ($debug > 1) { // We agree to die for debugging... if (!mail($to, $subject, $message, $headerstring)) { debug_die('Sending mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '» FAILED.'); } } else { // Soft debugging only.... if (!@mail($to, $subject, $message, $headerstring)) { $Debuglog->add('Sending mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '» FAILED.', 'error'); return false; } } $Debuglog->add('Sent mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '».'); return true; }
/** * Get list of IP addresses with link to back-office page if User has an access * * @param object|NULL User * @param array|NULL List of IP addresses * @param string Text of link, Use '#' to display IP address * @return array List of IP addresses */ function get_linked_ip_list($ip_list = NULL, $User = NULL, $link_text = '#') { if ($User === NULL) { // Get current User by default global $current_User; $User =& $current_User; } if ($ip_list === NULL) { // Get IP addresses by function get_ip_list() $ip_list = get_ip_list(false, true); } if (!empty($User) && $User->check_perm('admin', 'restricted') && $User->check_perm('spamblacklist', 'view')) { // User has an access to backoffice, Display a link for each IP address global $admin_url; foreach ($ip_list as $i => $ip_address) { if ($link_text == '#') { // Use IP address aslink text $link_text = $ip_address; } $ip_list[$i] = '<a href="' . $admin_url . '?ctrl=antispam&tab3=ipranges&ip_address=' . $ip_address . '">' . $link_text . '</a>'; } } return $ip_list; }
/** * Increase a counter in DB antispam ip range table * * @param string Counter name: 'user', 'contact_email' */ function antispam_increase_counter($counter_name) { switch ($counter_name) { case 'user': $field_name = 'aipr_user_count'; break; case 'contact_email': $field_name = 'aipr_contact_email_count'; break; default: debug_die('Wrong antispam counter name'); } foreach (get_ip_list() as $ip) { if ($ip === '') { // Skip an empty continue; } $ip = int2ip(ip2int($ip)); // Convert IPv6 to IPv4 if (preg_match('#^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$#i', $ip)) { // Check IP for correct format $ip_24bit_start = ip2int(preg_replace('#\\.\\d{1,3}$#i', '.0', $ip)); $ip_24bit_end = ip2int(preg_replace('#\\.\\d{1,3}$#i', '.255', $ip)); global $DB; if ($iprange = get_ip_range($ip_24bit_start, $ip_24bit_end)) { // Update ip range $DB->query('UPDATE T_antispam__iprange SET ' . $field_name . ' = ' . $field_name . ' + 1 WHERE aipr_ID = ' . $DB->quote($iprange->aipr_ID)); } else { // Insert new ip range $DB->query('INSERT INTO T_antispam__iprange ( aipr_IPv4start, aipr_IPv4end, ' . $field_name . ' ) VALUES ( ' . $DB->quote($ip_24bit_start) . ', ' . $DB->quote($ip_24bit_end) . ', 1 ) '); } } } }
/** * Event handler: Called at the end of the login procedure, if the * {@link $current_User current User} is set and the user is therefor registered. */ function AfterLoginRegisteredUser(&$params) { $Country = $this->get_country_by_IP(get_ip_list(true)); if (!$Country) { // Country not found return false; } // Check if the currently logged in user should be moved into the suspect group based on the Country ID antispam_suspect_user_by_country($Country->ID); }