PageRenderTime 52ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/app/omb/plugins/wp-plugins/plugins/enabled/wp-openid/logic.php

https://github.com/tjgillies/openmicroblogger
PHP | 1096 lines | 615 code | 214 blank | 267 comment | 129 complexity | d5b176580b68ab27349676e4820abee6 MD5 | raw file
Possible License(s): 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->error = '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 $wp_rewrite, $openid;
  119. openid_init();
  120. $store =& WordPressOpenID_Logic::getStore();
  121. $store->create_tables();
  122. //add_filter('generate_rewrite_rules', array('WordPressOpenID_Logic', 'rewrite_rules'));
  123. //$wp_rewrite->flush_rules();
  124. wp_schedule_event(time(), 'hourly', 'cleanup_openid');
  125. $openid->log->warning("activation memory usage: " . (int)((memory_get_usage() - $start_mem) / 1000));
  126. }
  127. function cleanup_nonces() {
  128. global $openid;
  129. openid_init();
  130. $store =& WordPressOpenID_Logic::getStore();
  131. $store->cleanupNonces();
  132. }
  133. /**
  134. * Called on plugin deactivation. Cleanup all transient tables.
  135. *
  136. * @see register_deactivation_hook
  137. */
  138. function deactivate_plugin() {
  139. set_include_path( dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
  140. require_once 'store.php';
  141. WordPressOpenID_Store::destroy_tables();
  142. }
  143. /*
  144. * Customer error handler for calls into the JanRain library
  145. */
  146. function customer_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
  147. global $openid;
  148. if( (2048 & $errno) == 2048 ) return;
  149. $openid->log->notice( "Library Error $errno: $errmsg in $filename :$linenum");
  150. }
  151. /**
  152. * If we're doing openid authentication ($_POST['openid_url'] is set), start the consumer & redirect
  153. * Otherwise, return and let WordPress handle the login and/or draw the form.
  154. *
  155. * @param string $username username provided in login form
  156. */
  157. function wp_authenticate( &$username ) {
  158. global $openid;
  159. if( !empty( $_POST['openid_url'] ) ) {
  160. if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken
  161. $redirect_to = '';
  162. if( !empty( $_REQUEST['redirect_to'] ) ) $redirect_to = $_REQUEST['redirect_to'];
  163. WordPressOpenID_Logic::start_login( $_POST['openid_url'], 'login', array('redirect_to' => $redirect_to) );
  164. }
  165. if( !empty( $openid->error ) ) {
  166. global $error;
  167. $error = $openid->error;
  168. }
  169. }
  170. /**
  171. * Handle OpenID profile management.
  172. */
  173. function openid_profile_management() {
  174. global $wp_version, $openid;
  175. openid_init();
  176. if( !isset( $_REQUEST['action'] )) return;
  177. $openid->action = $_REQUEST['action'];
  178. require_once(ABSPATH . 'wp-admin/admin-functions.php');
  179. if ($wp_version < '2.3') {
  180. require_once(ABSPATH . 'wp-admin/admin-db.php');
  181. require_once(ABSPATH . 'wp-admin/upgrade-functions.php');
  182. }
  183. auth_redirect();
  184. nocache_headers();
  185. get_currentuserinfo();
  186. if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken
  187. switch( $openid->action ) {
  188. case 'add_identity':
  189. check_admin_referer('wp-openid-add_identity');
  190. $user = wp_get_current_user();
  191. $store =& WordPressOpenID_Logic::getStore();
  192. global $openid_auth_request;
  193. if ($openid_auth_request == NULL) {
  194. $consumer = WordPressOpenID_Logic::getConsumer();
  195. $openid_auth_request = $consumer->begin($_POST['openid_url']);
  196. }
  197. $userid = $store->get_user_by_identity($openid_auth_request->endpoint->claimed_id);
  198. if ($userid) {
  199. global $error;
  200. if ($user->ID == $userid) {
  201. $error = 'You already have this openid!';
  202. } else {
  203. $error = 'This OpenID is already connected to another user.';
  204. }
  205. return;
  206. }
  207. WordPressOpenID_Logic::start_login($_POST['openid_url'], 'verify');
  208. break;
  209. case 'drop_identity': // Remove a binding.
  210. WordPressOpenID_Logic::_profile_drop_identity($_REQUEST['id']);
  211. break;
  212. }
  213. }
  214. /**
  215. * Remove identity URL from current user account.
  216. *
  217. * @param int $id id of identity URL to remove
  218. */
  219. function _profile_drop_identity($id) {
  220. global $openid;
  221. $user = wp_get_current_user();
  222. if( !isset($id)) {
  223. $openid->error = 'Identity url delete failed: ID paramater missing.';
  224. return;
  225. }
  226. $store =& WordPressOpenID_Logic::getStore();
  227. $deleted_identity_url = $store->get_identities($user->ID, $id);
  228. if( FALSE === $deleted_identity_url ) {
  229. $openid->error = 'Identity url delete failed: Specified identity does not exist.';
  230. return;
  231. }
  232. $identity_urls = $store->get_identities($user->ID);
  233. if (sizeof($identity_urls) == 1 && !$_REQUEST['confirm']) {
  234. $openid->error = '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 /> '
  235. . '<a href="?confirm=true&'.$_SERVER['QUERY_STRING'].'">Yes I\'m sure. Delete it</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
  236. . '<a href="?page=openid">No, don\'t delete it.</a>';
  237. $openid->action = 'warning';
  238. return;
  239. }
  240. check_admin_referer('wp-openid-drop-identity_'.$deleted_identity_url);
  241. if( $store->drop_identity($user->ID, $id) ) {
  242. $openid->error = 'Identity url delete successful. <b>' . $deleted_identity_url
  243. . '</b> removed.';
  244. $openid->action = 'success';
  245. return;
  246. }
  247. $openid->error = 'Identity url delete failed: Unknown reason.';
  248. }
  249. /**
  250. * Send the user to their OpenID provider to authenticate.
  251. *
  252. * @param Auth_OpenID_AuthRequest $auth_request OpenID authentication request object
  253. * @param string $trust_root OpenID trust root
  254. * @param string $return_to URL where the OpenID provider should return the user
  255. */
  256. function doRedirect($auth_request, $trust_root, $return_to) {
  257. global $openid;
  258. if ($auth_request->shouldSendRedirect()) {
  259. if (substr($trust_root, -1, 1) != '/') $trust_root .= '/';
  260. $redirect_url = $auth_request->redirectURL($trust_root, $return_to);
  261. if (Auth_OpenID::isFailure($redirect_url)) {
  262. $openid->log->error('Could not redirect to server: '.$redirect_url->message);
  263. } else {
  264. wp_redirect( $redirect_url );
  265. }
  266. } else {
  267. // Generate form markup and render it
  268. $form_id = 'openid_message';
  269. $form_html = $auth_request->formMarkup($trust_root, $return_to, false);
  270. if (Auth_OpenID::isFailure($form_html)) {
  271. $openid->log->error('Could not redirect to server: '.$form_html->message);
  272. } else {
  273. WordPressOpenID_Interface::display_openid_redirect_form($form_html);
  274. }
  275. }
  276. }
  277. /**
  278. * Finish OpenID Authentication.
  279. *
  280. * @return String authenticated identity URL, or null if authentication failed.
  281. */
  282. function finish_openid_auth() {
  283. global $openid;
  284. //set_error_handler( array('WordPressOpenID_Logic', 'customer_error_handler'));
  285. $consumer = WordPressOpenID_Logic::getConsumer();
  286. echo 'ouh on'.$_SESSION['oid_return_to']; exit;
  287. $openid->response = $consumer->complete($_SESSION['oid_return_to']);
  288. //restore_error_handler();
  289. switch( $openid->response->status ) {
  290. case Auth_OpenID_CANCEL:
  291. $openid->error = 'OpenID assertion cancelled';
  292. break;
  293. case Auth_OpenID_FAILURE:
  294. $openid->error = 'OpenID assertion failed: ' . $openid->response->message;
  295. break;
  296. case Auth_OpenID_SUCCESS:
  297. $openid->error = 'OpenID assertion successful';
  298. $identity_url = $openid->response->identity_url;
  299. $escaped_url = htmlspecialchars($identity_url, ENT_QUOTES);
  300. $openid->log->notice('Got back identity URL ' . $escaped_url);
  301. if ($openid->response->endpoint->canonicalID) {
  302. $openid->log->notice('XRI CanonicalID: ' . $openid->response->endpoint->canonicalID);
  303. }
  304. return $escaped_url;
  305. default:
  306. $openid->error = 'Unknown Status. Bind not successful. This is probably a bug';
  307. }
  308. return null;
  309. }
  310. /**
  311. * Generate a unique WordPress username for the given OpenID URL.
  312. *
  313. * @param string $url OpenID URL to generate username for
  314. * @return string generated username
  315. */
  316. function generate_new_username($url) {
  317. global $openid;
  318. $base = WordPressOpenID_Logic::normalize_username($url);
  319. $i='';
  320. while(true) {
  321. $username = WordPressOpenID_Logic::normalize_username( $base . $i );
  322. $user = get_userdatabylogin($username);
  323. if ( $user ) {
  324. $i++;
  325. continue;
  326. }
  327. return $username;
  328. }
  329. }
  330. /**
  331. * Normalize the OpenID URL into a username. This includes rules like:
  332. * - remove protocol prefixes like 'http://' and 'xri://'
  333. * - remove the 'xri.net' domain for i-names
  334. * - substitute certain characters which are not allowed by WordPress
  335. *
  336. * @param string $username username to be normalized
  337. * @return string normalized username
  338. */
  339. function normalize_username($username) {
  340. $username = preg_replace('|^https?://(xri.net/([^@]!?)?)?|', '', $username);
  341. $username = preg_replace('|^xri://([^@]!?)?|', '', $username);
  342. $username = preg_replace('|/$|', '', $username);
  343. $username = sanitize_user( $username );
  344. $username = preg_replace('|[^a-z0-9 _.\-@]+|i', '-', $username);
  345. return $username;
  346. }
  347. /**
  348. * Start the OpenID authentication process.
  349. *
  350. * @param string $claimed_url claimed OpenID URL
  351. * @param action $action OpenID action being performed
  352. * @param array $arguments array of additional arguments to be included in the 'return_to' URL
  353. */
  354. function start_login( $claimed_url, $action, $arguments = null) {
  355. global $openid;
  356. if ( empty($claimed_url) ) return; // do nothing.
  357. if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken
  358. if ( null !== $openid_auth_request) {
  359. $auth_request = $openid_auth_request;
  360. } else {
  361. set_error_handler( array('WordPressOpenID_Logic', 'customer_error_handler'));
  362. $consumer = WordPressOpenID_Logic::getConsumer();
  363. $auth_request = $consumer->begin( $claimed_url );
  364. restore_error_handler();
  365. }
  366. if ( null === $auth_request ) {
  367. $openid->error = 'Could not discover an OpenID identity server endpoint at the url: '
  368. . htmlentities( $claimed_url );
  369. if( strpos( $claimed_url, '@' ) ) {
  370. $openid->error .= '<br/>The address you specified had an @ sign in it, but OpenID '
  371. . 'Identities are not email addresses, and should probably not contain an @ sign.';
  372. }
  373. $openid->log->debug('OpenIDConsumer: ' . $openid->error );
  374. return;
  375. }
  376. $openid->log->debug('OpenIDConsumer: Is an OpenID url. Starting redirect.');
  377. // build return_to URL
  378. $return_to = get_option('home') . '/openid_consumer';
  379. $auth_request->return_to_args['action'] = $action;
  380. if (is_array($arguments) && !empty($arguments)) {
  381. foreach ($arguments as $k => $v) {
  382. if ($k && $v) {
  383. $auth_request->return_to_args[urlencode($k)] = urlencode($v);
  384. }
  385. }
  386. }
  387. /* If we've never heard of this url before, do attribute query */
  388. $store =& WordPressOpenID_Logic::getStore();
  389. if( $store->get_user_by_identity( $auth_request->endpoint->identity_url ) == NULL ) {
  390. $attribute_query = true;
  391. }
  392. if ($attribute_query) {
  393. // SREG
  394. $sreg_request = Auth_OpenID_SRegRequest::build(array(),array('nickname','email','fullname'));
  395. if ($sreg_request) $auth_request->addExtension($sreg_request);
  396. // AX
  397. }
  398. $_SESSION['oid_return_to'] = $return_to;
  399. WordPressOpenID_Logic::doRedirect($auth_request, get_option('home'), $return_to);
  400. exit(0);
  401. }
  402. /**
  403. * Intercept login requests on wp-login.php if they include an 'openid_url' value and start OpenID
  404. * authentication.
  405. */
  406. function wp_login_openid() {
  407. $self = basename( $GLOBALS['pagenow'] );
  408. if ($self == 'wp-login.php' && !empty($_POST['openid_url'])) {
  409. // TODO wp_signon only exists in wp2.5+
  410. wp_signon(array('user_login'=>'openid', 'user_password'=>'openid'));
  411. }
  412. }
  413. /**
  414. * Login user with specified identity URL. This will find the WordPress user account connected to this
  415. * OpenID and set it as the current user. Only call this function AFTER you've verified the identity URL.
  416. *
  417. * @param string $identity_url OpenID to set as current user
  418. * @param boolean $remember should we set the "remember me" cookie
  419. * @return void
  420. */
  421. function set_current_user($identity_url, $remember = true) {
  422. global $openid;
  423. $store =& WordPressOpenID_Logic::getStore();
  424. $user_id = $store->get_user_by_identity( $identity_url );
  425. if (!$user_id) return;
  426. $user = set_current_user($user_id);
  427. if (function_exists('wp_set_auth_cookie')) {
  428. wp_set_auth_cookie($user->ID, $remember);
  429. } else {
  430. wp_setcookie($user->user_login, $user->user_pass, false, '', '', $remember);
  431. }
  432. do_action('wp_login', $user->user_login);
  433. }
  434. /**
  435. * Finish OpenID authentication. After doing the basic stuff, the action method is called to complete the
  436. * process. Action methods are based on the action name passed in and are of the form
  437. * '_finish_openid_$action'. Action methods are passed the verified identity URL, or null if OpenID
  438. * authentication failed.
  439. *
  440. * @param string $action login action that is being performed
  441. */
  442. function finish_openid($action) {
  443. global $openid;
  444. if( !WordPressOpenID_Logic::late_bind() ) return; // something is broken
  445. $identity_url = WordPressOpenID_Logic::finish_openid_auth();
  446. if (!empty($action) && method_exists('WordPressOpenID_Logic', '_finish_openid_' . $action)) {
  447. call_user_func(array('WordPressOpenID_Logic', '_finish_openid_' . $action), $identity_url);
  448. }
  449. global $action;
  450. $action = $openid->action;
  451. }
  452. /**
  453. * Action method for completing the 'login' action. This action is used when a user is logging in from
  454. * wp-login.php.
  455. *
  456. * @param string $identity_url verified OpenID URL
  457. */
  458. function _finish_openid_login($identity_url) {
  459. global $openid;
  460. $redirect_to = urldecode($_REQUEST['redirect_to']);
  461. if (empty($identity_url)) {
  462. // FIXME unable to authenticate OpenID
  463. WordPressOpenID_Logic::set_error('Unable to authenticate OpenID.');
  464. wp_safe_redirect(get_option('siteurl') . '/wp-login.php');
  465. exit;
  466. }
  467. WordPressOpenID_Logic::set_current_user($identity_url);
  468. if (!is_user_logged_in()) {
  469. if ( get_option('users_can_register') ) {
  470. $user_data =& WordPressOpenID_Logic::get_user_data($identity_url);
  471. $user = WordPressOpenID_Logic::create_new_user($identity_url, $user_data);
  472. WordPressOpenID_Logic::set_current_user($identity_url); // TODO this does an extra db hit to get user_id
  473. } else {
  474. // TODO - Start a registration loop in WPMU.
  475. WordPressOpenID_Logic::set_error('OpenID authentication valid, but unable '
  476. . 'to find a WordPress account associated with this OpenID.<br /><br />'
  477. . 'Enable "Anyone can register" to allow creation of new accounts via OpenID.');
  478. wp_safe_redirect(get_option('siteurl') . '/wp-login.php');
  479. exit;
  480. }
  481. }
  482. if (empty($redirect_to)) {
  483. $redirect_to = 'wp-admin/';
  484. }
  485. if ($redirect_to == 'wp-admin/') {
  486. if (!current_user_can('edit_posts')) {
  487. $redirect_to .= 'profile.php';
  488. }
  489. }
  490. if (!preg_match('#^(http|\/)#', $redirect_to)) {
  491. $wpp = parse_url(get_option('siteurl'));
  492. $redirect_to = $wpp['path'] . '/' . $redirect_to;
  493. }
  494. if (function_exists('wp_safe_redirect')) {
  495. wp_safe_redirect( $redirect_to );
  496. } else {
  497. wp_redirect( $redirect_to );
  498. }
  499. exit;
  500. }
  501. /**
  502. * Action method for completing the 'comment' action. This action is used when leaving a comment.
  503. *
  504. * @param string $identity_url verified OpenID URL
  505. */
  506. function _finish_openid_comment($identity_url) {
  507. global $openid;
  508. if (empty($identity_url)) {
  509. // FIXME unable to authenticate OpenID - give option to post anonymously
  510. WordPressOpenID_Interface::display_error('unable to authenticate OpenID');
  511. }
  512. WordPressOpenID_Logic::set_current_user($identity_url);
  513. if (is_user_logged_in()) {
  514. // simulate an authenticated comment submission
  515. $_SESSION['oid_comment_post']['author'] = null;
  516. $_SESSION['oid_comment_post']['email'] = null;
  517. $_SESSION['oid_comment_post']['url'] = null;
  518. } else {
  519. // try to get user data from the verified OpenID
  520. $user_data =& WordPressOpenID_Logic::get_user_data($identity_url);
  521. if (!empty($user_data['display_name'])) {
  522. $_SESSION['oid_comment_post']['author'] = $user_data['display_name'];
  523. }
  524. if ($oid_user_data['user_email']) {
  525. $_SESSION['oid_comment_post']['email'] = $user_data['user_email'];
  526. }
  527. }
  528. // record that we're about to post an OpenID authenticated comment.
  529. // We can't actually record it in the database until after the repost below.
  530. $_SESSION['oid_posted_comment'] = true;
  531. $wpp = parse_url(get_option('siteurl'));
  532. WordPressOpenID_Interface::repost($wpp['path'] . '/wp-comments-post.php',
  533. array_filter($_SESSION['oid_comment_post']));
  534. }
  535. /**
  536. * Action method for completing the 'verify' action. This action is used adding an identity URL to a
  537. * WordPress user through the admin interface.
  538. *
  539. * @param string $identity_url verified OpenID URL
  540. */
  541. function _finish_openid_verify($identity_url) {
  542. global $openid;
  543. $user = wp_get_current_user();
  544. if (empty($identity_url)) {
  545. // FIXME unable to authenticate OpenID
  546. WordPressOpenID_Logic::set_error('Unable to authenticate OpenID.');
  547. } else {
  548. $store =& WordPressOpenID_Logic::getStore();
  549. if( !$store->insert_identity($user->ID, $identity_url) ) {
  550. // TODO should we check for this duplication *before* authenticating the ID?
  551. WordPressOpenID_Logic::set_error('OpenID assertion successful, but this URL is already claimed by '
  552. . 'another user on this blog. This is probably a bug. ' . $identity_url);
  553. } else {
  554. $openid->action = 'success';
  555. }
  556. }
  557. $wpp = parse_url(get_option('siteurl'));
  558. $redirect_to = $wpp['path'] . '/wp-admin/' . (current_user_can('edit_users') ? 'users.php' : 'profile.php') . '?page=openid';
  559. if (function_exists('wp_safe_redirect')) {
  560. wp_safe_redirect( $redirect_to );
  561. } else {
  562. wp_redirect( $redirect_to );
  563. }
  564. // TODO display success message
  565. exit;
  566. }
  567. /**
  568. * If last comment was authenticated by an OpenID, record that in the database.
  569. *
  570. * @param string $location redirect location
  571. * @param object $comment comment that was just left
  572. * @return string redirect location
  573. */
  574. function comment_post_redirect($location, $comment) {
  575. global $openid;
  576. if ($_SESSION['oid_posted_comment']) {
  577. WordPressOpenID_Logic::set_comment_openid($comment->comment_ID);
  578. $_SESSION['oid_posted_comment'] = null;
  579. }
  580. return $location;
  581. }
  582. /**
  583. * Create a new WordPress user with the specified identity URL and user data.
  584. *
  585. * @param string $identity_url OpenID to associate with the newly
  586. * created account
  587. * @param array $user_data array of user data
  588. */
  589. function create_new_user($identity_url, &$user_data) {
  590. global $wpdb, $openid;
  591. // Identity URL is new, so create a user
  592. @include_once( ABSPATH . 'wp-admin/upgrade-functions.php'); // 2.1
  593. @include_once( ABSPATH . WPINC . '/registration-functions.php'); // 2.0.4
  594. $user_data['user_login'] = $wpdb->escape( WordPressOpenID_Logic::generate_new_username($identity_url) );
  595. $user_data['user_pass'] = substr( md5( uniqid( microtime() ) ), 0, 7);
  596. $user_id = wp_insert_user( $user_data );
  597. $openid->log->debug("wp_create_user( $user_data ) returned $user_id ");
  598. if( $user_id ) { // created ok
  599. $user_data['ID'] = $user_id;
  600. // XXX this all looks redundant, see WordPressOpenID_Logic::set_current_user
  601. $openid->log->debug("OpenIDConsumer: Created new user $user_id : $username and metadata: "
  602. . var_export( $user_data, true ) );
  603. $user = new WP_User( $user_id );
  604. if( ! wp_login( $user->user_login, $user_data['user_pass'] ) ) {
  605. $openid->error = 'User was created fine, but wp_login() for the new user failed. '
  606. . 'This is probably a bug.';
  607. $openid->action= 'error';
  608. $openid->log->err( $openid->error );
  609. return;
  610. }
  611. // notify of user creation
  612. wp_new_user_notification( $user->user_login );
  613. wp_clearcookie();
  614. wp_setcookie( $user->user_login, md5($user->user_pass), true, '', '', true );
  615. // Bind the provided identity to the just-created user
  616. global $userdata;
  617. $userdata = get_userdata( $user_id );
  618. $store = WordPressOpenID_Logic::getStore();
  619. $store->insert_identity($user_id, $identity_url);
  620. $openid->action = 'redirect';
  621. if ( !$user->has_cap('edit_posts') ) $redirect_to = '/wp-admin/profile.php';
  622. } else {
  623. // failed to create user for some reason.
  624. $openid->error = 'OpenID authentication successful, but failed to create WordPress user. '
  625. . 'This is probably a bug.';
  626. $openid->action= 'error';
  627. $openid->log->error( $openid->error );
  628. }
  629. }
  630. /**
  631. * Get user data for the given identity URL. Data is returned as an associative array with the keys:
  632. * ID, user_url, user_nicename, display_name
  633. *
  634. * Multiple soures of data may be available and are attempted in the following order:
  635. * - OpenID Attribute Exchange !! not yet implemented
  636. * - OpenID Simple Registration
  637. * - hCard discovery !! not yet implemented
  638. * - default to identity URL
  639. *
  640. * @param string $identity_url OpenID to get user data about
  641. * @return array user data
  642. */
  643. function get_user_data($identity_url) {
  644. global $openid;
  645. $data = array(
  646. 'ID' => null,
  647. 'user_url' => $identity_url,
  648. 'user_nicename' => $identity_url,
  649. 'display_name' => $identity_url
  650. );
  651. // create proper website URL if OpenID is an i-name
  652. if (preg_match('/^[\=\@\+].+$/', $identity_url)) {
  653. $data['user_url'] = 'http://xri.net/' . $identity_url;
  654. }
  655. $result = WordPressOpenID_Logic::get_user_data_sreg($identity_url, $data);
  656. return $data;
  657. }
  658. /**
  659. * Retrieve user data from OpenID Attribute Exchange.
  660. *
  661. * @param string $identity_url OpenID to get user data about
  662. * @param reference $data reference to user data array
  663. * @see get_user_data
  664. */
  665. function get_user_data_ax($identity_url, &$data) {
  666. // TODO implement attribute exchange
  667. }
  668. /**
  669. * Retrieve user data from OpenID Simple Registration.
  670. *
  671. * @param string $identity_url OpenID to get user data about
  672. * @param reference $data reference to user data array
  673. * @see get_user_data
  674. */
  675. function get_user_data_sreg($identity_url, &$data) {
  676. global $openid;
  677. $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($openid->response);
  678. $sreg = $sreg_resp->contents();
  679. $openid->log->debug(var_export($sreg, true));
  680. if (!$sreg) return false;
  681. if (array_key_exists('email', $sreg) && $sreg['email']) {
  682. $data['user_email'] = $sreg['email'];
  683. }
  684. if (array_key_exists('nickname', $sreg) && $sreg['nickname']) {
  685. $data['nickname'] = $sreg['nickname'];
  686. $data['user_nicename'] = $sreg['nickname'];
  687. $data['display_name'] = $sreg['nickname'];
  688. }
  689. if (array_key_exists('fullname', $sreg) && $sreg['fullname']) {
  690. $namechunks = explode( ' ', $sreg['fullname'], 2 );
  691. if( isset($namechunks[0]) ) $data['first_name'] = $namechunks[0];
  692. if( isset($namechunks[1]) ) $data['last_name'] = $namechunks[1];
  693. $data['display_name'] = $sreg['fullname'];
  694. }
  695. return true;
  696. }
  697. /**
  698. * Retrieve user data from hCard discovery.
  699. *
  700. * @param string $identity_url OpenID to get user data about
  701. * @param reference $data reference to user data array
  702. * @see get_user_data
  703. */
  704. function get_user_data_hcard($identity_url, &$data) {
  705. // TODO implement hcard discovery
  706. }
  707. /**
  708. * For comments that were handled by WordPress normally (not our code), check if the author
  709. * registered with OpenID and set comment openid flag if so.
  710. *
  711. * @action post_comment
  712. */
  713. function check_author_openid($comment_ID) {
  714. global $openid;
  715. $comment = get_comment($comment_ID);
  716. if ( $comment->user_id && !$comment->openid && is_user_openid($comment->user_id) ) {
  717. WordPressOpenID_Logic::set_comment_openid($comment_ID);
  718. }
  719. }
  720. /**
  721. * Mark the provided comment as an OpenID comment
  722. *
  723. * @param int $comment_ID id of comment to set as OpenID
  724. */
  725. function set_comment_openid($comment_ID) {
  726. global $wpdb, $openid;
  727. $comments_table = WordPressOpenID::comments_table_name();
  728. $wpdb->query("UPDATE $comments_table SET openid='1' WHERE comment_ID='$comment_ID' LIMIT 1");
  729. }
  730. /**
  731. * If the comment contains a valid OpenID, skip the check for requiring a name and email address. Even if
  732. * this data is provided in the form, we may get it through other methods, so we don't want to bail out
  733. * prematurely. After OpenID authentication has completed (and $_SESSION['oid_skip'] is set), we don't
  734. * interfere so that this data can be required if desired.
  735. *
  736. * @param boolean $value existing value of flag, whether to require name and email
  737. * @return boolean new value of flag, whether to require name and email
  738. * @see get_user_data
  739. */
  740. function bypass_option_require_name_email( $value ) {
  741. global $openid_auth_request, $openid;
  742. if ($_REQUEST['oid_skip']) {
  743. return $value;
  744. }
  745. if (array_key_exists('openid_url', $_POST)) {
  746. if( !empty( $_POST['openid_url'] ) ) {
  747. return false;
  748. }
  749. } else {
  750. if (!empty($_POST['url'])) {
  751. if (WordPressOpenID_Logic::late_bind()) {
  752. // check if url is valid OpenID by forming an auth request
  753. set_error_handler( array('WordPressOpenID_Logic', 'customer_error_handler'));
  754. $consumer = WordPressOpenID_Logic::getConsumer();
  755. $openid_auth_request = $consumer->begin( $_POST['url'] );
  756. restore_error_handler();
  757. if (null !== $openid_auth_request) {
  758. return false;
  759. }
  760. }
  761. }
  762. }
  763. return $value;
  764. }
  765. /**
  766. * Intercept comment submission and check if it includes a valid OpenID. If it does, save the entire POST
  767. * array and begin the OpenID authentication process.
  768. *
  769. * regarding comment_type: http://trac.wordpress.org/ticket/2659
  770. *
  771. * @param object $comment comment object
  772. * @return object comment object
  773. */
  774. function comment_tagging( $comment ) {
  775. global $openid;
  776. if (!$openid->enabled) return $comment;
  777. if ($_REQUEST['oid_skip']) return $comment;
  778. $openid_url = (array_key_exists('openid_url', $_POST) ? $_POST['openid_url'] : $_POST['url']);
  779. if( !empty($openid_url) ) { // Comment form's OpenID url is filled in.
  780. $_SESSION['oid_comment_post'] = $_POST;
  781. $_SESSION['oid_comment_post']['comment_author_openid'] = $openid_url;
  782. $_SESSION['oid_comment_post']['oid_skip'] = 1;
  783. WordPressOpenID_Logic::start_login( $openid_url, 'comment');
  784. // Failure to redirect at all, the URL is malformed or unreachable.
  785. // Display an error message only if an explicit OpenID field was used. Otherwise,
  786. // just ignore the error... it just means the user entered a normal URL.
  787. if (array_key_exists('openid_url', $_POST)) {
  788. // TODO give option to post without OpenID
  789. global $error;
  790. $error = $openid->error;
  791. $_POST['openid_url'] = '';
  792. include( ABSPATH . 'wp-login.php' );
  793. exit();
  794. }
  795. }
  796. return $comment;
  797. }
  798. /**
  799. * This filter callback simply approves all OpenID comments, but later it could do more complicated logic
  800. * like whitelists.
  801. *
  802. * @param string $approved comment approval status
  803. * @return string new comment approval status
  804. */
  805. function comment_approval($approved) {
  806. if ($_SESSION['oid_posted_comment']) {
  807. return 1;
  808. }
  809. return $approved;
  810. }
  811. /**
  812. * Get any additional comments awaiting moderation by this user. WordPress
  813. * core has been udpated to grab most, but we still do one last check for
  814. * OpenID comments that have a URL match with the current user.
  815. *
  816. * @param array $comments array of comments to display
  817. * @param int $post_id id of the post to display comments for
  818. * @return array new array of comments to display
  819. */
  820. function comments_awaiting_moderation(&$comments, $post_id) {
  821. global $wpdb, $openid;
  822. $user = wp_get_current_user();
  823. $commenter = wp_get_current_commenter();
  824. extract($commenter);
  825. $author_db = $wpdb->escape($comment_author);
  826. $email_db = $wpdb->escape($comment_author_email);
  827. $url_db = $wpdb->escape($comment_author_url);
  828. if ($url_db) {
  829. $comments_table = WordPressOpenID::comments_table_name();
  830. $additional = $wpdb->get_results(
  831. "SELECT * FROM $comments_table"
  832. . " WHERE comment_post_ID = '$post_id'"
  833. . " AND openid = '1'" // get OpenID comments
  834. . " AND comment_author_url = '$url_db'" // where only the URL matches
  835. . ($user ? " AND user_id != '$user->ID'" : '')
  836. . ($author_db ? " AND comment_author != '$author_db'" : '')
  837. . ($email_db ? " AND comment_author_email != '$email_db'" : '')
  838. . " AND comment_approved = '0'"
  839. . " ORDER BY comment_date");
  840. if ($additional) {
  841. $comments = array_merge($comments, $additional);
  842. usort($comments, create_function('$a,$b',
  843. 'return strcmp($a->comment_date_gmt, $b->comment_date_gmt);'));
  844. }
  845. }
  846. return $comments;
  847. }
  848. /**
  849. * Make sure that a user's OpenID is stored and retrieved properly. This is important because the OpenID
  850. * may be an i-name, but WordPress is expecting the comment URL cookie to be a valid URL.
  851. *
  852. * @wordpress-action sanitize_comment_cookies
  853. */
  854. function sanitize_comment_cookies() {
  855. if ( isset($_COOKIE['comment_author_openid_'.COOKIEHASH]) ) {
  856. // this might be an i-name, so we don't want to run clean_url()
  857. remove_filter('pre_comment_author_url', 'clean_url');
  858. $comment_author_url = apply_filters('pre_comment_author_url',
  859. $_COOKIE['comment_author_openid_'.COOKIEHASH]);
  860. $comment_author_url = stripslashes($comment_author_url);
  861. $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
  862. }
  863. }
  864. /**
  865. * Parse the WordPress request. If the pagename is 'openid_consumer', then the request
  866. * is an OpenID response and should be handled accordingly.
  867. *
  868. * @param WP $wp WP instance for the current request
  869. */
  870. function parse_request($wp) {
  871. openid_init();
  872. if ($wp->query_vars['pagename'] == 'openid_consumer') {
  873. WordPressOpenID_Logic::finish_openid($_REQUEST['action']);
  874. }
  875. }
  876. function set_error($error) {
  877. $_SESSION['oid_error'] = $error;
  878. return;
  879. }
  880. } // end class definition
  881. endif; // end if-class-exists test
  882. ?>