get_role( 'administrator' ); if ( $role ) { $role->add_cap( 'use_openid_provider' ); } } // for some reason, show_on_front is not always set, causing is_front_page() to fail $show_on_front = get_option( 'show_on_front' ); if ( empty( $show_on_front ) ) { update_option( 'show_on_front', 'posts' ); } // Add custom OpenID options add_option( 'openid_enable_commentform', true ); add_option( 'openid_plugin_enabled', true ); add_option( 'openid_plugin_revision', 0 ); add_option( 'openid_db_revision', 0 ); add_option( 'openid_enable_approval', false ); add_option( 'openid_xrds_returnto', true ); add_option( 'openid_comment_displayname_length', 12 ); openid_create_tables(); openid_migrate_old_data(); // setup schedule cleanup wp_clear_scheduled_hook( 'cleanup_openid' ); wp_schedule_event( time(), 'hourly', 'cleanup_openid' ); // flush rewrite rules if ( ! isset( $wp_rewrite ) ) { $wp_rewrite = new WP_Rewrite(); } $wp_rewrite->flush_rules(); // set current revision update_option( 'openid_plugin_revision', OPENID_PLUGIN_REVISION ); openid_remove_historical_options(); } /** * Remove options that were used by previous versions of the plugin. */ function openid_remove_historical_options() { delete_option( 'oid_db_revision' ); delete_option( 'oid_db_version' ); delete_option( 'oid_enable_approval' ); delete_option( 'oid_enable_commentform' ); delete_option( 'oid_enable_email_mapping' ); delete_option( 'oid_enable_foaf' ); delete_option( 'oid_enable_localaccounts' ); delete_option( 'oid_enable_loginform' ); delete_option( 'oid_enable_selfstyle' ); delete_option( 'oid_enable_unobtrusive' ); delete_option( 'oid_plugin_enabled' ); delete_option( 'oid_plugin_revision' ); delete_option( 'oid_plugin_version' ); delete_option( 'oid_trust_root' ); delete_option( 'force_openid_registration' ); delete_option( 'openid_skip_require_name' ); delete_option( 'openid_enable_email_mapping' ); delete_option( 'openid_xrds_idib' ); delete_option( 'openid_xrds_eaut' ); } /** * Called on plugin deactivation. Cleanup all transient data. * * @see register_deactivation_hook */ function openid_deactivate_plugin() { wp_clear_scheduled_hook( 'cleanup_openid' ); delete_option( 'openid_associations' ); delete_option( 'openid_nonces' ); delete_option( 'openid_server_associations' ); delete_option( 'openid_server_nonces' ); } /** * Delete options in database */ function openid_uninstall_plugin() { openid_delete_tables(); wp_clear_scheduled_hook( 'cleanup_openid' ); // current options delete_option( 'openid_enable_commentform' ); delete_option( 'openid_plugin_enabled' ); delete_option( 'openid_plugin_revision' ); delete_option( 'openid_db_revision' ); delete_option( 'openid_enable_approval' ); delete_option( 'openid_xrds_returnto' ); delete_option( 'openid_comment_displayname_length' ); delete_option( 'openid_associations' ); delete_option( 'openid_nonces' ); delete_option( 'openid_server_associations' ); delete_option( 'openid_server_nonces' ); delete_option( 'openid_blog_owner' ); delete_option( 'openid_no_require_name' ); delete_option( 'openid_required_for_registration' ); // historical options openid_remove_historical_options(); } /** * Cleanup expired nonces and associations from the OpenID store. */ function openid_cleanup() { $store = openid_getStore(); $store->cleanupNonces(); $store->cleanupAssociations(); } /* * Customer error handler for calls into the JanRain library */ function openid_customer_error_handler( $errno, $errmsg, $filename, $linenum, $vars = null ) { if ( 2048 == ( 2048 & $errno ) ) { return; } if ( ! defined( 'WP_DEBUG' ) || ! ( WP_DEBUG ) ) { // XML errors if ( strpos( $errmsg, 'DOMDocument::loadXML' ) === 0 ) { return; } if ( strpos( $errmsg, 'domxml' ) === 0 ) { return; } // php-openid errors //if (strpos($errmsg, 'Successfully fetched') === 0) return; //if (strpos($errmsg, 'Got no response code when fetching') === 0) return; //if (strpos($errmsg, 'Fetching URL not allowed') === 0) return; // curl errors //if (strpos($errmsg, 'CURL error (6)') === 0) return; // couldn't resolve host //if (strpos($errmsg, 'CURL error (7)') === 0) return; // couldn't connect to host } openid_error( "Library Error $errno: $errmsg in $filename :$linenum" ); } /** * Generate a unique WordPress username for the given OpenID URL. * * @param string $url OpenID URL to generate username for * @param boolean $append should we try appending a number if the username is already taken * @return mixed generated username or null if unable to generate */ function openid_generate_new_username( $url, $append = true ) { $base = openid_normalize_username( $url ); $i = ''; while ( true ) { $username = openid_normalize_username( $base . $i ); $user = get_user_by( 'login', $username ); if ( $user ) { if ( ! $append ) { return null; } $i++; continue; } // TODO: add hook return $username; } } /** * Normalize the OpenID URL into a username. This includes rules like: * - remove protocol prefixes like 'http://' and 'xri://' * - remove the 'xri.net' domain for i-names * - substitute certain characters which are not allowed by WordPress * * @param string $username username to be normalized * @return string normalized username * @uses apply_filters() Calls 'openid_normalize_username' just before returning normalized username */ function openid_normalize_username( $username ) { $normalized = $username; $normalized = preg_replace( '|^https?://(xri.net/([^@]!?)?)?|', '', $normalized ); $normalized = preg_replace( '|^xri://([^@]!?)?|', '', $normalized ); $normalized = preg_replace( '|/$|', '', $normalized ); $normalized = sanitize_user( $normalized ); $normalized = preg_replace( '|[^a-z0-9 _.\-@]+|i', '-', $normalized ); $normalized = apply_filters( 'openid_normalize_username', $normalized, $username ); return $normalized; } /** * Get the OpenID trust root for the given return_to URL. * * @param string $return_to OpenID return_to URL * @return string OpenID trust root */ function openid_trust_root( $return_to = null ) { $trust_root = trailingslashit( get_option( 'home' ) ); // If return_to is HTTPS, trust_root must be as well if ( ! empty( $return_to ) && preg_match( '/^https/', $return_to ) ) { $trust_root = preg_replace( '/^http\:/', 'https:', $trust_root ); } $trust_root = apply_filters( 'openid_trust_root', $trust_root, $return_to ); return $trust_root; } /** * Login user with specified identity URL. This will find the WordPress user account connected to this * OpenID and set it as the current user. Only call this function AFTER you've verified the identity URL. * * @param string $identity userID or OpenID to set as current user * @param boolean $remember should we set the "remember me" cookie * @return void */ function openid_set_current_user( $identity, $remember = true ) { if ( is_numeric( $identity ) ) { $user_id = $identity; } else { $user_id = get_user_by_openid( $identity ); } if ( ! $user_id ) { return; } $user = set_current_user( $user_id ); wp_set_auth_cookie( $user->ID, $remember ); do_action( 'wp_login', $user->user_login ); } /** * Create a new WordPress user with the specified identity URL and user data. * * @param string $identity_url OpenID to associate with the newly * created account * @param array $user_data array of user data * @uses do_action() Calls 'openid_consumer_new_user_custom_data' hook action after creating user */ function openid_create_new_user( $identity_url, &$user_data ) { global $wpdb; // Identity URL is new, so create a user @include_once( ABSPATH . 'wp-admin/upgrade-functions.php' ); // 2.1 @include_once( ABSPATH . WPINC . '/registration-functions.php' ); // 2.0.4 // otherwise, try to use preferred username if ( empty( $username ) && array_key_exists( 'nickname', $user_data ) ) { $username = openid_generate_new_username( $user_data['nickname'], false ); } // try using email address before resorting to URL if ( empty( $username ) && array_key_exists( 'user_email', $user_data ) ) { $username = openid_generate_new_username( $user_data['user_email'], false ); } // finally, build username from OpenID URL if ( empty( $username ) ) { $username = openid_generate_new_username( $identity_url ); } $user_data['user_login'] = $username; $user_data['display_name'] = $username; $user_data['user_pass'] = substr( md5( uniqid( microtime() ) ), 0, 7 ); $user_id = wp_insert_user( $user_data ); if ( $user_id instanceof WP_Error ) { openid_message( $user_id->get_error_message() ); openid_status( 'error' ); return; } elseif ( is_integer( $user_id ) ) { // created ok $user_data['ID'] = $user_id; // XXX this all looks redundant, see openid_set_current_user $user = new WP_User( $user_id ); $credentials = array( 'user_login' => $user->user_login, 'user_password' => $user_data['user_pass'], 'remember' => true, ); if ( ! wp_signon( $credentials ) ) { openid_message( __( 'User was created fine, but wp_signon() for the new user failed. This is probably a bug.', 'openid' ) ); openid_status( 'error' ); openid_error( openid_message() ); return; } // notify of user creation wp_new_user_notification( $user_id ); wp_clear_auth_cookie(); wp_set_auth_cookie( $user_id, true ); // Bind the provided identity to the just-created user openid_add_user_identity( $user_id, $identity_url ); openid_status( 'redirect' ); do_action( 'openid_consumer_new_user_custom_data', $user_id, $user_data ); if ( ! $user->has_cap( 'edit_posts' ) ) { $redirect_to = '/wp-admin/profile.php'; } } else { // failed to create user for some reason. openid_message( __( 'OpenID authentication successful, but failed to create WordPress user. This is probably a bug.', 'openid' ) ); openid_status( 'error' ); openid_error( openid_message() ); } } /** * Get user data for the given identity URL. Data is returned as an associative array with the keys: * ID, user_url, user_nicename, display_name * * Multiple soures of data may be available and are attempted in the following order: * - OpenID Attribute Exchange !! not yet implemented * - OpenID Simple Registration * - hCard discovery !! not yet implemented * - default to identity URL * * @param string $identity_url OpenID to get user data about * @return array user data * @uses apply_filters() Calls 'openid_user_data' to gather profile data associated with the identity URL */ function openid_get_user_data( $identity_url ) { $data = array( 'ID' => null, 'user_url' => $identity_url, 'user_nicename' => $identity_url, 'display_name' => $identity_url, ); // create proper website URL if OpenID is an i-name if ( preg_match( '/^[\=\@\+].+$/', $identity_url ) ) { $data['user_url'] = 'http://xri.net/' . $identity_url; } $data = apply_filters( 'openid_user_data', $data, $identity_url ); // if display_name is still the same as the URL, clean that up a bit if ( $data['display_name'] == $identity_url ) { $parts = parse_url( $identity_url ); if ( false !== $parts ) { $host = preg_replace( '/^www./', '', $parts['host'] ); $path = substr( $parts['path'], 0, get_option( 'openid_comment_displayname_length' ) ); if ( strlen( $path ) < strlen( $parts['path'] ) ) { $path .= '…'; } $data['display_name'] = $host . $path; } } return $data; } /** * Retrieve user data from OpenID Attribute Exchange. * * @param string $identity_url OpenID to get user data about * @param reference $data reference to user data array * @see get_user_data */ function openid_get_user_data_ax( $data, $identity_url ) { require_once( 'Auth/OpenID/AX.php' ); $response = openid_response(); $ax = Auth_OpenID_AX_FetchResponse::fromSuccessResponse( $response ); if ( ! $ax ) { return $data; } $email = $ax->getSingle( 'http://axschema.org/contact/email' ); if ( $email && ! is_a( $email, 'Auth_OpenID_AX_Error' ) ) { $data['user_email'] = $email; } $nickname = $ax->getSingle( 'http://axschema.org/namePerson/friendly' ); if ( $nickname && ! is_a( $nickname, 'Auth_OpenID_AX_Error' ) ) { $data['nickname'] = $ax->getSingle( 'http://axschema.org/namePerson/friendly' ); $data['user_nicename'] = $ax->getSingle( 'http://axschema.org/namePerson/friendly' ); $data['display_name'] = $ax->getSingle( 'http://axschema.org/namePerson/friendly' ); } $fullname = $ax->getSingle( 'http://axschema.org/namePerson' ); if ( $fullname && ! is_a( $fullname, 'Auth_OpenID_AX_Error' ) ) { $namechunks = explode( ' ', $fullname, 2 ); if ( isset( $namechunks[0] ) ) { $data['first_name'] = $namechunks[0]; } if ( isset( $namechunks[1] ) ) { $data['last_name'] = $namechunks[1]; } $data['display_name'] = $fullname; } return $data; } /** * Retrieve user data from OpenID Simple Registration. * * @param string $identity_url OpenID to get user data about * @param reference $data reference to user data array * @see get_user_data */ function openid_get_user_data_sreg( $data, $identity_url ) { require_once( 'Auth/OpenID/SReg.php' ); $response = openid_response(); $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse( $response ); $sreg = $sreg_resp->contents(); if ( ! $sreg ) { return $data; } if ( array_key_exists( 'email', $sreg ) && $sreg['email'] ) { $data['user_email'] = $sreg['email']; } if ( array_key_exists( 'nickname', $sreg ) && $sreg['nickname'] ) { $data['nickname'] = $sreg['nickname']; $data['user_nicename'] = $sreg['nickname']; $data['display_name'] = $sreg['nickname']; } if ( array_key_exists( 'fullname', $sreg ) && $sreg['fullname'] ) { $namechunks = explode( ' ', $sreg['fullname'], 2 ); if ( isset( $namechunks[0] ) ) { $data['first_name'] = $namechunks[0]; } if ( isset( $namechunks[1] ) ) { $data['last_name'] = $namechunks[1]; } $data['display_name'] = $sreg['fullname']; } return $data; } /** * Retrieve user data from hCard discovery. * * @param string $identity_url OpenID to get user data about * @param reference $data reference to user data array * @see get_user_data */ function openid_get_user_data_hcard( $data, $identity_url ) { // TODO implement hcard discovery return $data; } /** * Parse the WordPress request. If the query var 'openid' is present, then * handle the request accordingly. * * @param WP $wp WP instance for the current request */ function openid_parse_request( $wp ) { if ( array_key_exists( 'openid', $wp->query_vars ) ) { openid_clean_request(); switch ( $wp->query_vars['openid'] ) { case 'consumer': @session_start(); $action = $_SESSION['openid_action']; // no action, which probably means OP-initiated login. Set // action to 'login', and redirect to home page when finished if ( empty( $action ) ) { $action = 'login'; if ( empty( $_SESSION['openid_finish_url'] ) ) { //$_SESSION['openid_finish_url'] = get_option('home'); } } finish_openid( $action ); break; case 'server': $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : null; openid_server_request( $action ); break; case 'ajax': if ( check_admin_referer( 'openid_ajax' ) ) { header( 'Content-Type: application/json' ); echo '{ "valid":' . ( is_url_openid( $_REQUEST['url'] ) ? 'true' : 'false' ) . ', "nonce":"' . wp_create_nonce( 'openid_ajax' ) . '" }'; exit; } } } } /** * Check if the provided URL is a valid OpenID. * * @param string $url URL to check * @return boolean true if the URL is a valid OpenID */ function is_url_openid( $url ) { $auth_request = openid_begin_consumer( $url ); return ( null != $auth_request ); } /** * Clean HTTP request parameters for OpenID. * * Apache's rewrite module is often used to produce "pretty URLs" in WordPress. * Other webservers, such as lighttpd, nginx, and Microsoft IIS each have ways * (read: hacks) for simulating this kind of functionality. This function * reverses the side-effects of these hacks so that the OpenID request * variables are in the form that the OpenID library expects. */ function openid_clean_request() { if ( array_key_exists( 'q', $_GET ) ) { // handle nginx web server, which adds an additional query string parameter named "q" unset( $_GET['q'] ); $vars = explode( '&', $_SERVER['QUERY_STRING'] ); $clean = array(); foreach ( $vars as $v ) { if ( strpos( $v, 'q=' ) !== 0 ) { $clean[] = $v; } } $_SERVER['QUERY_STRING'] = implode( '&', $clean ); } elseif ( isset( $_SERVER['argc'] ) && $_SERVER['argc'] >= 1 && 'error=404' == $_SERVER['argv'][0] ) { // handle lighttpd hack which uses a custom error-handler, passing 404 errors to WordPress. // This results in the QUERY_STRING not having the correct information, but fortunately we // can pull it out of REQUEST_URI list($path, $query) = explode( '?', $_SERVER['REQUEST_URI'], 2 ); $_SERVER['QUERY_STRING'] = $query; } } /** * Build an OpenID service URL. * * @param string $service service to build URL for * @param string $scheme URL scheme to use for URL (see site_url()) * @return string service URL * @see site_url */ function openid_service_url( $service, $scheme = null ) { global $wp_rewrite; if ( ! $wp_rewrite ) { $wp_rewrite = new WP_Rewrite(); } if ( ! defined( 'OPENID_SSL' ) || ! OPENID_SSL ) { $scheme = null; } $url = site_url( '/?openid=' . $service, $scheme ); return $url; } /** * Add rewrite rules to WP_Rewrite for the OpenID services. */ function openid_rewrite_rules( $wp_rewrite ) { $openid_rules = array( 'openid/(.+)' => 'index.php?openid=$matches[1]', ); $wp_rewrite->rules = $openid_rules + $wp_rewrite->rules; } /** * Add valid query vars to WordPress for OpenID. */ function openid_query_vars( $vars ) { $vars[] = 'openid'; return $vars; } function openid_status( $new = null ) { static $status; return ( null == $new ) ? $status : $status = $new; } function openid_message( $new = null ) { static $message; return ( null == $new ) ? $message : $message = $new; } function openid_response( $new = null ) { static $response; return ( null == $new ) ? $response : $response = $new; } function openid_enabled( $new = null ) { static $enabled; if ( null == $enabled ) { $enabled = true; } return ( null == $new ) ? $enabled : $enabled = $new; } /** * Send HTTP post through the user-agent. If javascript is not supported, the * user will need to click on a "continue" button. * * @param string $action form action (URL to POST form to) * @param array $parameters key-value pairs of parameters to include in the form * @uses do_action() Calls 'openid_page_head' hook action */ function openid_repost( $action, $parameters ) { $html = '
'; foreach ( $parameters as $k => $v ) { if ( 'submit' == $k ) { continue; } $html .= "\n" . ''; } $html .= '
'; openid_page( $html, __( 'OpenID Authentication Redirect', 'openid' ) ); } function openid_page( $message, $title = '' ) { global $wp_locale; ?> > <?php echo $title; ?> text_direction ) ) { wp_admin_css( 'login-rtl', true ); } do_action( 'admin_head' ); do_action( 'openid_page_head' ); ?> prepare( 'INSERT INTO ' . openid_identity_table() . ' (user_id,url,hash) VALUES ( %s, %s, MD5(%s) )', $user_id, $url, $url ); return $wpdb->query( $sql ); } /** * Remove identity url from user. * * @param int $user_id user id * @param string $identity_url identity url to remove */ function openid_drop_identity( $user_id, $identity_url ) { global $wpdb; return $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . openid_identity_table() . ' WHERE user_id = %s AND url = %s', $user_id, $identity_url ) ); } /** * Remove all identity urls from user. * * @param int $user_id user id */ function openid_drop_all_identities( $user_id ) { global $wpdb; return $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . openid_identity_table() . ' WHERE user_id = %s', $user_id ) ); } // -------------- // // Other Function // // -------------- // /** * Format OpenID for display... namely, remove the fragment if present. * @param string $url url to display * @return url formatted for display */ function openid_display_identity( $url ) { return preg_replace( '/#.+$/', '', $url ); } function openid_error( $msg ) { error_log( '[OpenID] ' . $msg ); } function openid_debug( $msg ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { openid_error( $msg ); } }