/** * Validate and sanitize our options before saving to the DB. * * Callback from {@link register_setting()}. * * @param array $input The submitted values from the form * @return array $output The validated values to be inserted into the DB */ public function validate($input) { $messages = false; $output = array(); $output['mode'] = wp_filter_nohtml_kses($input['mode']); // switching from IMAP to inbound email mode if ('inbound' == $output['mode'] && 'imap' == bp_rbe_get_setting('mode')) { // stop RBE if still connected via IMAP if (bp_rbe_is_connected()) { bp_rbe_stop_imap(); } bp_rbe_log('- Operating mode switched to inbound -'); } // check if key is alphanumeric if (ctype_alnum($input['key'])) { $output['key'] = $input['key']; } /** INBOUND-related ***************************************************/ $inbound_provider = wp_filter_nohtml_kses($input['inbound-provider']); if (!empty($inbound_provider)) { $output['inbound-provider'] = $inbound_provider; } $inbound_domain = isset($input['inbound-domain']) ? wp_filter_nohtml_kses($input['inbound-domain']) : ''; if (!empty($inbound_domain)) { $output['inbound-domain'] = $inbound_domain; } /** IMAP-related ******************************************************/ $username = wp_filter_nohtml_kses($input['username']); $password = wp_filter_nohtml_kses($input['password']); if ($email = is_email($input['email'])) { $output['email'] = $email; if ($input['gmail'] == 1) { $output['username'] = $email; } } if (!empty($username)) { $output['username'] = $username; if (is_email($username)) { $output['email'] = $username; } } if (!empty($password)) { $output['password'] = $password; } // check if port is numeric if (is_numeric($input['port'])) { $output['port'] = $input['port']; } // check if address tag is one character if (strlen($input['tag']) == 1) { $output['tag'] = $input['tag']; } // override certain settings if "gmail" setting is true if (!empty($input['gmail']) && $input['gmail'] == 1) { $output['servername'] = 'imap.gmail.com'; $output['port'] = 993; $output['tag'] = '+'; $output['gmail'] = 1; $output['email'] = $username; // use alternate server settings as defined by the user } else { $output['servername'] = wp_filter_nohtml_kses($input['servername']); $output['port'] = absint($input['port']); $output['tag'] = wp_filter_nohtml_kses($input['tag']); $output['gmail'] = 0; } if (is_numeric($input['keepalive']) && $input['keepalive'] < 30) { $output['keepalive'] = $input['keepalive']; } // keepalive for safe mode will never exceed the execution time if (ini_get('safe_mode')) { $output['keepalive'] = $input['keepalive'] = bp_rbe_get_execution_time('minutes'); } // automatically reconnect after keep-alive limit if (!empty($input['keepaliveauto'])) { $output['keepaliveauto'] = 1; } else { $output['keepaliveauto'] = 0; } // do a quick imap check if we have valid credentials to check if ($output['mode'] == 'imap' && !empty($output['servername']) && !empty($output['port']) && !empty($output['username']) && !empty($output['password'])) { if (function_exists('imap_open')) { $imap = BP_Reply_By_Email_Connect::init(array('host' => $output['servername'], 'port' => $output['port'], 'username' => $output['username'], 'password' => $output['password'])); // if connection failed, add an error if ($imap === false) { $errors = imap_errors(); $messages['connect_error'] = sprintf(__('Error: Unable to connect to inbox - %s', 'bp-rbe'), $errors[0]); $output['connect'] = 0; // connection was successful, now close our temporary connection } else { // this tells bp_rbe_is_required_completed() that we're good to go! $output['connect'] = 1; imap_close($imap); } } } // encode the password if (!empty($password)) { $output['password'] = bp_rbe_encode(array('string' => $password, 'key' => wp_salt())); } /**********************************************************************/ /* error time! */ if (strlen($input['tag']) > 1 && !$output['tag']) { $messages['tag_error'] = __('Error: <strong>Address tag</strong> must only be one character.', 'bp-rbe'); } if (!empty($input['port']) && !is_numeric($input['port']) && !$output['port']) { $messages['port_error'] = __('Error: <strong>Port</strong> must be numeric.', 'bp-rbe'); } if (!empty($input['key']) && !$output['key']) { $messages['key_error'] = __('Error: <strong>Key</strong> must only contain letters and / or numbers.', 'bp-rbe'); } if (!empty($input['keepalive']) && !is_numeric($input['keepalive']) && !$output['keepalive']) { $messages['keepalive_error'] = __('Error: <strong>Keep Alive Connection</strong> value must be less than 30.', 'bp-rbe'); } if (is_array($messages)) { $output['messages'] = $messages; } // For sites using an external object cache, make sure they get the new value. // This is all sorts of ugly! :( // I think this is a WP core bug with the WP Settings API... wp_cache_delete('alloptions', 'options'); return $output; }
/** * Should we enable SSL for the IMAP connection?? * * Check to see if both the OpenSSL and IMAP modules are loaded. Next, see if * the port is explictly 993. * * @since 1.0-beta * * @param int $port The port number for the IMAP server * @return bool */ function bp_rbe_is_imap_ssl($port = 0) { if (empty($port)) { $port = bp_rbe_get_setting('port'); } return BP_Reply_By_Email_Connect::is_ssl((int) $port); }
/** * Constructor. * * @param array $args { * An array of arguments. * * @type string $host The server name. (eg. imap.gmail.com) * @type int $port The port number used by the server name. * @type string $username The username used to login to the server. * @type string $password The password used to login to the server. * @type bool $validatecert Whether to validate certificates from TLS/ * SSL server. Defaults to true. * @type string $mailbox The mailbox to open. Defaults to 'INBOX'. * @type int $retries How many times to try reconnection on failure. * Defaults to 1. * @type bool $reconnect Are we attempting a reconnection? Defaults to false. * } * @param resource $connection The IMAP resource if attempting a reconnection. * @return resource|bool The IMAP resource on success. Boolean false on failure. */ public function __construct($args = array(), $connection = false) { $this->args = wp_parse_args($args, array('host' => bp_rbe_get_setting('servername'), 'port' => bp_rbe_get_setting('port'), 'username' => bp_rbe_get_setting('username'), 'password' => bp_rbe_get_setting('password') ? bp_rbe_decode(array('string' => bp_rbe_get_setting('password'), 'key' => wp_salt())) : false, 'validatecert' => true, 'mailbox' => 'INBOX', 'retries' => 1, 'reconnect' => false)); $this->connection = $connection; }
/** * Returns the querystring from an email address. * * In IMAP mode: * test+THEQUERYSTRING@gmail.com> -> THEQUERYSTRING * * In Inbound mode: * THEQUERYSTRING@reply.mydomain.com -> THEQUERYSTRING * * The querystring is encoded by default. * * @param string $address The email address containing the address tag * @return mixed Either the address tag on success or false on failure */ public static function get_querystring($address = '') { if (empty($address)) { return false; } $at = strpos($address, '@'); if ($at === false) { return false; } // inbound mode uses subdomain addressing if (bp_rbe_is_inbound()) { $qs = substr($address, 0, $at); // imap mode uses address tags } else { $tag = strpos($address, bp_rbe_get_setting('tag')); if ($tag === false) { $qs = false; } else { $qs = substr($address, ++$tag, $at - $tag); } } /** * Filter the querystring from an email address. * * @since 1.0-RC4 * * @param string|false $qs Current querystring. * @param string $address Full 'to' email address. */ return apply_filters('bp_rbe_get_querystring', $qs, $address); }
/** * The main method we use to parse an IMAP inbox. */ public function run() { // $instance must be initialized before we go on! if (self::$instance === false) { return false; } // If safe mode isn't on, then let's set the execution time to unlimited if (!ini_get('safe_mode')) { set_time_limit(0); } // Try to connect $connect = $this->connect(); if (!$connect) { return false; } // Total duration we should keep the IMAP stream alive for in seconds $duration = bp_rbe_get_execution_time(); bp_rbe_log('--- Keep alive for ' . $duration / 60 . ' minutes ---'); bp_rbe_remove_imap_lock(); // Mark the current timestamp, mark the future time when we should close the IMAP connection; // Do our parsing until $future > $now; re-mark the timestamp at end of loop... rinse and repeat! for ($now = time(), $future = time() + $duration; $future > $now; $now = time()) { // Get number of messages $message_count = imap_num_msg($this->connection); // If there are messages in the inbox, let's start parsing! if ($message_count != 0) { // According to this: // http://www.php.net/manual/pl/function.imap-headerinfo.php#95012 // This speeds up rendering the email headers... could be wrong imap_headers($this->connection); bp_rbe_log('- Checking inbox -'); // Loop through each email message for ($i = 1; $i <= $message_count; ++$i) { // flush object cache if necessary // // we only flush the cache if the default object cache is in use // why? b/c the default object cache is only meant for single page loads and // since RBE runs in the background, we need to flush the object cache so WP // will do a direct DB query for the next data fetch if (!wp_using_ext_object_cache()) { wp_cache_flush(); } self::$html = false; $content = self::body_parser($this->connection, $i); $headers = $this->get_mail_headers($this->connection, $i); $data = array('headers' => $headers, 'to_email' => BP_Reply_By_Email_Parser::get_header($headers, 'To'), 'from_email' => BP_Reply_By_Email_Parser::get_header($headers, 'From'), 'content' => $content, 'is_html' => self::$html, 'subject' => imap_utf8(BP_Reply_By_Email_Parser::get_header($headers, 'Subject'))); $parser = BP_Reply_By_Email_Parser::init($data, $i); if (is_wp_error($parser)) { //do_action( 'bp_rbe_imap_no_match', $this->connection, $i, $headers, $parser->get_error_code() ); do_action('bp_rbe_no_match', $parser, $data, $i, $this->connection); } // do something during the loop // we mark the message for deletion here via this hook do_action('bp_rbe_imap_loop', $this->connection, $i); // unset some variables at the end of the loop unset($content, $headers, $data, $parser); } // do something after the loop do_action('bp_rbe_imap_after_loop', $this->connection); } // stop the loop if necessary if (bp_rbe_should_stop()) { if ($this->close()) { bp_rbe_log('--- Manual termination of connection confirmed! Kaching! ---'); } else { bp_rbe_log('--- Error - invalid connection during manual termination ---'); } remove_action('shutdown', 'bp_rbe_spawn_inbox_check'); exit; } // Give IMAP server a break sleep(10); // If the IMAP connection is down, reconnect if (!imap_ping($this->connection)) { bp_rbe_log('-- IMAP connection is down, attempting to reconnect... --'); if (bp_rbe_is_connecting(array('clearcache' => true))) { bp_rbe_log('--- RBE is already attempting to connect - stopping connection attempt ---'); continue; } // add lock marker before connecting bp_rbe_add_imap_lock(); // attempt to reconnect $reopen = BP_Reply_By_Email_Connect::init(array('reconnect' => true), $this->connection); if ($reopen) { bp_rbe_log('-- Reconnection successful! --'); } else { bp_rbe_log('-- Reconnection failed! :( --'); bp_rbe_log('Cannot connect: ' . imap_last_error()); // cleanup RBE after failure bp_rbe_cleanup(); remove_action('shutdown', 'bp_rbe_spawn_inbox_check'); exit; } } // Unset some variables to clear some memory unset($message_count); } if ($this->close()) { bp_rbe_log('--- Closing current connection automatically ---'); } else { bp_rbe_log('--- Invalid connection during close time ---'); } // autoconnect is off if (1 !== (int) bp_rbe_get_setting('keepaliveauto', array('refetch' => true))) { remove_action('shutdown', 'bp_rbe_spawn_inbox_check'); // sleep a bit before autoconnecting again } else { sleep(5); } exit; }
/** * Load inbound provider. * * @since 1.0-RC3 */ public function load_inbound_provider() { $selected = bp_rbe_get_setting('inbound-provider'); $providers = self::get_inbound_providers(); if (isset($providers[$selected]) && class_exists($providers[$selected])) { $this->inbound_provider = new $providers[$selected](); // Default to SendGrid if no provider is valid. } else { $this->inbound_provider = new $providers['sendgrid'](); } }