/wp-content/plugins/openid/logic.php
PHP | 1300 lines | 766 code | 244 blank | 290 comment | 156 complexity | 1454b6d6eb60df5beea975015a8923aa MD5 | raw file
1<?php 2/** 3 * logic.php 4 * 5 * Dual License: GPLv2 & Modified BSD 6 */ 7if (!class_exists('WordPressOpenID_Logic')): 8 9/** 10 * Basic logic for wp-openid plugin. 11 */ 12class WordPressOpenID_Logic { 13 14 /** 15 * Soft verification of plugin activation 16 * 17 * @return boolean if the plugin is okay 18 */ 19 function uptodate() { 20 global $openid; 21 22 $openid->log->debug('checking if database is up to date'); 23 if( get_option('oid_db_revision') != WPOPENID_DB_REVISION ) { 24 $openid->enabled = false; 25 $openid->log->warning('Plugin database is out of date: ' . get_option('oid_db_revision') . ' != ' . WPOPENID_DB_REVISION); 26 update_option('oid_plugin_enabled', false); 27 return false; 28 } 29 $openid->enabled = (get_option('oid_plugin_enabled') == true ); 30 return $openid->enabled; 31 } 32 33 34 /** 35 * Get the internal SQL Store. If it is not already initialized, do so. 36 * 37 * @return WordPressOpenID_Store internal SQL store 38 */ 39 function getStore() { 40 global $openid; 41 42 if (!isset($openid->store)) { 43 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 44 require_once 'store.php'; 45 46 $openid->store = new WordPressOpenID_Store($openid); 47 if (null === $openid->store) { 48 $openid->log->err('OpenID store could not be created properly.'); 49 $openid->enabled = false; 50 } 51 } 52 53 return $openid->store; 54 } 55 56 57 /** 58 * Get the internal OpenID Consumer object. If it is not already initialized, do so. 59 * 60 * @return Auth_OpenID_Consumer OpenID consumer object 61 */ 62 function getConsumer() { 63 global $openid; 64 65 if (!isset($openid->consumer)) { 66 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 67 require_once 'Auth/OpenID/Consumer.php'; 68 69 $store = WordPressOpenID_Logic::getStore(); 70 $openid->consumer = new Auth_OpenID_Consumer($store); 71 if( null === $openid->consumer ) { 72 $openid->log->err('OpenID consumer could not be created properly.'); 73 $openid->enabled = false; 74 } 75 } 76 77 return $openid->consumer; 78 } 79 80 81 /** 82 * Initialize required store and consumer and make a few sanity checks. This method 83 * does a lot of the heavy lifting to get everything initialized, so we don't call it 84 * until we actually need it. 85 */ 86 function late_bind($reload = false) { 87 global $wpdb, $openid; 88 openid_init(); 89 90 $openid->log->debug('beginning late binding'); 91 92 $openid->enabled = true; // Be Optimistic 93 if( $openid->bind_done && !$reload ) { 94 $openid->log->debug('we\'ve already done the late bind... moving on'); 95 return WordPressOpenID_Logic::uptodate(); 96 } 97 $openid->bind_done = true; 98 99 $f = @fopen( '/dev/urandom', 'r'); 100 if ($f === false) { 101 define( 'Auth_OpenID_RAND_SOURCE', null ); 102 } 103 104 // include required JanRain OpenID library files 105 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 106 $openid->log->debug('temporary include path for importing = ' . get_include_path()); 107 require_once('Auth/OpenID/Discover.php'); 108 require_once('Auth/OpenID/DatabaseConnection.php'); 109 require_once('Auth/OpenID/MySQLStore.php'); 110 require_once('Auth/OpenID/Consumer.php'); 111 require_once('Auth/OpenID/SReg.php'); 112 restore_include_path(); 113 114 $openid->log->debug("Bootstrap -- checking tables"); 115 if( $openid->enabled ) { 116 117 $store =& WordPressOpenID_Logic::getStore(); 118 if (!$store) return; // something broke 119 $openid->enabled = $store->check_tables(); 120 121 if( !WordPressOpenID_Logic::uptodate() ) { 122 update_option('oid_plugin_enabled', true); 123 update_option('oid_plugin_revision', WPOPENID_PLUGIN_REVISION ); 124 update_option('oid_db_revision', WPOPENID_DB_REVISION ); 125 WordPressOpenID_Logic::uptodate(); 126 } 127 } else { 128 $openid->message = 'WPOpenID Core is Disabled!'; 129 update_option('oid_plugin_enabled', false); 130 } 131 132 return $openid->enabled; 133 } 134 135 136 /** 137 * Called on plugin activation. 138 * 139 * @see register_activation_hook 140 */ 141 function activate_plugin() { 142 $start_mem = memory_get_usage(); 143 global $openid; 144 openid_init(); 145 146 $store =& WordPressOpenID_Logic::getStore(); 147 $store->create_tables(); 148 149 wp_schedule_event(time(), 'hourly', 'cleanup_openid'); 150 //$openid->log->warning("activation memory usage: " . (int)((memory_get_usage() - $start_mem) / 1000)); 151 } 152 153 function cleanup_nonces() { 154 global $openid; 155 openid_init(); 156 $store =& WordPressOpenID_Logic::getStore(); 157 $store->cleanupNonces(); 158 } 159 160 161 /** 162 * Called on plugin deactivation. Cleanup all transient tables. 163 * 164 * @see register_deactivation_hook 165 */ 166 function deactivate_plugin() { 167 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 168 require_once 'store.php'; 169 WordPressOpenID_Store::destroy_tables(); 170 } 171 172 173 /* 174 * Customer error handler for calls into the JanRain library 175 */ 176 function customer_error_handler($errno, $errmsg, $filename, $linenum, $vars) { 177 global $openid; 178 179 if( (2048 & $errno) == 2048 ) return; 180 $openid->log->notice( "Library Error $errno: $errmsg in $filename :$linenum"); 181 } 182 183 184 /** 185 * If we're doing openid authentication ($_POST['openid_url'] is set), start the consumer & redirect 186 * Otherwise, return and let WordPress handle the login and/or draw the form. 187 * 188 * @param string $username username provided in login form 189 */ 190 function wp_authenticate( &$username ) { 191 global $openid; 192 193 if( !empty( $_POST['openid_url'] ) ) { 194 if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken 195 $redirect_to = ''; 196 if( !empty( $_REQUEST['redirect_to'] ) ) $redirect_to = $_REQUEST['redirect_to']; 197 WordPressOpenID_Logic::start_login( $_POST['openid_url'], 'login', array('redirect_to' => $redirect_to) ); 198 } 199 if( !empty( $openid->message ) ) { 200 global $error; 201 $error = $openid->message; 202 } 203 } 204 205 206 /** 207 * Handle OpenID profile management. 208 */ 209 function openid_profile_management() { 210 global $wp_version, $openid; 211 openid_init(); 212 213 if( !isset( $_REQUEST['action'] )) return; 214 215 $openid->action = $_REQUEST['action']; 216 217 require_once(ABSPATH . 'wp-admin/admin-functions.php'); 218 219 if ($wp_version < '2.3') { 220 require_once(ABSPATH . 'wp-admin/admin-db.php'); 221 require_once(ABSPATH . 'wp-admin/upgrade-functions.php'); 222 } 223 224 auth_redirect(); 225 nocache_headers(); 226 get_currentuserinfo(); 227 228 if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken 229 230 switch( $openid->action ) { 231 case 'add_identity': 232 check_admin_referer('wp-openid-add_identity'); 233 234 $user = wp_get_current_user(); 235 236 $store =& WordPressOpenID_Logic::getStore(); 237 $auth_request = WordPressOpenID_Logic::begin_consumer($_POST['openid_url']); 238 239 $userid = $store->get_user_by_identity($auth_request->endpoint->claimed_id); 240 241 if ($userid) { 242 global $error; 243 if ($user->ID == $userid) { 244 $error = 'You already have this Identity URL!'; 245 } else { 246 $error = 'This Identity URL is already connected to another user.'; 247 } 248 return; 249 } 250 251 WordPressOpenID_Logic::start_login($_POST['openid_url'], 'verify'); 252 break; 253 254 case 'drop_identity': // Remove a binding. 255 WordPressOpenID_Logic::_profile_drop_identity($_REQUEST['id']); 256 break; 257 } 258 } 259 260 261 /** 262 * Remove identity URL from current user account. 263 * 264 * @param int $id id of identity URL to remove 265 */ 266 function _profile_drop_identity($id) { 267 global $openid; 268 269 $user = wp_get_current_user(); 270 271 if( !isset($id)) { 272 $openid->message = 'Identity url delete failed: ID paramater missing.'; 273 $openid->action = 'error'; 274 return; 275 } 276 277 $store =& WordPressOpenID_Logic::getStore(); 278 $deleted_identity_url = $store->get_identities($user->ID, $id); 279 if( FALSE === $deleted_identity_url ) { 280 $openid->message = 'Identity url delete failed: Specified identity does not exist.'; 281 $openid->action = 'error'; 282 return; 283 } 284 285 $identity_urls = $store->get_identities($user->ID); 286 if (sizeof($identity_urls) == 1 && !$_REQUEST['confirm']) { 287 $openid->message = 'This is your last identity URL. Are you sure you want to delete it? Doing so may interfere with your ability to login.<br /><br /> ' 288 . '<a href="?confirm=true&'.$_SERVER['QUERY_STRING'].'">Yes I\'m sure. Delete it</a> ' 289 . '<a href="?page=openid">No, don\'t delete it.</a>'; 290 $openid->action = 'warning'; 291 return; 292 } 293 294 check_admin_referer('wp-openid-drop-identity_'.$deleted_identity_url); 295 296 297 if( $store->drop_identity($user->ID, $id) ) { 298 $openid->message = 'Identity url delete successful. <b>' . $deleted_identity_url 299 . '</b> removed.'; 300 $openid->action = 'success'; 301 302 // ensure that profile URL is still a verified Identity URL 303 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 304 require_once 'Auth/OpenID.php'; 305 if ($GLOBALS['wp_version'] >= '2.3') { 306 require_once(ABSPATH . 'wp-admin/includes/admin.php'); 307 } else { 308 require_once(ABSPATH . WPINC . '/registration.php'); 309 } 310 $identities = $store->get_identities($user->ID); 311 $current_url = Auth_OpenID::normalizeUrl($user->user_url); 312 313 $verified_url = false; 314 if (!empty($identities)) { 315 foreach ($identities as $id) { 316 if ($id['url'] == $current_url) { 317 $verified_url = true; 318 break; 319 } 320 } 321 322 if (!$verified_url) { 323 $user->user_url = $identities[0]['url']; 324 wp_update_user( get_object_vars( $user )); 325 $openid->message .= '<br /><strong>Note:</strong> For security reasons, your profile URL has been updated to match your Identity URL.'; 326 } 327 } 328 return; 329 } 330 331 $openid->message = 'Identity url delete failed: Unknown reason.'; 332 $openid->action = 'error'; 333 } 334 335 336 /** 337 * Send the user to their OpenID provider to authenticate. 338 * 339 * @param Auth_OpenID_AuthRequest $auth_request OpenID authentication request object 340 * @param string $trust_root OpenID trust root 341 * @param string $return_to URL where the OpenID provider should return the user 342 */ 343 function doRedirect($auth_request, $trust_root, $return_to) { 344 global $openid; 345 346 if ($auth_request->shouldSendRedirect()) { 347 $trust_root = trailingslashit($trust_root); 348 $redirect_url = $auth_request->redirectURL($trust_root, $return_to); 349 350 if (Auth_OpenID::isFailure($redirect_url)) { 351 $openid->log->error('Could not redirect to server: '.$redirect_url->message); 352 } else { 353 wp_redirect( $redirect_url ); 354 } 355 } else { 356 // Generate form markup and render it 357 $request_message = $auth_request->getMessage($trust_root, $return_to, false); 358 359 if (Auth_OpenID::isFailure($request_message)) { 360 $openid->log->error('Could not redirect to server: '.$request_message->message); 361 } else { 362 WordPressOpenID_Interface::repost($auth_request->endpoint->server_url, $request_message->toPostArgs()); 363 } 364 } 365 } 366 367 368 /** 369 * Finish OpenID Authentication. 370 * 371 * @return String authenticated identity URL, or null if authentication failed. 372 */ 373 function finish_openid_auth() { 374 global $openid; 375 376 //set_error_handler( array('WordPressOpenID_Logic', 'customer_error_handler')); 377 $consumer = WordPressOpenID_Logic::getConsumer(); 378 $openid->response = $consumer->complete($_SESSION['oid_return_to']); 379 //restore_error_handler(); 380 381 switch( $openid->response->status ) { 382 case Auth_OpenID_CANCEL: 383 $openid->message = 'OpenID assertion cancelled'; 384 $openid->action = 'error'; 385 break; 386 387 case Auth_OpenID_FAILURE: 388 $openid->message = 'OpenID assertion failed: ' . $openid->response->message; 389 $openid->action = 'error'; 390 break; 391 392 case Auth_OpenID_SUCCESS: 393 $openid->message = 'OpenID assertion successful'; 394 $openid->action = 'success'; 395 396 $identity_url = $openid->response->identity_url; 397 $escaped_url = htmlspecialchars($identity_url, ENT_QUOTES); 398 $openid->log->notice('Got back identity URL ' . $escaped_url); 399 400 if ($openid->response->endpoint->canonicalID) { 401 $openid->log->notice('XRI CanonicalID: ' . $openid->response->endpoint->canonicalID); 402 } 403 404 return $escaped_url; 405 406 default: 407 $openid->message = 'Unknown Status. Bind not successful. This is probably a bug'; 408 $openid->action = 'error'; 409 } 410 411 return null; 412 } 413 414 415 /** 416 * Generate a unique WordPress username for the given OpenID URL. 417 * 418 * @param string $url OpenID URL to generate username for 419 * @return string generated username 420 */ 421 function generate_new_username($url) { 422 global $openid; 423 424 $base = WordPressOpenID_Logic::normalize_username($url); 425 $i=''; 426 while(true) { 427 $username = WordPressOpenID_Logic::normalize_username( $base . $i ); 428 $user = get_userdatabylogin($username); 429 if ( $user ) { 430 $i++; 431 continue; 432 } 433 return $username; 434 } 435 } 436 437 /** 438 * Normalize the OpenID URL into a username. This includes rules like: 439 * - remove protocol prefixes like 'http://' and 'xri://' 440 * - remove the 'xri.net' domain for i-names 441 * - substitute certain characters which are not allowed by WordPress 442 * 443 * @param string $username username to be normalized 444 * @return string normalized username 445 */ 446 function normalize_username($username) { 447 $username = preg_replace('|^https?://(xri.net/([^@]!?)?)?|', '', $username); 448 $username = preg_replace('|^xri://([^@]!?)?|', '', $username); 449 $username = preg_replace('|/$|', '', $username); 450 $username = sanitize_user( $username ); 451 $username = preg_replace('|[^a-z0-9 _.\-@]+|i', '-', $username); 452 return $username; 453 } 454 455 function begin_consumer($url) { 456 global $openid_auth_request; 457 458 if ($openid_auth_request == NULL) { 459 set_error_handler( array('WordPressOpenID_Logic', 'customer_error_handler')); 460 461 if (WordPressOpenID_Logic::isValidEmail($url)) { 462 $_SESSION['openid_login_email'] = $url; 463 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 464 require_once 'Auth/Yadis/Email.php'; 465 $mapped_url = Auth_Yadis_Email_getID($url, trailingslashit(get_option('home'))); 466 if ($mapped_url) { 467 $url = $mapped_url; 468 } 469 } 470 471 $consumer = WordPressOpenID_Logic::getConsumer(); 472 $openid_auth_request = $consumer->begin($url); 473 474 restore_error_handler(); 475 } 476 477 return $openid_auth_request;; 478 } 479 480 function isValidEmail($email) { 481 return eregi("^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$", $email); 482 } 483 484 485 /** 486 * Start the OpenID authentication process. 487 * 488 * @param string $claimed_url claimed OpenID URL 489 * @param action $action OpenID action being performed 490 * @param array $arguments array of additional arguments to be included in the 'return_to' URL 491 */ 492 function start_login( $claimed_url, $action, $arguments = null) { 493 global $openid; 494 495 if ( empty($claimed_url) ) return; // do nothing. 496 497 if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken 498 499 $auth_request = WordPressOpenID_Logic::begin_consumer( $claimed_url ); 500 501 if ( null === $auth_request ) { 502 $openid->action = 'error'; 503 $openid->message = 'Could not discover an OpenID identity server endpoint at the url: ' 504 . htmlentities( $claimed_url ); 505 if( strpos( $claimed_url, '@' ) ) { 506 $openid->message .= '<br />It looks like you entered an email address, but it ' 507 . 'was not able to be transformed into a valid OpenID.'; 508 } 509 $openid->log->debug('OpenIDConsumer: ' . $openid->message ); 510 return; 511 } 512 513 $openid->log->debug('OpenIDConsumer: Is an OpenID url. Starting redirect.'); 514 515 516 // build return_to URL 517 $return_to = trailingslashit(get_option('home')); 518 $auth_request->return_to_args['openid_consumer'] = '1'; 519 $auth_request->return_to_args['action'] = $action; 520 if (is_array($arguments) && !empty($arguments)) { 521 foreach ($arguments as $k => $v) { 522 if ($k && $v) { 523 $auth_request->return_to_args[urlencode($k)] = urlencode($v); 524 } 525 } 526 } 527 528 529 /* If we've never heard of this url before, do attribute query */ 530 $store =& WordPressOpenID_Logic::getStore(); 531 if( $store->get_user_by_identity( $auth_request->endpoint->identity_url ) == NULL ) { 532 $attribute_query = true; 533 } 534 if ($attribute_query) { 535 // SREG 536 $sreg_request = Auth_OpenID_SRegRequest::build(array(),array('nickname','email','fullname')); 537 if ($sreg_request) $auth_request->addExtension($sreg_request); 538 539 // AX 540 } 541 542 $_SESSION['oid_return_to'] = $return_to; 543 WordPressOpenID_Logic::doRedirect($auth_request, get_option('home'), $return_to); 544 exit(0); 545 } 546 547 548 /** 549 * Intercept login requests on wp-login.php if they include an 'openid_url' 550 * value and start OpenID authentication. This hook is only necessary in 551 * WordPress 2.5.x because it has the 'wp_authenticate' action call in the 552 * wrong place. 553 */ 554 function wp_login_openid() { 555 global $wp_version; 556 557 // this is only needed in WordPress 2.5.x 558 if (strpos($wp_version, '2.5') != 0) { 559 return; 560 } 561 562 $self = basename( $GLOBALS['pagenow'] ); 563 564 if ($self == 'wp-login.php' && !empty($_POST['openid_url'])) { 565 if (function_exists('wp_signon')) { 566 wp_signon(array('user_login'=>'openid', 'user_password'=>'openid')); 567 } 568 } 569 } 570 571 572 /** 573 * Login user with specified identity URL. This will find the WordPress user account connected to this 574 * OpenID and set it as the current user. Only call this function AFTER you've verified the identity URL. 575 * 576 * @param string $identity userID or OpenID to set as current user 577 * @param boolean $remember should we set the "remember me" cookie 578 * @return void 579 */ 580 function set_current_user($identity, $remember = true) { 581 global $openid; 582 583 if (is_numeric($identity)) { 584 $user_id = $identity; 585 } else { 586 $store =& WordPressOpenID_Logic::getStore(); 587 $user_id = $store->get_user_by_identity( $identity ); 588 } 589 590 if (!$user_id) return; 591 592 $user = set_current_user($user_id); 593 594 if (function_exists('wp_set_auth_cookie')) { 595 wp_set_auth_cookie($user->ID, $remember); 596 } else { 597 wp_setcookie($user->user_login, md5($user->user_pass), true, '', '', $remember); 598 } 599 600 do_action('wp_login', $user->user_login); 601 } 602 603 604 /** 605 * Finish OpenID authentication. After doing the basic stuff, the action method is called to complete the 606 * process. Action methods are based on the action name passed in and are of the form 607 * '_finish_openid_$action'. Action methods are passed the verified identity URL, or null if OpenID 608 * authentication failed. 609 * 610 * @param string $action login action that is being performed 611 */ 612 function finish_openid($action) { 613 global $openid; 614 615 if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken 616 617 $identity_url = WordPressOpenID_Logic::finish_openid_auth(); 618 619 if (!empty($action) && method_exists('WordPressOpenID_Logic', '_finish_openid_' . $action)) { 620 call_user_func(array('WordPressOpenID_Logic', '_finish_openid_' . $action), $identity_url); 621 } 622 623 global $action; 624 $action = $openid->action; 625 } 626 627 628 /** 629 * Action method for completing the 'login' action. This action is used when a user is logging in from 630 * wp-login.php. 631 * 632 * @param string $identity_url verified OpenID URL 633 */ 634 function _finish_openid_login($identity_url) { 635 global $openid; 636 637 $redirect_to = urldecode($_REQUEST['redirect_to']); 638 639 if (empty($identity_url)) { 640 WordPressOpenID_Logic::set_error('Unable to authenticate OpenID.'); 641 wp_safe_redirect(get_option('siteurl') . '/wp-login.php'); 642 exit; 643 } 644 645 WordPressOpenID_Logic::set_current_user($identity_url); 646 647 if (!is_user_logged_in()) { 648 if ( get_option('users_can_register') ) { 649 $user_data =& WordPressOpenID_Logic::get_user_data($identity_url); 650 $user = WordPressOpenID_Logic::create_new_user($identity_url, $user_data); 651 WordPressOpenID_Logic::set_current_user($user->ID); 652 } else { 653 // TODO - Start a registration loop in WPMU. 654 WordPressOpenID_Logic::set_error('OpenID authentication valid, but unable ' 655 . 'to find a WordPress account associated with this OpenID.<br /><br />' 656 . 'Enable "Anyone can register" to allow creation of new accounts via OpenID.'); 657 wp_safe_redirect(get_option('siteurl') . '/wp-login.php'); 658 exit; 659 } 660 661 } 662 663 if (empty($redirect_to)) { 664 $redirect_to = 'wp-admin/'; 665 } 666 if ($redirect_to == 'wp-admin/') { 667 if (!current_user_can('edit_posts')) { 668 $redirect_to .= 'profile.php'; 669 } 670 } 671 if (!preg_match('#^(http|\/)#', $redirect_to)) { 672 $wpp = parse_url(get_option('siteurl')); 673 $redirect_to = $wpp['path'] . '/' . $redirect_to; 674 } 675 676 if (function_exists('wp_safe_redirect')) { 677 wp_safe_redirect( $redirect_to ); 678 } else { 679 wp_redirect( $redirect_to ); 680 } 681 682 exit; 683 } 684 685 686 /** 687 * Action method for completing the 'comment' action. This action is used when leaving a comment. 688 * 689 * @param string $identity_url verified OpenID URL 690 */ 691 function _finish_openid_comment($identity_url) { 692 global $openid; 693 694 if (empty($identity_url)) { 695 WordPressOpenID_Interface::repost_comment_anonymously($_SESSION['oid_comment_post']); 696 } 697 698 WordPressOpenID_Logic::set_current_user($identity_url); 699 700 if (is_user_logged_in()) { 701 // simulate an authenticated comment submission 702 $_SESSION['oid_comment_post']['author'] = null; 703 $_SESSION['oid_comment_post']['email'] = null; 704 $_SESSION['oid_comment_post']['url'] = null; 705 } else { 706 // try to get user data from the verified OpenID 707 $user_data =& WordPressOpenID_Logic::get_user_data($identity_url); 708 709 if (!empty($user_data['display_name'])) { 710 $_SESSION['oid_comment_post']['author'] = $user_data['display_name']; 711 } 712 if (!empty($user_data['user_email'])) { 713 $_SESSION['oid_comment_post']['email'] = $user_data['user_email']; 714 } 715 $_SESSION['oid_comment_post']['url'] = $identity_url; 716 } 717 718 // record that we're about to post an OpenID authenticated comment. 719 // We can't actually record it in the database until after the repost below. 720 $_SESSION['oid_posted_comment'] = true; 721 722 $wpp = parse_url(get_option('siteurl')); 723 WordPressOpenID_Interface::repost($wpp['path'] . '/wp-comments-post.php', 724 array_filter($_SESSION['oid_comment_post'])); 725 } 726 727 728 /** 729 * Action method for completing the 'verify' action. This action is used adding an identity URL to a 730 * WordPress user through the admin interface. 731 * 732 * @param string $identity_url verified OpenID URL 733 */ 734 function _finish_openid_verify($identity_url) { 735 global $openid; 736 737 $user = wp_get_current_user(); 738 if (empty($identity_url)) { 739 WordPressOpenID_Logic::set_error('Unable to authenticate OpenID.'); 740 } else { 741 $store =& WordPressOpenID_Logic::getStore(); 742 if( !$store->insert_identity($user->ID, $identity_url) ) { 743 WordPressOpenID_Logic::set_error('OpenID assertion successful, but this URL is already claimed by ' 744 . 'another user on this blog. This is probably a bug. ' . $identity_url); 745 } else { 746 $openid->action = 'success'; 747 $openid->message = "Successfully added Identity URL: $identity_url."; 748 749 // ensure that profile URL is a verified Identity URL 750 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 751 require_once 'Auth/OpenID.php'; 752 if ($GLOBALS['wp_version'] >= '2.3') { 753 require_once(ABSPATH . 'wp-admin/includes/admin.php'); 754 } else { 755 require_once(ABSPATH . WPINC . '/registration.php'); 756 } 757 $identities = $store->get_identities($user->ID); 758 $current_url = Auth_OpenID::normalizeUrl($user->user_url); 759 760 $verified_url = false; 761 if (!empty($identities)) { 762 foreach ($identities as $id) { 763 if ($id['url'] == $current_url) { 764 $verified_url = true; 765 break; 766 } 767 } 768 769 if (!$verified_url) { 770 $user->user_url = $identity_url; 771 wp_update_user( get_object_vars( $user )); 772 $openid->message .= '<br /><strong>Note:</strong> For security reasons, your profile URL has been updated to match your Identity URL.'; 773 } 774 } 775 } 776 } 777 778 $_SESSION['oid_message'] = $openid->message; 779 $_SESSION['oid_action'] = $openid->action; 780 $wpp = parse_url(get_option('siteurl')); 781 $redirect_to = $wpp['path'] . '/wp-admin/' . (current_user_can('edit_users') ? 'users.php' : 'profile.php') . '?page=openid'; 782 if (function_exists('wp_safe_redirect')) { 783 wp_safe_redirect( $redirect_to ); 784 } else { 785 wp_redirect( $redirect_to ); 786 } 787 exit; 788 } 789 790 791 /** 792 * If last comment was authenticated by an OpenID, record that in the database. 793 * 794 * @param string $location redirect location 795 * @param object $comment comment that was just left 796 * @return string redirect location 797 */ 798 function comment_post_redirect($location, $comment) { 799 global $openid; 800 801 if ($_SESSION['oid_posted_comment']) { 802 WordPressOpenID_Logic::set_comment_openid($comment->comment_ID); 803 $_SESSION['oid_posted_comment'] = null; 804 } 805 806 return $location; 807 } 808 809 810 /** 811 * Create a new WordPress user with the specified identity URL and user data. 812 * 813 * @param string $identity_url OpenID to associate with the newly 814 * created account 815 * @param array $user_data array of user data 816 */ 817 function create_new_user($identity_url, &$user_data) { 818 global $wpdb, $openid; 819 820 // Identity URL is new, so create a user 821 @include_once( ABSPATH . 'wp-admin/upgrade-functions.php'); // 2.1 822 @include_once( ABSPATH . WPINC . '/registration-functions.php'); // 2.0.4 823 824 // use email address for username if URL is from emailtoid.net 825 $username = $identity_url; 826 if (null != $_SESSION['openid_login_email'] and strpos($username, 'http://emailtoid.net/') == 0) { 827 if($user_data['user_email'] == NULL) { 828 $user_data['user_email'] = $_SESSION['openid_login_email']; 829 } 830 $username = $_SESSION['openid_login_email']; 831 unset($_SESSION['openid_login_email']); 832 } 833 834 $user_data['user_login'] = $wpdb->escape( WordPressOpenID_Logic::generate_new_username($username) ); 835 $user_data['user_pass'] = substr( md5( uniqid( microtime() ) ), 0, 7); 836 $user_id = wp_insert_user( $user_data ); 837 838 $openid->log->debug("wp_create_user( $user_data ) returned $user_id "); 839 840 if( $user_id ) { // created ok 841 842 $user_data['ID'] = $user_id; 843 // XXX this all looks redundant, see WordPressOpenID_Logic::set_current_user 844 845 $openid->log->debug("OpenIDConsumer: Created new user $user_id : " . $user_data['user_login'] . " and metadata: " 846 . var_export( $user_data, true ) ); 847 848 $user = new WP_User( $user_id ); 849 850 if( ! wp_login( $user->user_login, $user_data['user_pass'] ) ) { 851 $openid->message = 'User was created fine, but wp_login() for the new user failed. ' 852 . 'This is probably a bug.'; 853 $openid->action= 'error'; 854 $openid->log->err( $openid->message ); 855 return; 856 } 857 858 // notify of user creation 859 wp_new_user_notification( $user->user_login ); 860 861 wp_clearcookie(); 862 wp_setcookie( $user->user_login, md5($user->user_pass), true, '', '', true ); 863 864 // Bind the provided identity to the just-created user 865 global $userdata; 866 $userdata = get_userdata( $user_id ); 867 $store = WordPressOpenID_Logic::getStore(); 868 $store->insert_identity($user_id, $identity_url); 869 870 $openid->action = 'redirect'; 871 872 if ( !$user->has_cap('edit_posts') ) $redirect_to = '/wp-admin/profile.php'; 873 874 } else { 875 // failed to create user for some reason. 876 $openid->message = 'OpenID authentication successful, but failed to create WordPress user. ' 877 . 'This is probably a bug.'; 878 $openid->action= 'error'; 879 $openid->log->error( $openid->message ); 880 } 881 882 } 883 884 885 /** 886 * Get user data for the given identity URL. Data is returned as an associative array with the keys: 887 * ID, user_url, user_nicename, display_name 888 * 889 * Multiple soures of data may be available and are attempted in the following order: 890 * - OpenID Attribute Exchange !! not yet implemented 891 * - OpenID Simple Registration 892 * - hCard discovery !! not yet implemented 893 * - default to identity URL 894 * 895 * @param string $identity_url OpenID to get user data about 896 * @return array user data 897 */ 898 function get_user_data($identity_url) { 899 global $openid; 900 901 $data = array( 902 'ID' => null, 903 'user_url' => $identity_url, 904 'user_nicename' => $identity_url, 905 'display_name' => $identity_url 906 ); 907 908 // create proper website URL if OpenID is an i-name 909 if (preg_match('/^[\=\@\+].+$/', $identity_url)) { 910 $data['user_url'] = 'http://xri.net/' . $identity_url; 911 } 912 913 $data = apply_filters('openid_user_data', $identity_url, $data); 914 915 return $data; 916 } 917 918 919 /** 920 * Retrieve user data from OpenID Attribute Exchange. 921 * 922 * @param string $identity_url OpenID to get user data about 923 * @param reference $data reference to user data array 924 * @see get_user_data 925 */ 926 function get_user_data_ax($identity_url, $data) { 927 // TODO implement attribute exchange 928 return $data; 929 } 930 931 932 /** 933 * Retrieve user data from OpenID Simple Registration. 934 * 935 * @param string $identity_url OpenID to get user data about 936 * @param reference $data reference to user data array 937 * @see get_user_data 938 */ 939 function get_user_data_sreg($identity_url, $data) { 940 global $openid; 941 942 $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($openid->response); 943 $sreg = $sreg_resp->contents(); 944 945 $openid->log->debug(var_export($sreg, true)); 946 if (!$sreg) return $data; 947 948 if (array_key_exists('email', $sreg) && $sreg['email']) { 949 $data['user_email'] = $sreg['email']; 950 } 951 952 if (array_key_exists('nickname', $sreg) && $sreg['nickname']) { 953 $data['nickname'] = $sreg['nickname']; 954 $data['user_nicename'] = $sreg['nickname']; 955 $data['display_name'] = $sreg['nickname']; 956 } 957 958 if (array_key_exists('fullname', $sreg) && $sreg['fullname']) { 959 $namechunks = explode( ' ', $sreg['fullname'], 2 ); 960 if( isset($namechunks[0]) ) $data['first_name'] = $namechunks[0]; 961 if( isset($namechunks[1]) ) $data['last_name'] = $namechunks[1]; 962 $data['display_name'] = $sreg['fullname']; 963 } 964 965 return $data;; 966 } 967 968 969 /** 970 * Retrieve user data from hCard discovery. 971 * 972 * @param string $identity_url OpenID to get user data about 973 * @param reference $data reference to user data array 974 * @see get_user_data 975 */ 976 function get_user_data_hcard($identity_url, $data) { 977 // TODO implement hcard discovery 978 return $data; 979 } 980 981 /** 982 * Retrieve user data from comment form. 983 * 984 * @param string $identity_url OpenID to get user data about 985 * @param reference $data reference to user data array 986 * @see get_user_data 987 */ 988 function get_user_data_form($identity_url, $data) { 989 $comment = $_SESSION['oid_comment_post']; 990 991 if (!$comment) { 992 return $data; 993 } 994 995 if ($comment['email']) { 996 $data['user_email'] = $comment['email']; 997 } 998 999 if ($comment['author']) { 1000 $data['nickname'] = $comment['author']; 1001 $data['user_nicename'] = $comment['author']; 1002 $data['display_name'] = $comment['author']; 1003 } 1004 1005 return $data; 1006 } 1007 1008 1009 /** 1010 * For comments that were handled by WordPress normally (not our code), check if the author 1011 * registered with OpenID and set comment openid flag if so. 1012 * 1013 * @action post_comment 1014 */ 1015 function check_author_openid($comment_ID) { 1016 global $openid; 1017 1018 $comment = get_comment($comment_ID); 1019 if ( $comment->user_id && !$comment->openid && is_user_openid($comment->user_id) ) { 1020 WordPressOpenID_Logic::set_comment_openid($comment_ID); 1021 } 1022 } 1023 1024 /** 1025 * hook in and call when user is updating their profile URL... make sure it is an OpenID they control. 1026 */ 1027 function personal_options_update() { 1028 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 1029 require_once 'Auth/OpenID.php'; 1030 $claimed = Auth_OpenID::normalizeUrl($_POST['url']); 1031 1032 $user = wp_get_current_user(); 1033 1034 openid_init(); 1035 $store =& WordPressOpenID_Logic::getStore(); 1036 $identities = $store->get_identities($user->ID); 1037 1038 if (!empty($identities)) { 1039 $urls = array(); 1040 foreach ($identities as $id) { 1041 if ($id['url'] == $claimed) { 1042 return; 1043 } else { 1044 $urls[] = $id['url']; 1045 } 1046 } 1047 1048 wp_die('For security reasons, your profile URL must be one of your claimed ' 1049 . 'Identity URLs: <ul><li>' . join('</li><li>', $urls) . '</li></ul>'); 1050 } 1051 } 1052 1053 1054 /** 1055 * Mark the provided comment as an OpenID comment 1056 * 1057 * @param int $comment_ID id of comment to set as OpenID 1058 */ 1059 function set_comment_openid($comment_ID) { 1060 global $wpdb, $openid; 1061 1062 $comments_table = WordPressOpenID::comments_table_name(); 1063 $wpdb->query("UPDATE $comments_table SET openid='1' WHERE comment_ID='$comment_ID' LIMIT 1"); 1064 } 1065 1066 1067 /** 1068 * If the comment contains a valid OpenID, skip the check for requiring a name and email address. Even if 1069 * this data is provided in the form, we may get it through other methods, so we don't want to bail out 1070 * prematurely. After OpenID authentication has completed (and $_SESSION['oid_skip'] is set), we don't 1071 * interfere so that this data can be required if desired. 1072 * 1073 * @param boolean $value existing value of flag, whether to require name and email 1074 * @return boolean new value of flag, whether to require name and email 1075 * @see get_user_data 1076 */ 1077 function bypass_option_require_name_email( $value ) { 1078 global $openid; 1079 1080 if ($_REQUEST['oid_skip']) { 1081 return $value; 1082 } 1083 1084 if (array_key_exists('openid_url', $_POST)) { 1085 if( !empty( $_POST['openid_url'] ) ) { 1086 return false; 1087 } 1088 } else { 1089 if (!empty($_POST['url'])) { 1090 if (WordPressOpenID_Logic::late_bind()) { 1091 // check if url is valid OpenID by forming an auth request 1092 $auth_request = WordPressOpenID_Logic::begin_consumer($_POST['url']); 1093 1094 if (null !== $auth_request) { 1095 return false; 1096 } 1097 } 1098 } 1099 } 1100 1101 return $value; 1102 } 1103 1104 1105 /** 1106 * Intercept comment submission and check if it includes a valid OpenID. If it does, save the entire POST 1107 * array and begin the OpenID authentication process. 1108 * 1109 * regarding comment_type: http://trac.wordpress.org/ticket/2659 1110 * 1111 * @param object $comment comment object 1112 * @return object comment object 1113 */ 1114 function comment_tagging( $comment ) { 1115 global $openid; 1116 1117 if ($_REQUEST['oid_skip']) return $comment; 1118 1119 $openid_url = (array_key_exists('openid_url', $_POST) ? $_POST['openid_url'] : $_POST['url']); 1120 1121 if( !empty($openid_url) ) { // Comment form's OpenID url is filled in. 1122 $_SESSION['oid_comment_post'] = $_POST; 1123 $_SESSION['oid_comment_post']['comment_author_openid'] = $openid_url; 1124 $_SESSION['oid_comment_post']['oid_skip'] = 1; 1125 1126 WordPressOpenID_Logic::start_login( $openid_url, 'comment'); 1127 1128 // Failure to redirect at all, the URL is malformed or unreachable. 1129 1130 // Display an error message only if an explicit OpenID field was used. Otherwise, 1131 // just ignore the error... it just means the user entered a normal URL. 1132 if (array_key_exists('openid_url', $_POST)) { 1133 WordPressOpenID_Interface::repost_comment_anonymously($_SESSION['oid_comment_post']); 1134 } 1135 } 1136 1137 /* 1138 if (get_option('oid_enable_email_mapping') && !empty($_POST['email'])) { 1139 $_SESSION['oid_comment_post'] = $_POST; 1140 $_SESSION['oid_comment_post']['comment_author_openid'] = $openid_url; 1141 $_SESSION['oid_comment_post']['oid_skip'] = 1; 1142 1143 set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); 1144 require_once 'Auth/Yadis/Email.php'; 1145 $id = Auth_Yadis_Email_getID($_POST['email'], trailingslashit(get_option('home'))); 1146 WordPressOpenID_Logic::start_login( $id, 'comment'); 1147 } 1148 */ 1149 1150 return $comment; 1151 } 1152 1153 1154 /** 1155 * This filter callback simply approves all OpenID comments, but later it could do more complicated logic 1156 * like whitelists. 1157 * 1158 * @param string $approved comment approval status 1159 * @return string new comment approval status 1160 */ 1161 function comment_approval($approved) { 1162 if ($_SESSION['oid_posted_comment']) { 1163 return 1; 1164 } 1165 1166 return $approved; 1167 } 1168 1169 1170 /** 1171 * Get any additional comments awaiting moderation by this user. WordPress 1172 * core has been udpated to grab most, but we still do one last check for 1173 * OpenID comments that have a URL match with the current user. 1174 * 1175 * @param array $comments array of comments to display 1176 * @param int $post_id id of the post to display comments for 1177 * @return array new array of comments to display 1178 */ 1179 function comments_awaiting_moderation(&$comments, $post_id) { 1180 global $wpdb, $openid; 1181 $user = wp_get_current_user(); 1182 1183 $commenter = wp_get_current_commenter(); 1184 extract($commenter); 1185 1186 $author_db = $wpdb->escape($comment_author); 1187 $email_db = $wpdb->escape($comment_author_email); 1188 $url_db = $wpdb->escape($comment_author_url); 1189 1190 if ($url_db) { 1191 $comments_table = WordPressOpenID::comments_table_name(); 1192 $additional = $wpdb->get_results( 1193 "SELECT * FROM $comments_table" 1194 . " WHERE comment_post_ID = '$post_id'" 1195 . " AND openid = '1'" // get OpenID comments 1196 . " AND comment_author_url = '$url_db'" // where only the URL matches 1197 . ($user ? " AND user_id != '$user->ID'" : '') 1198 . ($author_db ? " AND comment_author != '$author_db'" : '') 1199 . ($email_db ? " AND comment_author_email != '$email_db'" : '') 1200 . " AND comment_approved = '0'" 1201 . " ORDER BY comment_date"); 1202 1203 if ($additional) { 1204 $comments = array_merge($comments, $additional); 1205 usort($comments, create_function('$a,$b', 1206 'return strcmp($a->comment_date_gmt, $b->comment_date_gmt);')); 1207 } 1208 } 1209 1210 return $comments; 1211 } 1212 1213 /** 1214 * Delete user. 1215 */ 1216 function delete_user($userid) { 1217 openid_init(); 1218 $store = WordPressOpenID_Logic::getStore(); 1219 $store->drop_all_identities_for_user($userid); 1220 } 1221 1222 1223 /** 1224 * Make sure that a user's OpenID is stored and retrieved properly. This is important because the OpenID 1225 * may be an i-name, but WordPress is expecting the comment URL cookie to be a valid URL. 1226 * 1227 * @wordpress-action sanitize_comment_cookies 1228 */ 1229 function sanitize_comment_cookies() { 1230 if ( isset($_COOKIE['comment_author_openid_'.COOKIEHASH]) ) { 1231 1232 // this might be an i-name, so we don't want to run clean_url() 1233 remove_filter('pre_comment_author_url', 'clean_url'); 1234 1235 $comment_author_url = apply_filters('pre_comment_author_url', 1236 $_COOKIE['comment_author_openid_'.COOKIEHASH]); 1237 $comment_author_url = stripslashes($comment_author_url); 1238 $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url; 1239 } 1240 } 1241 1242 1243 function xrds_simple($xrds) { 1244 $xrds = xrds_add_service($xrds, 'main', 'OpenID Consumer Service', 1245 array( 1246 'Type' => array(array('content' => 'http://specs.openid.net/auth/2.0/return_to') ), 1247 'URI' => array(array('content' => trailingslashit(get_option('home'))) ), 1248 ) 1249 ); 1250 1251 $siteurl = function_exists('site_url') ? site_url('/wp-login.php', 'login_post') : get_option('siteurl').'/wp-login.php'; 1252 $xrds = xrds_add_service($xrds, 'main', 'Identity in the Browser Login Service', 1253 array( 1254 'Type' => array(array('content' => 'http://specs.openid.net/idib/1.0/login') ), 1255 'URI' => array( 1256 array( 1257 'simple:httpMethod' => 'POST', 1258 'content' => $siteurl, 1259 ), 1260 ), 1261 ) 1262 ); 1263 1264 $xrds = xrds_add_service($xrds, 'main', 'Identity in the Browser Indicator Service', 1265 array( 1266 'Type' => array(array('content' => 'http://specs.openid.net/idib/1.0/indicator') ), 1267 'URI' => array(array('content' => trailingslashit(get_option('home')) . '?openid_check_login')), 1268 ) 1269 ); 1270 1271 return $xrds; 1272 } 1273 1274 /** 1275 * Parse the WordPress request. If the pagename is 'openid_consumer', then the request 1276 * is an OpenID response and should be handled accordingly. 1277 * 1278 * @param WP $wp WP instance for the current request 1279 */ 1280 function parse_request($wp) { 1281 if (array_key_exists('openid_check_login', $_REQUEST)) { 1282 echo is_user_logged_in() ? 'true' : 'false'; 1283 exit; 1284 } 1285 1286 if (array_key_exists('openid_consumer', $_REQUEST) && $_REQUEST['action']) { 1287 openid_init(); 1288 WordPressOpenID_Logic::finish_openid($_REQUEST['action']); 1289 } 1290 } 1291 1292 function set_error($error) { 1293 $_SESSION['oid_error'] = $error; 1294 return; 1295 } 1296 1297} // end class definition 1298endif; // end if-class-exists test 1299 1300?>