PageRenderTime 88ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/openid/logic.php

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